【Ruby】Seleniumでスクレイピングするときに起こりがちなあるあるエラー5選とその対処法

Webスクレイピングに役立つツール<b>「Selenium」</b>を使うときにおこりがちなエラー5選をまとめました。 

# 環境
- mac OS Catalina 10.15.6
- Ruby 2.6.3
- Rails 6.0.3.2

# はじめに
Seleniumは複雑な操作をするWebスクレイピングするときに役立つgemです。

<b>RubyでWebスクレイピングするときに使えるgem</b>

- Nokogiri （基本）
- Mechanize （かんたん） 
- Selenium （ブラウザ操作でいろいろできる）

NokogiriやMechanizeでは、HTMLタグやCSSを指定してページの内容を取得します。  
Seleniumではプログラムでブラウザを開いて擬似的に操作できるので、複雑な処理ができます。

- ログインが必要な場合
- JavaScriptでページを描画している場合
- データを入力したい場合

など、HTMLタグやCSSセレクタだけでは必要な情報を取得できないときに使用すると便利です。

# Seleniumの使い方
Seleniumの使い方はいろんな方がまとめてくれているのでそちらを見てください。


[[Ruby] seleniumでChromeを使ってWebスクレイピング](https://qiita.com/y-agatsuma/items/ea2c9845ee0a931d5c9c)



[Seleniumチートシート [Ruby]](https://morizyun.github.io/web/selenium-cheat-sheet.html)



```ruby
# Selenium使用に必要なツールをインストール
require 'selenium-webdriver'

# Seleniumを起動
driver = Selenium::WebDriver.for :chrome
```

# Seleniumを使うときのあるあるエラー5選
## 1. ブラウザとSeleniumを動かすツールのバージョンが違う
```ruby
Selenium::WebDriver::Error::SessionNotCreatedError (session not created: This version of ChromeDriver only supports Chrome version 75)
```

Seleniumではブラウザの種類を指定してプログラムでマウスを動かします。

```ruby
# seleniumの起動
driver = Selenium::WebDriver.for :chrome
```

Chromeを使いたい場合はChromeと同じバージョンのChromedriverをインストール。  
FirefoxやChromeなど、使用したいブラウザを開発環境にインストールしておく必要があります。  
  
<b>Google Chromeのバージョン確認方法</b>
Google Chromeの設定＞ヘルプ＞Google Chromeについてから、バージョンを確認  
![20190515051326.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238801/964c3336-561b-bb25-6117-e0383553e271.png)

<b>Chromedriverのインストール方法</b>
[[Ruby] seleniumでChromeを使ってWebスクレイピング](https://qiita.com/y-agatsuma/items/ea2c9845ee0a931d5c9c)


Herokuにデプロイして使う場合は、Heroku上にもGoogle ChromeとChromedriverのインストールが必要です。
また、Chromeをheadlessで動かすためのオプションを追加する必要があります。  
[HerokuにChromeとChrome driverを入れておく](https://blog.ikappio.com/put-chrome-and-its-driver-in-heroku/)


[[無料]Chrome headless + seleniumをherokuで定期実行](https://katsulog.tech/regularly-run-chrome-headless-selenium-with-heroku/)



[Herokuのrailsアプリでheadless chromeを使ってwebページのスクリーンショットをとる](https://qiita.com/junara/itwaems/375c4b18486cb4fd2a43)




## 2. Webページを開けなかった

```ruby
Selenium::WebDriver::Error::InvalidArgumentError (invalid argument: 'url' must be a string)
```

### ダメな例：変数で渡す

```ruby
# URLを開く
@url = 'https://www...'
driver.get(@url)
driver.get("#{@url)")
```

()の中がrubyの変数だとダメなのか？
文字列にしろと怒られました。

### OKな例：文字列をそのまま書く

```ruby
# URLを開く
driver.get('https://www...')
```

## 3. ページ内の要素をうまく取得できなかった

```ruby
Selenium::WebDriver::Error::NoSuchElementError: no such element: Unable to locate element: {"method":"id","selector":"#entryBtn"}
```

HTML/CSSに対応する要素がないときに起こるエラー。  
クラス名などをちゃんと指定できてるかチェック。

### nokogiriの場合：CSSセレクタで要素を指定
CSSセレクタで取得したい要素を指定します。

```ruby
# nokogiri使用に必要なツールをインストール
require 'nokogiri'
require 'open-uri'

# Nokogiriを使用してページの要素を取得
html = Nokogiri::HTML(open('https://www.google.co.jp/'))
logo = html.css('#hplogo')
```

### seleniumの場合：要素の種類＋要素名（HTMLタグ、CSSのクラス名など）で要素を指定
要素の種類＋要素名で取得したい要素を指定します。

```ruby
# selenium使用に必要なツールをインストール
require 'selenium-webdriver'

# Seleniumを起動してページの要素を取得
driver = Selenium::WebDriver.for :chrome
driver.find_element(:id, 'hplogo') 
```

seleniumだと「#」がいらない。

## 4. ページ上にないSelenium要素に対して操作をしている
```ruby
Selenium::WebDriver::Error::StaleElementReferenceError (stale element reference: element is not attached to the page document)
```

ブラウザバックして、前のページにあった要素に対して操作しようとしたときに起きたエラー。

### ダメな例：ループの2回目以降で変数が空になってしまっている

```ruby
# selenium使用に必要なツールをインストール
require 'selenium-webdriver'

# Seleniumを起動
driver = Selenium::WebDriver.for :chrome

# イベント一覧ページからイベント詳細ページのURLを取得する
events = driver.find_elements(:class, 'eventItem')

# イベント詳細ページへ移動する
for i in 0..events.size()-1
  # イベント詳細ページへのボタンをクリック
  events.find_element(:class, 'entryBtn').click()
　# →2回目以降のループ処理でエラーになる

  # 前のページに戻る
　driver.navigate.back
end
```

### OKな例：ループ処理の中で要素を取得し直す

```ruby
# selenium使用に必要なツールをインストール
require 'selenium-webdriver'

# Seleniumを起動
driver = Selenium::WebDriver.for :chrome

# イベント一覧ページからイベント詳細ページのURLを取得する
events = driver.find_elements(:class, 'eventItem')

# イベント詳細ページへ移動する
for i in 0..events.size()-1
  # 2回目以降のループ処理の際にドライバがなくなってるので、再度ドライバ指定
  events_in_loop = driver.find_elements(:class, 'prfItem')

  # イベント詳細ページへのボタンをクリック
  events_in_loop[i].find_element(:class, 'entryBtn').click()

  # 前のページに戻る
　driver.navigate.back
end
```

ループしているとドライバが有効でなくなってしまうので、ループ内でもう一度ドライバを取得し直す必要がある。

参考
[get StaleElementReferenceException error while using driver.navigate().back() in a loop in selenium](https://stackoverflow.com/questions/38366251/get-staleelementreferenceexception-error-while-using-driver-navigate-back-in)



## 5. Rubyのコマンドが入力できなくなる
「終わった〜」という感じの環境構築系エラーです。

```
 `require': incompatible library version - /Users/cathy/Desktop/work/vagrant/Test/vendor/bundle/ruby/2.5.0/gems/pg-0.19.0/lib/pg_ext.bundle (LoadError)
```

gemをアンインストールしたりしてたら別のエラーが出て抜け出せなくなりました…

```
/Users/cathy/.rbenv/versions/2.5.1/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- rubygems/core_ext/kernel_warn (LoadError)
```

Seleniumのgemがなくなってしまったのにrequire 'selenium-driver'の記述があることが原因らしい？  


[gemファイル cannot load such file](https://teratail.com/questions/118997)


いろいろやっても解決しないので、結局Rubyのバージョンをあげたらなんとか動くようになりました。  

[Rubyのバージョンアップしたときに躓いたところ](https://qiita.com/taki4227/items/754a2f6ac27595b37dab)