Amazonレビューは、何かと話題になります。
今なら、Amazonレビューにはサクラが多くて胡散臭いという話題でしょうか?
Amazonレビューは、以前は本当に役に立ちました。
参考にして買い物をすることも多くありました。
しかし、今はレビューを参考にすることが、めっきりと減りました。
そもそも、Amazonでの買い物をする機会が減りました。
その代わりに、安全なイメージのヨドバシで買い物をしています。
レビュー以外にも、悪質な業者の問題がAmazonには存在していますからね。
そのイメージの悪いAmazonレビューに関して、この記事で取り上げます。
どう取り上げるかと言うと、センチメント分析(感情分析)の対象とします。
本記事の内容
- Amazonレビューをセンチメント分析(感情分析)する目的・意図
- Amazonレビューを取得(データ収集)する
- Amazonレビューをセンチメント分析(感情分析)する
- Amazonレビューのセンチメント分析結果
- センチメント分析(感情分析)は使えない!?
それでは、上記に沿って解説していきます。
Amazonレビューをセンチメント分析(感情分析)する目的・意図
Amazonレビューには、多くの情報が集まっています。
まず、その投稿の量が圧倒的です。
確かに、サクラも多いのでしょう。
でも、サクラ以外の真っ当なレビューも多く投稿されています。
その投稿数が、他サイトとは比較にはなりません。
ヨドバシでは、レビューはほとんど投稿されないようです。
その意味でも、Amazonのレビューには価値があります。
また、コメントと評価(星1~5)がセットになっているのもポイントです。
このようにセットになっている点が、データ分析の際には役に立ちます。
以上より、Amazonレビューにはデータ量と内容(コメントと評価)が備わっていると言えます。
そして、何よりもネガポジ分析を実際のデータでやりたかったというのが大きいです。
自分にも身近なデータで分析する方が、モチベーションも沸きますからね。
Amazonレビューを取得(データ収集)する
データ分析の最初の関門となります。
どうやってデータを大量に集めるのか?ということですね。
これに関しては、全く問題ありません。
本ブログでは、多くのサイトをスクレイピングした結果を記事にしています。
その中でも、次の記事は今回の作業にビンゴです。
上の記事の内容を若干修正して、データを集めました。
改良したコードは以下。
import bs4 import time import re from selenium import webdriver from selenium.webdriver.chrome.options import Options import pandas as pd CHROME_DRIVER = "chromedriver.exeのパス" FILE_PATH = "./amazon_data/review.json" # Amazonページ取得 def get_page_from_amazon(url): text = "" # ヘッドレスモードでブラウザを起動 options = Options() options.add_argument( '--headless' ) # ブラウザーを起動 driver = webdriver.Chrome(CHROME_DRIVER, options = options) driver.get(url) driver.implicitly_wait( 10 ) # 見つからないときは、10秒まで待つ text = driver.page_source # ブラウザ停止 driver.quit() return text # 全ページ分をリストにする def get_all_reviews(url): rvw_list = [] i = 1 while True : print (i, 'searching' ) i + = 1 text = get_page_from_amazon(url) amazon_soup = bs4.BeautifulSoup(text, features = 'lxml' ) for ratings in amazon_soup.find_all( "div" , attrs = { "data-hook" : "review" }): submission_date = ratings.find( "span" , { 'data-hook' : 'review-date' }).text rating = ratings.find( 'i' , attrs = { "data-hook" : "review-star-rating" }).text review_txt = ratings.find( "span" , { 'data-hook' : 'review-body' }).text #paid = ratings.find("span", attrs={"class": "a-color-success a-text-bold"}) print (submission_date + "@" + rating + "@" + review_txt) print ( '\n' ) if submission_date: rating = rating.replace( "5つ星のうち" , "") submission_date = extract_date(submission_date) info = {} info[ "postdate" ] = submission_date info[ "rating" ] = rating info[ "review_txt" ] = review_txt rvw_list.append(info) # 次へボタン next_page = amazon_soup.select( 'li.a-last a' ) if next_page ! = []: next_url = 'https://www.amazon.co.jp/' + next_page[ 0 ].attrs[ 'href' ] url = next_url # 最低でも1秒は間隔をあける time.sleep( 1 ) else : break return rvw_list def extract_date(s): date_pattern = re. compile ( '(\d{4})年(\d{1,2})月(\d{1,2})日' ) result = date_pattern.search(s) if result: y, m, d = result.groups() return str (y) + str (m.zfill( 2 )) + str (d.zfill( 2 )) else : return None if __name__ = = '__main__' : # Amzon商品ページ url = 'https://www.amazon.co.jp/dp/B081CR9LQ1?pf_rd_r=9G7DP9DBQV9R5WWBA7GB&pf_rd_p=cf3dd3d4-7c29-4fa2-a9b7-8e9f95c1f37f&pd_rd_r=56934866-4323-49ea-ae8e-289260468100&pd_rd_w=bIWmn&pd_rd_wg=gA4B0&ref_=pd_gw_ci_mcx_mr_hp_d' # URLをレビューページのものに書き換える new_url = url.replace( 'dp' , 'product-reviews' ) # レビューの取得 rvw_list = get_all_reviews(new_url) # データフレームに変換 df = pd.DataFrame(rvw_list) # jsonとして保存 df.to_json(FILE_PATH, orient = 'records' ) |
内容に不明な点があれば、過去記事を参考にしてください。
詳細を説明しています。
このサンプルコードを動かすと、該当するAmazonの商品ページのレビューを収集します。
その結果は、amazon_dataフォルダ内のreview.jsonに保存されます。
- 投稿日
- 評価
- コメント
項目的には、上記の3つとなります。
Amazonレビューをセンチメント分析(感情分析)する
センチメント分析(感情分析)は、難しそう。。。
このように思うかもしれません。
ただ、分析するだけなら簡単にできます。
やり方は以下。
- 単語感情極性対応表(ネガポジ辞書)を取得する
- 分析対象の文章を単語に分割する(形態素解析)
- ネガポジ辞書と単語を比較する(否定・肯定の判定)
では、上記を説明していきましょう。
単語感情極性対応表(ネガポジ辞書)を取得する
以下のページにアクセスします。
「日本語」のリンク先が辞書となります。
http://www.lr.pi.titech.ac.jp/~takamura/pndic_ja.html
優れる:すぐれる:動詞:1 良い:よい:形容詞:0.999995 喜ぶ:よろこぶ:動詞:0.999979 褒める:ほめる:動詞:0.999979 めでたい:めでたい:形容詞:0.999645 ~ 厄難:やくなん:名詞:-0.765276 紡ぐ:つむぐ:動詞:-0.765358 自製:じせい:名詞:-0.765388 死亡:しぼう:名詞:-0.765674 主義:しゅぎ:名詞:-0.765714 |
上記がその内容です。
単語毎に値が設定されています。
その値の範囲は、-1から1までとなります。
1に近いほど、肯定的なワードです。
-1に近いほど、否定的なワードです。
分析対象の文章を単語に分割する(形態素解析)
文章を形態素解析します。
形態素解析に関しては、次の記事で詳しく説明しています。
プログラム的には、複雑なことはしません。
専用のソフトを用いて、簡単に行うことができます。
今回は、MecabをPythonから用いて形態素解析を行っています。
ネガポジ辞書と単語を比較する(否定・肯定の判定)
ここまで説明してきたら、わかると思います。
形態素解析によって、文章毎(レビューのコメント)に単語が求められます。
そして、その単語が単語感情極性対応表(ネガポジ辞書)にあるかどうかを調べます。
あった場合は、何点(-1~1の間の数値)なのかを求めます。
複数の単語がマッチする場合は、その平均を用います。
Amazonレビューのセンチメント分析結果
約9000件のレビューを分析しました。
上記のデータ収集プログラムで集めたデータです。
以下の3つの結果を確認しましょう
- ヒストグラム
- 散布図
- 結果一覧
では、それぞれを確認します。
ヒストグラム

