今回は、Pythonを使ってAmazonの口コミをスクレイプする方法について紹介したいと思います。
以前こちらのページで、WindowsFormとC#を使ってAmazonの口コミをスクレイプする方法を紹介しました。
そして、こちらのページでは、Pythonを使ったスクレイプ用の自作クラスを紹介しました。
今回はこの2つの記事を合体させたような内容となります。
このページだけ読んで頂いても事が足りる用にしてありますが、スクレイプについて詳しく知りたい方は、併せてこちらの記事もご一読ください。
概要
今回は関数として作成しており、引数にamazon の商品URLを渡すと、口コミ収集してくれるようになっています。
関数内部では、こちらの記事で紹介した自作クラス(Scrape)を呼び出しています。

私が今回解析したURLやHTMLのパターン以外のものや、amazon のページの仕様が変わった場合は、うまくスクレイプできなくなりますがご了承ください。
関数のソースは次の様になります。
出来るだけ多くのコメントを記載していますので、ソースに目を通していただければ、おおよそのことが分かるかと思います。
def scrape_amazon(url):
scr = Scrape(wait=2,max=5)
#urlの /dp/ 直後に商品IDがあるので、それを取得
pos = url.find('/dp/') + 4
#商品IDは10桁なので、10桁分切り取る
id = url[pos:pos + 10]
#最大500ページ分(500×10=5000レビュー分)を読み出すループ
for n in range(1,500):
#商品IDからレビュー記事のページを生成
target = f'https://www.amazon.co.jp/product-reviews/{id}/ref=cm_cr_arp_d_viewopt_sr?ie=UTF8&filterByStar=all_stars&reviewerType=all_reviews&pageNumber={n}#reviews-filter-bar'
print(f'get:{target}')
#ページを読み込む
soup = scr.request(target)
#ページ内のレビューを全て取得(1ページ10レビュー)
reviews = soup.find_all('div',class_='a-section review aok-relative')
print(f'レビュー数:{len(reviews)}')
#レビューの数だけループ
for review in reviews:
#日本のレビューアが書いたレビューのタイトルを取得
title = scr.get_text(review.find('a',class_='a-size-base a-link-normal review-title a-color-base review-title-content a-text-bold'))
#外国人のレビューアが書いたレビューのタイトルを取得(日本人と外国人ではタグが異なっていた)
title = scr.get_text(review.find('span',class_='cr-original-review-content')) if title.strip() == '' else title
#レビューアの名前を取得
name = scr.get_text(review.find('span',class_='a-profile-name'))
#評価は「5つ星のうち4.3」という記述のされ方なので、「ち」以降の数値のみを取得
star = scr.get_text(review.find('span',class_='a-icon-alt'))
star = star[star.find('ち')+1:]
#日付けは「2022年6月16日に日本でレビュー済み」という記述のされ方なので、「に」までの文字列を取得
date = scr.get_text(review.find('span',class_='a-size-base a-color-secondary review-date'))
date = date[:date.find('に')]
#レビューの内容を取得
comment = scr.get_text(review.find('span',class_='a-size-base review-text review-text-content'))
#CSV出力用のDFに登録
scr.add_df([title,name,star,date,comment],['title','name','star','date','comment'],['\n'])
#ページ内のレビュー数が10未満なら最後と判断してループを抜ける
if len(reviews) < 10:
break
#CSVファイルに口コミを出力
scr.to_csv("p:/amazon口コミ.csv")
関数の使い方
使い方は簡単で、引数にスクレイプしたい商品のURLを渡すだけです。
scrape_amazon(スクレイプしたいURL)
scrape_amazon('https://www.amazon.co.jp/%E3%82%B7%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%91%E3%83%AF%E3%83%BC-USB%E3%83%A1%E3%83%A2%E3%83%AA-USB3-0-%E3%83%8D%E3%82%A4%E3%83%93%E3%83%BC%E3%83%96%E3%83%AB%E3%83%BC-SP064GBUF3B05V1D/dp/B00GOJ4R0U/ref=cm_cr_arp_d_product_top?ie=UTF8')
アマゾンのページのスクリーンショットをサンプルで掲載したいところですが、違反になるので割愛します。
下記商品のスクレイピングなので、実際にページに移動してご確認下さい。
下記は、スクレイピングした結果のCSVをEXCELで開いた画面です。
name は個人情報に抵触するかどうかわかりませんが、念のためボカしています。

