RubyとSeleniumとFirefoxを使ってWebスクレイピングをする時のtips


今までに作ったスクレイピングシステムで得られたノウハウを書く。たまにしかやらないし、目的を達成したらもういじらないし、すぐ忘れるので備忘録として。

使うツール

  • Ruby
    • 楽だし慣れてるから。
    • しかし、Selenium bindingsの対応具合からするとPythonの方がいいような気がする。
  • Selenium
    • Webブラウザをプログラムから操作するためのアイツ。
    • 本来はテスト用のツールのような気がするが、細かいことは気にしない。
    • MechanizeとかPhantomJSとかの、プログラム制御専用の実装もあるが、人様の作ったWebサイトを読み込む場合は、普通のブラウザでないとまともに動かないことが懸念される。特にログインが必要で、かつ限られた人向けのサイトは。
    • 3.0がリリースされてたけど、なんか動かなかったので2.x系を使った。
  • Firefox
    • ESR版を使った。 https://www.mozilla.jp/business/
    • ブラウザとSeleniumとの間でバージョン不整合により動かなくなることがよく起きるので、あまり頻繁に更新したくない。
  • Debian 8
    • Firefox ESR版のパッケージが含まれているメジャーなdistroが他になかった。
    • rpm系は普段使わないので調べてない。
  • docker
    • 時代はコンテナらしいのでがんばってついていきたい。

Implicit Wait と Explicit Wait

昨今のWebサイトはやたらと非同期処理があるので、適切にwaitを入れてやらないと目的のDOMが構築される前に探しに行って、例外が発生してしまう。

今までは適当にsleepとか入れていた(ひどい)のだが、正しい方法がちゃんとあった。

Implicit Wait

これをやっておくと find_element を実行した時、目的の要素が現れるまで「探す→待つ」を繰り返してくれる。指定した秒数を超えるとtimeoutして例外が発生する。 Selenium::WebDriver インスタンス生成時に指定する。

driver = Selenium::WebDriver.for :firefox, :profile => Selenium::WebDriver::Firefox::Profile.new
      browser.manage.timeouts.implicit_wait = 10

Explicit Wait

特定のリクエストについて、特別に長く待ちたい時などに使う。

# まずインスタンスを作る
wait = Selenium::WebDriver::Wait.new(:timeout => 30)

特定の要素が現れるのを待って取得する

e = wait.until { driver.find_element(:xpath, "//input[@name='sometext']") }

要素が clickable になるのを待つ

e = wait.until { e.displayed? && e.enabled? }

Seleniumには ExpectedConditions というクラスがあるらしいのだが、Ruby Selenium bindingsには無いらしい。また clickable というのは要するに displayed? && enabled? のことらしい。 https://stackoverflow.com/a/29903931

テキストボックス(<input type="text" />)に入力する

driver.find_element(:xpath, "//input[@name='sometext']").clear
driver.find_element(:xpath, "//input[@name='sometext']").send_keys("hogehoge")

clear しておいた方が、続けて検索する時とかにハマらなくて済む。

特定の文字列がページ内にあるかチェックする

driver.page_source.match(/ほげほげ/u)

# これは思ったより重い
driver.find_element(:xpath, "//body").innerHTML.match(/ほげほげ/u)

あとがき

なんかあったら追記していく。