基本的には、ネガティブの結果が多いです。
9割以上が、0以下という結果ですね。
ヒストグラムに関しては、次の記事が参考になります。
簡単にヒストグラムの図を作成できます。
レビューのコメントには、ネガティブな結果が多いことはわかりました。
では、その結果と評価(星1~5)との関係が気になります。
その二つの値の関係性を見るには、散布図が約に立ちます。
以下で、それを確認しましょう。
散布図

横(x軸)が、ネガポジ(PN)結果の値です。
縦(y軸)が、Amazonレビューにおける評価です。
この結果を見ると、次のことが言えます。
- 低評価(1と2)にはネガティブなコメントしかない
- 高評価にもネガティブなコメントが多い
- 基本的にはネガティブなコメントが多い
結果一覧
結果を直接確認します。
まずは、ポジティブなコメントから。
つまり、値が1に近いモノということです。

pn:ネガポジ分析の値
rating:Amazonレビューにおける評価(星)
review_text:Amazonレビューにおけるコメント
確かに、肯定的なコメントが並びます。
あと、コメントが短いというのが目立ちますね。
次は、ネガティブなコメントを確認しましょう。
pnが-1に近いモノということです。

どうもオカシイ。。。
まあ、これが単語感情極性対応表(ネガポジ辞書)を単純に利用した場合の限界ですね。
「言うことなし」の「なし」が、ネガティブ判定されているのです。
「部屋の汚れが少なくなって来ました」では、「汚れ」や「少なく」がネガティブとされているのでしょう。
文脈に沿った分析を行えていないという証拠になります。
単純に、単語で判定するとこのような結果になってしまうのでしょう。
センチメント分析(感情分析)は使えない!?
センチメント分析(感情分析)を行ってきました。
正直、微妙ですね。
散布図を見る限りでは、全く見当違いというわけでもないようです。
そのため、「センチメント分析(感情分析)は使えない」と断定するのは時期早々でしょう。
これは、あくまでセンチメント分析(感情分析)のスタートです。
ここからネガポジ判定の精度をどうやって上げていくのか?
単語感情極性対応表(ネガポジ辞書)にもっと情報を増やすのか?
それとも、文脈を考慮した分析方法があるのか?
センチメント分析(感情分析)における課題ですね。
それでは、最後にセンチメント分析(感情分析)を行ったサンプルコードを載せておきます。
import pandas as pd import json import MeCab import re import matplotlib.pyplot as plt import numpy as np FILE_PATH = "./amazon_data/review.json" DIC_PATH = "-d ipadic-neologdのパス" # 辞書ipadic-neologdがあれば、そのパスを記述 def get_dic_list(text): mecab = MeCab.Tagger(DIC_PATH) parsed = mecab.parse(text) lines = parsed.split( '\n' ) lines = lines[ 0 : - 2 ] dic_list = [] for line in lines: tmp = re.split( '\t|,' , line) dic = { 'dsp' :tmp[ 0 ], 'kind_1' :tmp[ 1 ], 'kind_2' :tmp[ 2 ], 'value' :tmp[ 7 ]} dic_list.append(dic) return dic_list # PN判定 def judge_pn(pn_dict, dic_list): all_pn = 0 count = 0 result = "" for word in dic_list: base = word[ 'value' ] if base in pn_dict: count = count + 1 pn = float (pn_dict[base]) all_pn = all_pn + pn if all_pn ! = 0 : result = all_pn / count return result # Amazonレビューの読み込み json_open = open (FILE_PATH, 'r' ) review_list = json.load(json_open) # ネガポジ辞書の読み込み pn_df = pd.read_csv( 'dic/pn_ja.dic.txt' ,\ sep = ':' , encoding = 'utf-8' , names = ( 'Word' , 'Reading' , 'POS' , 'PN' ) ) # ネガポジ辞書をデータフレームからdict型に変換 word_list = list (pn_df[ 'Word' ]) pn_list = list (pn_df[ 'PN' ]) pn_dict = dict ( zip (word_list, pn_list)) new_review_list = [] for review in review_list: review_txt = review[ "review_txt" ] rating = review[ "rating" ] postdate = review[ "postdate" ] dic_list = get_dic_list(review_txt) pn = judge_pn(pn_dict, dic_list) review[ "pn" ] = pn if pn: new_review_list.append(review) new_review_list_df = pd.DataFrame(new_review_list) # 欠損値が一つでも含まれる行を削除 new_review_list_df = new_review_list_df.dropna(how = 'any' ) x = new_review_list_df[ "pn" ].astype( float ) y = new_review_list_df[ "rating" ].astype( float ) fig = plt.figure(dpi = 100 , figsize = ( 4.6 , 3.8 )) #fig.suptitle('評価:PN', fontsize=10, x=0.27, y=0.87) ax = fig.add_subplot( 111 ) ax.yaxis.set_label_coords( - 0.07 , 0.4 ) # 目盛の値 xscale = np.arange( - 1.0 , 1.1 , 0.2 ) yscale = np.arange( 0 , 6 , 1.0 ) # 散布図を描画 plt.scatter(x, y, c = '#00FFFF' , edgecolors = "#37BAF2" ) plt.title( "評価・PN散布図" , fontsize = 17 ) plt.xticks(xscale, fontsize = 8 ) plt.yticks(yscale, fontsize = 8 ) plt.xlabel( "PN" , fontsize = 10 ) plt.ylabel( "評\n価" , fontsize = 10 , rotation = 0 ) plt.xlim( - 1.1 , 1.1 ) plt.ylim( 0.5 , 5.5 ) plt.subplots_adjust(left = 0.1 , right = 0.98 , bottom = 0.13 , top = 0.89 ) plt.savefig( "result_1.png" , facecolor = "white" ) """ # グラフを表示 plt.hist(x) # グラフを画像保存 plt.savefig("result_2.png", facecolor="white") """ |