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") """