18
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【スクレイピング】 他社の商品データを収集

Last updated at Posted at 2019-05-05

974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3132303839382f30313366663136332d333730642d373335382d376363612d3839346637393636613138302e706e675のコピー35436.png

#概要

Pythonでのスクレイピングにより、競合など他社サイトに掲載されている商品データを収集し、

マーケティング・競合分析への活用のため、CSVファイルにまとめ、出力します

#対象ページ

スクレイピング対象は、以下のようなページです

  • インテリアのECサイト
  • チェア:カテゴリの商品一覧ページ

demostore2.png

##対象データ

今回は、先述のページから

  • 商品名
  • 価格
  • レビュー|評価値
  • レビュー|投稿数

以上 4つの情報を、チェア:カテゴリの全商品分、取得します

#注意

  • 記事内の「Webサイト/URL/データ」は全てダミーです
  • 記事内のコードで、実際にスクレイピングする事は可能ですが、実在のサイトに対するスクレイピングは、サーバー負担への配慮、著作権/利用規約の十分な確認等が必要となります

#対象分析

対象から利用制限が指定されていないか、対象ページの構造はどうなっているか、等を分析/確認していきます

##利用制限

人様のサイトからデータを取得するにあたり、まずはそのサイトの「著作権/利用規約ページ」をしっかり確認した上で、以下のコードでアクセスに対する制限を確認

. . . . .

クローリングへの指示書である「robots.txt」解析のためのモジュールurllib.robotparserをインポート

import urllib.robotparser
rp = urllib.robotparser.RobotFileParser()

robots.txtの読み込み

rp.set_url("https://demo-store.com/robots.txt")
rp.read()

request_rateで、クローラーに対し遅延時間が指定されていないかの確認

req_rate = rp.request_rate("*")
print(req_rate)
output
None

遅延時間の指定は無し。とはいえ、相手サーバー負荷への配慮として、ロード遅延処理は必須

. . . . .

can_fetchでURLの取得が許可されているかを確認

rp.can_fetch("*", "https://demo-store.com/")
output
True

Trueが返され、URL取得の許可が確認できました

##ページ構造

Chrome|デベロッパーツールを用い、対象ページの構造を確認

arg8g7rbd6r87.png

ges8b9fg7ndf687.png

1商品ごとにpd_boxというクラス名のパーツで囲われ、
その中に今回取得したい4つの商品情報が入っており、それぞれ以下のようにクラス名が付けられています

情報 クラス名
商品名 pd_name
価格 pd_price
レビュー:投稿数 rev_count
レビュー:評価値 rev_rate

##URLの規則性

対象ページのURLは以下(※ ダミーURLです)
https://demo-store.com/living/chair/?page=1

また、この「チェア:カテゴリ|商品一覧」は、全10ページで

3efg57rs687bd6n9ht8d7.png

10ページ目のURLはhttps://demo-store.com/living/chair/?page=10となっており、
?page=XXの箇所が、現在のページ数を表しているという事が分かりました

以上を踏まえ、スクレイピング実行のコードを考えていきます

#データ取得

実際にブラウザを起動・サイトを描画しながらスクレイピングする手法もありますが、今回はヘッダレスブラウザを用い、画面描写無しでサッサとスクレイピングしていきます

. . . . .

Selenium|Chromeブラウザをインポート

from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.common.keys import Keys

--headlessでオプション:ヘッドレスモードを有効化

options = ChromeOptions()
options.add_argument('--headless')

ChromeのWebDriverオブジェクトを作成

browser = Chrome(options=options)

後に商品情報を格納していく、空のリストを作成

names = []
prices = []
rev_rates = []
rev_counts = []

今回の対象は「商品一覧:10ページ分」つまり

https://demo-store.com/living/chair/?page=1から
https://demo-store.com/living/chair/?page=10までが対象です

よって?page={}として、ページ数箇所を変数とし、format(i)で変数箇所にiを埋め込みます

iにはfor文で1から10までの数字を順に当てはめていきます

for i in range(1,11):
    url = 'https://demo-store.com/living/chair/?page={}'.format(i)

ややこしいですがrange(1,11)で、1から10までの数字が入ることになります

. . . . .

相手サーバーへの負荷配慮としてimplicitly_waitでロードの際に3秒の間隔が空くように設定

    browser.implicitly_wait(3)

?page={}箇所に連番が入ったURL」を取得

    browser.get(url)

一覧ページでは、商品ごとにpd_boxで囲われていました
その中に、それぞれの商品名(pd_name)や価格(pd_price)が含まれている

ges8b9fg7ndf687.png

よってpd_boxの中身を探し終えたら、次のpd_boxの中身を探し始める、という内容のfor文とします

find_elements_by_class_nameで指定したクラス名の要素を取得し、リストとしてpd_boxesに格納

    pd_boxes = browser.find_elements_by_class_name('pd_box')

pd_box1つあたりに対し、「pd_name/pd_price/rev_rate/rev_count」を探し、取得し、それぞれリストとして格納していく。完了したら次のpd_boxに対し同様の処理を行っていく

    for pd_box in pd_boxes:

        name = pd_box.find_element_by_class_name('pd_name').text
        names.append(name)

        price = pd_box.find_element_by_class_name('pd_price').text
        price = price.replace("¥","").replace(",","")
        prices.append(price)

        rev_rate = pd_box.find_element_by_class_name('rev_rate').text
        rev_rates.append(rev_rate)

        rev_count = pd_box.find_element_by_class_name('rev_count').text
        rev_count = rev_count.replace("(","").replace("件)","")
        rev_counts.append(rev_count)

名前や価格を取得しつつ、「¥」や「件」などの不要文字列はreplaceにより削除しています

. . . . .

取得したデータを確認

names
output
['Ultra Chair','Super Chair','Hyper Chair'...]
prices
output
['10000','20000','30000'...]
rev_rates
output
['5','5','5'...]
rev_counts
output
['26','36','46'...]

ちゃんとデータ取得できています

#CSV出力

取得したデータは現時点では個々のリストになっているので、データフレームとしてまとめます

import pandas as pd
df = pd.DataFrame()

リスト毎に列を作成

df['name'] = names
df['price'] = prices
df['rev_rate'] = rev_rates
df['rev_count'] = rev_counts

内容確認のため、データフレームの上から5行目までを表示

df.head()

|| name | price | rev_rate | rev_count |
|:--|:--|:--|--:|--:|--:|
1| Ultra Chair | 10000 | 5 | 26 |
2| Super Chair | 20000 | 5 | 36 |
3| Hyper Chair | 30000 | 5 | 46 |
4| Miracle Chair | 40000 | 5 | 56 |
5| Passion Chair | 50000 | 5 | 66 |

問題無くデータフレームになっているので、CSVファイルに変換します

df.to_csv('living_chair.csv', index=False)

gre68vf7sf.png

CSVファイルが出力されました

#総括

他社サイトの商品情報をスクレイピングで収集し、CSVファイルにまとめました

  • 各商品のレビュー投稿数を、販売数とある程度の相関があると仮定し、販売数予測の目安とする
  • 価格帯ごとのレビュー評価(満足度)を分析する

など、競合分析において活用のできる、データファイルが取得できたかと考えます

18
18
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?