【第２回】Ruby + Seleniumを使ってスクレイピングしてみる

##@cosmeの商品レビューをスクレイピングしてみる
第１回の前置きが長くなってしまいましたが、今回からコーディングしていきたいと思います。

###想定仕様
１.プロダクトIDが入ったURLを受け取る
２.上記で指定した商品の各レビューページに設置されている「続きを読む」リンクを踏み、全文レビューページに遷移
３.レビュー全文を取得する
４.同ページにある「次へ」リンクを踏み、次のレビュー全文ページに飛ぶ
５.３＆４を任意で指定したレビュー取得件数分繰り返す
６.完了したらchromeを閉じる

こんな感じの仕様ができればと思います。Seleniumを使うことで、「**4.同ページにある「次へ」リンクを踏み、次のレビュー全文ページに飛ぶ**」という仕様ができるのが本当にデカいですね。これなら各レビューIDを指定する必要もないので、100件でも1000件でも楽々取得できるようになります。

##Let's ruby
今回使用するライブラリはこんな感じです

```ruby
#自動ブラウジング
require 'selenium-webdriver'
#HTMLをパースする
require 'nokogiri'
#指定したURLを開く&レビュー本文を取得
require 'open-uri'
#既存のxlsxフォーマットの書き込み
require 'rubyXL'
```
`nokogiri`と`open-uri`はスクレイピングではほぼ必須のライブラリ。
今回はxlsxファイルへの「書き込み」のみ（読み込みはしない）ですので`rubyXL`を使用します。

###Selenium起動＆全文レビューページに遷移

```ruby
#Seleniumを立てる（今回はchromeを使います)
driver = Selenium::WebDriver.for :chrome

#商品ページのURLを変数input_urlで受け取る
input_url = "https://www.cosme.net/product/product_id/10163439/top"

#input_urlで受け取ったページに移動する
driver.navigate.to("#{input_url}")

#「続きを読む」リンクを踏み、全文レビューページに遷移する
driver.find_element(:partial_link_text,'続きを読む').click

#カウンター（後のwhile文で使います）
count = 0

#取得レビュー件数を変数limitで受け取る
limit = 50

#商品名を変数nameで受け取る(任意の商品名を設定する。出力するxlsxのファイル名になります。)
name = "ほげほげ"

#変数reviewsの空配列を置いておく(取得した各レビューは、変数reviewsの配列の要素として代入されます。)
reviews = []
```
・`driver = Selenium::WebDriver.for :chrome`
今回はchromeで行いたいと思います。firefox, IE, Safariなどにも対応しているようです。

・`limit = 50`
取得レビュー件数はここで指定します。今回はwhile構文を使った繰り返し処理を行いますが、その繰り返しの回数＝limitの値になります。

・`reviews = []`
取得した各レビューは配列の要素としてreviewsに追加できるようにします。

###while文による繰り返し処理

```ruby
while count < limit.to_i
  #現在のページのURLを変数current_urlで取得
  current_url = driver.current_url

  #UTF-8に変換（@cosmeの文字コードはShift_JISなので）
  doc = Nokogiri::HTML.parse(open(current_url, "r:Shift_JIS:UTF-8").read)

  #レビューを指定＆本文のみを変数nに代入
  n = doc.css("p.read").text

  #変数reviewsに配列要素としてnを代入する
  reviews << n

  #カウンターを追加
  count+=1

  #Dos対策（次のレビューページに遷移するまで1秒以上空ける）
  sleep 1

  #ページ内の「次へ」を選択＆ページ遷移
  driver.find_element(:partial_link_text,'次へ').click
end
```
・`while count < limit.to_i`
今回はwhile文を使ってみました。構文内にカウンターを設置し一周ごとに`count+=1`になるようにしてます。limitで指定した取得レビュー件数を上回るまで繰り返し処理を実行する、という仕組みです。

・`doc = Nokogiri::HTML.parse(open(current_url, "r:Shift_JIS:UTF-8").read)`
なんとよくよくみたら@cosmeはShift-JISだったことが判明。UTF-8に変換しておきます。

```
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis">
```

・`sleep 1`
次のHTTPリクエストまで1秒以上空けます。

###取得したレビューをEXCELに書き込む

```ruby
#フォーマットのxlsxを選択
workbook = RubyXL::Parser.parse("@cosme_format.xlsx")

#シートを指定＆シート名を変更
worksheet_a = workbook[0]
worksheet_a.sheet_name ="@cosmeレビュー収集"

#指定したセルに各レビューを書き出す
p = 1
reviews.each do |re|
  worksheet_a.add_cell(p,2,"#{re}")
  p+=1
end

#書き込みが完了したらブラウザを終了する
driver.quit

#保存する(序盤で任意で設定したnameの値がここに代入されます。)
workbook.write("@cosmeレビュー収集結果_#{name}.xlsx")
```
・`workbook = RubyXL::Parser.parse("@cosme_format.xlsx")`
エクセルを操作できるrubyのライブラリは色々ありますが、今回は読み込みはせず書き込みだけなので`rubyXL`を使ってみました。

###アウトプット例
モザイクかけましたがこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519598/e1f74331-b2fb-634f-3dcb-8b3b1f971d94.png)

#まとめ
・`sleep 1`で次のリクエストまで１秒開けているものの、あまりにも膨大な量を続けてリクエストすることはあまり良くないので、使用には十分注意する。長時間連続して回し続けないこと。
・特殊絵文字？などがレビュー本文に含まれている場合、エラーが起きて処理が止まってしまうので例外処理を用いて止まらないようにする。