全ソース(自作クラスを含む)
自作クラスを含む全てのソースを掲載しておきます。
# pip install requests
# pip install beautifulsoup4
import requests
from bs4 import BeautifulSoup
import time
import random
import pandas as pd
import time
import datetime
#------------------- 自作のScrapクラス ----------------
class Scrape():
def __init__(self,wait=1,max=None):
self.response = None
self.df = pd.DataFrame()
self.wait = wait
self.max = max
self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"}
self.timeout = 5
def request(self,url,wait=None,max=None,console=True):
'''
指定したURLからページを取得する。
取得後にwaitで指定された秒数だけ待機する。
max が指定された場合、waitが最小値、maxが最大値の間でランダムに待機する。
Params
---------------------
url:str
URL
wait:int
ウェイト秒
max:int
ウェイト秒の最大値
console:bool
状況をコンソール出力するか
Returns
---------------------
soup:BeautifulSoupの戻り値
'''
self.wait = self.wait if wait is None else wait
self.max = self.max if max is None else max
start = time.time()
response = requests.get(url,headers=self.headers,timeout = self.timeout)
time.sleep(random.randint(self.wait,self.wait if self.max is None else self.max))
if console:
tm = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
lap = time.time() - start
print(f'{tm} : {url} 経過時間 : {lap:.3f} 秒')
return BeautifulSoup(response.content, "html.parser")
def get_href(self,soup,contains = None):
'''
soupの中からアンカータグを検索し、空でないurlをリストで返す
containsが指定された場合、更にその文字列が含まれるurlだけを返す
Params
---------------------
soup:str
BeautifulSoupの戻り値
contains:str
抽出条件となる文字列
Returns
---------------------
return :[str]
条件を満たすurlのリスト
'''
urls = list(set([url.get('href') for url in soup.find_all('a')]))
if contains is not None:
return [url for url in urls if self.contains(url,contains)]
return [url for url in urls if urls is not None or urls.strip() != '']
def get_src(self,soup,contains = None):
'''
soupの中からimgタグを検索し、空でないsrcをリストで返す
containsが指定された場合、更にその文字列が含まれるurlだけを返す
Params
---------------------
soup:str
BeautifulSoupの戻り値
contains:str
抽出条件となる文字列
Returns
---------------------
return :[str]
条件を満たすurlのリスト
'''
urls = list(set([url.get('src') for url in soup.find_all('img')]))
if contains is not None:
return [url for url in urls if contains(url,self.contains)]
return [url for url in urls if urls is not None or urls.strip() != '']
def contains(self,line,kwd):
'''
line に kwd が含まれているかチェックする。
line が None か '' の場合、或いは kwd が None 又は '' の場合は Trueを返す。
Params
---------------------
line:str
HTMLの文字列
contains:str
抽出条件となる文字列
Returns
---------------------
return :[str]
条件を満たすurlのリスト
'''
if line is None or line.strip() == '':
return False
if kwd is None or kwd == '':
return True
return kwd in line
def omit_char(self,values,omits):
'''
リストで指定した文字、又は文字列を削除する
Params
---------------------
values:str
対象文字列
omits:str
削除したい文字、又は文字列
Returns
---------------------
return :str
不要な文字を削除した文字列
'''
for n in range(len(values)):
for omit in omits:
values[n] = values[n].replace(omit,'')
return values
def add_df(self,values,columns,omits = None):
'''
指定した値を DataFrame に行として追加する
omits に削除したい文字列をリストで指定可能
Params
---------------------
values:[str]
列名
omits:[str]
削除したい文字、又は文字列
'''
if omits is not None:
values = self.omit_char(values,omits)
columns = self.omit_char(columns,omits)
df = pd.DataFrame(values,index=self.rename_column(columns))
self.df = pd.concat([self.df,df.T])
def to_csv(self,filename,dropcolumns=None):
'''
DataFrame をCSVとして出力する
dropcolumns に削除したい列をリストで指定可能
Params
---------------------
filename:str
ファイル名
dropcolumns:[str]
削除したい列名
'''
if dropcolumns is not None:
self.df.drop(dropcolumns,axis=1,inplace=True)
self.df.to_csv(filename,index=False,encoding="shift-jis",errors="ignore")
def get_text(self,soup):
'''
渡された soup が Noneでなければ textプロパティの値を返す
Params
---------------------
soup: bs4.element.Tag
bs4でfindした結果の戻り値
Returns
---------------------
return :str
textプロパティに格納されている文字列
'''
return ' ' if soup == None else soup.text
def rename_column(self,columns):
'''
重複するカラム名の末尾に連番を付与し、ユニークなカラム名にする
例 ['A','B','B',B'] → ['A','B','B_1','B_2']
Params
---------------------
columns: [str]
カラム名のリスト
Returns
---------------------
return :str
重複するカラム名の末尾に連番が付与されたリスト
'''
lst = list(set(columns))
for column in columns:
dupl = columns.count(column)
if dupl > 1:
cnt = 0
for n in range(0,len(columns)):
if columns[n] == column:
if cnt > 0:
columns[n] = f'{column}_{cnt}'
cnt += 1
return columns
def write_log(self,filename,message):
'''
指定されたファイル名にmessageを追記する。
Params
---------------------
filename: str
ファイル名
message: str
ファイルに追記する文字列
'''
message += '\n'
with open(filename, 'a', encoding='shift-jis') as f:
f.write(message)
print(message)
def read_log(self,filename):
'''
指定されたファイル名を読み込んでリストで返す
Params
---------------------
filename: str
ファイル名
Returns
---------------------
return :[str]
読み込んだ結果
'''
with open(filename, 'r', encoding='shift-jis') as f:
lines = f.read()
return lines
#------------------- アマゾン口コミのスクレイピング用関数 ----------------
def scrape_amazon(url):
scr = Scrape(wait=2,max=5)
pos = url.find('/dp/') + 4
id = url[pos:pos + 10]
for n in range(1,100):
target = f'https://www.amazon.co.jp/product-reviews/{id}/ref=cm_cr_arp_d_viewopt_sr?ie=UTF8&filterByStar=all_stars&reviewerType=all_reviews&pageNumber={n}#reviews-filter-bar'
print(f'get:{target}')
soup = scr.request(target)
reviews = soup.find_all('div',class_='a-section review aok-relative')
print(f'レビュー数:{len(reviews)}')
for review in reviews:
title = scr.get_text(review.find('a',class_='a-size-base a-link-normal review-title a-color-base review-title-content a-text-bold'))
title = scr.get_text(review.find('span',class_='cr-original-review-content')) if title.strip() == '' else title
name = scr.get_text(review.find('span',class_='a-profile-name'))
star = scr.get_text(review.find('span',class_='a-icon-alt'))
star = star[star.find('ち')+1:]
date = scr.get_text(review.find('span',class_='a-size-base a-color-secondary review-date'))
date = date[:date.find('に')]
comment = scr.get_text(review.find('span',class_='a-size-base review-text review-text-content'))
scr.add_df([title,name,star,date,comment],['title','name','star','date','comment'],['\n'])
if len(reviews) < 10:
break
scr.to_csv("p:/amazon口コミ.csv")
まとめ
今回は、Amazonの口コミをPythonでスクレイピングする方法について紹介しました。
関数化しているので、コピペしてお使いいただけます。
但し、全ての商品について確認できておりませんので、商品の中にはうまくスクレイピングできない可能性もあります。
また、将来Amazonのページの仕様が変わった場合もスクレイピングできなくなりますので、その場合はソースのコメントを見ながら、適宜修正して頂ければと思います。
今回のスクレイピングで得た口コミに対して、ワードクラウドや文書要約など適応すれば、全ての口コミに目を通すことなく、全体の概要が掴めるかもしれませんので、興味のある方はお試しください。
今回の記事が皆様のプログラミングの一助になれば幸いです。