Filmarksからのデータ抽出を目的としたウェブスクレイピングの勉強 #5

閑話休題として，特定の映画のレビューを全件取得し，頻出の単語の集計を行います．
ほとんど，コピペと今までのコードの流用です．
本稿は筆者の趣味の塊ですので，ご容赦ください．


#レビュー取得のコード

特定の映画のレビューを1ページ分取得するコードを紹介します．申し訳ありませんが，引用元以上のコードをかける自信がなかったので9割9分コピペです．

```python
def fetch_movie_reviews_per_page(beautiful_soup_object):
    """
    Fetch movie reviews per page. Max 10 reviews.
    :param beautiful_soup_object:
    :return list of dict:
    """
    output = []
    # 辞書型で格納
    review = dict(review_id="",
                  review_text="",
                  posted_date="",
                  rating_score=0)

    soup = beautiful_soup_object

    # id_urlsのリストを取得(ここを変更しました)
    review_idurls = soup.select(".c-media__content > .c-media__text > a")
    # レビューのリストを取得
    review_texts = soup.select(".p-mark__review")
    # 投稿日時のリストを取得
    posted_dates = soup.select(".c-media__content > time")
    # 評価(星)のリストを取得
    rating_scores = soup.select(".c-media__content > div > .c-rating__score")
    # レビューがあれば
    if review_texts:
        # id_urls,レビュー,投稿日時,評価を各々のリストから1ずつ処理
        for review_idurl,review_text, posted_date, rating_score in zip(review_idurls,review_texts,posted_dates, rating_scores):
            # id_urlsを取得(ここを変更しました)
            review_id = review_idurl.attrs['href'].split('/')[-1]
            # レビューを取得
            review_text = review_text.text.replace("\u3000", "")
            # 投稿日時を取得
            posted_date = posted_date.get("datetime")
            # 評価無しでは0
            if rating_score.text == "-":
                review['rating_score'] = 0
            # 文字列から数値へ
            else:
                rating_score = float(rating_score.text)
                review['rating_score'] = rating_score
            review['review_id']   = review_id
            review['review_text'] = review_text
            review['posted_date'] = posted_date
            output.append(review.copy()) # output.append(review)ではすべての要素が同じになるダメ
    return output
```

<figure class="wp-block-image"><img src="https://program-board.com/wp-content/uploads/2019/10/スクリーンショット-2019-10-16-23.57.57-1024x223.png" alt="" class="wp-image-665"/><figcaption><strong><span class="fz-18px">出力結果</span></strong></figcaption></figure>


#頻出単語の集計

今回はMecabで形態素解析を行い，一般名詞に限り頻出単語を集計してみようと思います．<br>形態素解析は文章を名詞，形容詞等の品詞ごとに分割することで，Mecabはpythonと連携できる形態素解析のオープンソースです．他にjanome，jumanなどがありますが，Mecabが簡単でしたのでしようしました．<br>Mecabの名前の由来は，開発者の好物が「海藻のメカブだった」ことだそうです．<br>コードは以下の通りです．</p>

```python
import MeCab
import sys
import re
from collections import Counter
import pandas as pd

# レビューはcsvで出力したので読み込む
df = pd.read_csv('joker_reviews.csv')

# 形態素解析の準備
m = MeCab.Tagger()

items = []
# 1行ずつ解析
for i in range(len(df)):
    # 形態素解析
    parse = m.parse (df.review_text[i])
    # 形態素解析の結果を改行で区切ってリスト化
    lines = parse.split('\n')
    
    # 形態素解析の結果を再整理
    for line in lines:
        # リストの要素を\t・,で区切る
        mecab = re.split('[\t,]',line)
        items.append(mecab)
        
# 形態素解析の結果の内を名詞抽出
# 一般名詞をcount
words = [[item[0],1]
         for item in items
         # 名詞をリストに格納
         if (item[0] not in ('EOS', '', 't', 'ー','‼') and
             (item[1] == '名詞') and (item[2] == '一般'))]

word_df = pd.DataFrame(words,columns=['一般名詞','count'])

# '一般名詞'ごとに頻度計算
out_df = word_df.groupby(['一般名詞']).sum().reset_index()
```


頻出単語の集計が終わりましたので，上記の出力結果の頻出単語の上位30位をグラフで示します．

```python
# グラフ描画のためmatplotlibのpyplotをインポート
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

#最大表示列数の指定（ここでは50列を指定）
pd.set_option(&#39;display.max_columns&#39;, 50)
pd.set_option(&#39;display.max_rows&#39;, 500)

# ノートブック内で拡大・縮小可能
#%matplotlib notebook
%matplotlib inline

# seabornのスタイルでプロット
sns.set(style=&quot;darkgrid&quot;, palette=&quot;muted&quot;, color_codes=True,font=[&#39;IPAGothic&#39;])


# 降順に並べる
rank_df = out_df.sort_values(&#39;count&#39;,ascending=False).reset_index(drop=True)

# 棒グラフを出力-x軸
labels = rank_df.iloc[:30,0].tolist()
x_axis = np.arange(len(labels))  # numpyで横軸を設定

# バーの太さ
height = 0.5

# 5月の棒グラフ
# 図の大きさ
plt.figure(figsize=(15,20))
#plt.ylim(0, 900)
y = rank_df.iloc[:30,1].to_numpy()
plt.barh(x_axis,y[::-1],height=height)
plt.yticks(x_axis,labels[::-1])# ラベル

plt.show()
```

# 出力結果

ここからのグラフを見たときの筆者の浅い感想です．<br>タイトルにもなっている「ジョーカー」が一番多いですね．<br>あとは，ジョーカーのキャライメージの「悪，狂気，笑い」や「ダーク，ナイト，ヒース」のジョーカーが登場したダークナイトのキーワードがありますね．<br>意外だったのが「階段」ですね．おそらく，階段でのダンスシーンのことを言ってると思いますが，上位30位に入るほど印象に残ってるとは思いませんでした．

# おわりに

今回は，箸休めがてらレビューの抽出・集計を行いました．<br>次回は年代別で高評価の映画の抽出をします．