映画.comのレビューをスクレイピングする

# はじめに
本記事では、[映画.comの「劇場版 鬼滅の刃 無限列車編」のレビュー][eiga]をスクレイピングし、csvで出力します。

[eiga]:https://eiga.com/movie/91918/review/

# 環境
python 3.10.4
Windows 11 Home
ChromeDriver 114.0.5735.110

# 必要なライブラリのインストール
seleniumとpandasを使用します。
seleniumはブラウザの操作を自動化するライブラリですが、スクレイピングも行うことができます。今回は「次へ」ボタンの押下も自動化し、全レビューの取得を目的とするため、こちらを使用します。
pandasはデータ解析に使うライブラリです。今回はスクレイピングして取得したデータをcsvで出力する目的で使用します。
```python
pip install selenium
pip install pandas
```
# HTMLの構造
映画.comのレビュー部分は以下のようなHTMLで構成されています。（2023年8月現在）

```html
<!-- このdiv要素がレビューの数だけ並ぶ -->
<div class="txt-block">
 <p class="short">レビュー前半（実際に表示されている部分）
  <span>…</span>
  <span class="hidden" style="display: none;">レビュー後半（文字数制限で表示できていない部分）</span>
  <a class="more">続きを読む</a>
 </p> 
</div>

<!-- 次へボタンが押せる場合 -->
<a class="next icon-after" href="url">次へ</a>

<!-- 次へボタンが押せない場合 -->
<span class="next disabled icon-after">次へ</span>

```
# 作成コード
上記のHTML要素を取得し、スクレイピングを行います。

```python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import pandas as pd
import time

# ブラウザの起動
chromedriver = "chromedriver.exe"
service = Service(execureviews_path=chromedriver)
driver = webdriver.Chrome(service=service)
url = "https://eiga.com/movie/91918/review/" 
driver.get(url)

# データを取得し、配列に格納する
reviews = []
is_continue = True
while(is_continue):
  text_elems = driver.find_elements(By.CLASS_NAME, "txt-block")
  for text_i in text_elems:
    text = text_i.get_attribute("textContent")
    if '続きを読む' in text:
      text = text.replace("…","").replace("続きを読む","")
    if 'ネタバレ！ クリックして本文を読む' in text:
      text = text.replace("ネタバレ！ クリックして本文を読む","")

    # 配列にデータを格納
    reviews.append(text)

  #　次へボタンが押せなければループを終了、押せるなら押して次のページへ
  if driver.find_elements(By.CSS_SELECTOR, ".next.icon-after.disabled"):
    is_continue = False
  else:
    next_button = driver.find_element(By.CSS_SELECTOR, ".next.icon-after")
    next_button.click()
    time.sleep(2)

#　配列からDataFrameを作成し、csvで出力する
df = pd.DataFrame(reviews, columns=['レビュー'])
df.index.name = 'index'
df.to_csv("無限列車編_レビュー.csv", encoding="utf-8")

#ブラウザの終了
driver.quit()
```

# 解説

```python:解説1
text_elems = driver.find_elements(By.CLASS_NAME, "txt-block")
for text_i in text_elems:
  text = text_i.get_attribute("textContent")
  if '続きを読む' in text:
    text = text.replace("…","").replace("続きを読む","")
  if 'ネタバレ！ クリックして本文を読む' in text:
    text = text.replace("ネタバレ！ クリックして本文を読む","")
```
- `driver.find_elements(By.CLASS_NAME, 'txt-block')`
クラス名が「txt-block」である要素を全て取得
- `text_i.get_attribute("textContent")`
クラス名「txt-block」を持つタグが内部に持っている全文字列を連結して取得
- `replace("…","")`
文字列中に含まれる「…」を削除する

`text_i.text`とすると、`display: none`指定されているレビュー後半部分を取得することができませんでした。そのため、`text_i.get_attribute("textContent")`で、`display: none`指定されている部分を含めた全文字列を取得し、その後、不要な文字列を`replace`で取り除いています。


```python:解説2
if driver.find_elements(By.CSS_SELECTOR, ".next.icon-after.disabled"):
  is_continue = False
else:
  next_button = driver.find_element(By.CSS_SELECTOR, ".next.icon-after")
  next_button.click()
  time.sleep(2)
```
- `driver.find_elements(By.CSS_SELECTOR, ".next.icon-after.disabled")`
クラス名「next」、「icon-after」、「disabled」を持つ要素（押せない次へボタン）を取得
- `time.sleep(2)`
2秒間待機

押せない次へボタンがある場合はデータ取得処理を終了し、ない場合は次へボタンを押して次のページに進みます。
2秒間待機を挟むのは、ページが読み込まれる前に要素の検索が実行され、エラーとなるのを防ぐためです。

# 終わりに
今回は映画.comのレビューをスクレイピングしました。
ボタンを押したら文字を表示する仕組みは、他のwebサイトでも活用されているので、`.get_attribute("textContent")`は応用が利きそうと感じました。
今後は取得したcsvを使ってテキストマイニングを行う予定です。