Selenium WebDriver + Ruby でWeb関係の自動化

RubyからSelenium WebDriverというのを最近知りました。 Webの自動化がとても簡単にできるようになり、感動したのでメモしておきます。

Selenium WebDriver がないとき

Webの自動化というと、ほぼ「Webブラウザの真似をするという事」になると思います。 操作したときにサーバーとどんなやりとりをしてるのか、ブラウザの開発ツール等を使って調べて、 同じ事をするようにコードを組んで…という流れになると思います。 これがなかなか思ったようにできません。

また、Webブラウザが、cookiejavascriptといった得体の知れないものも面倒見ている、 という事も考慮しなければなりません。「ページの内容によって処理が変わるけど、 見るべき要素は実はjavascriptの実行結果として生成されていた」なんてことになるともうお手上げ状態です。 普通にやってる人もいると思いますが、私の技倆ではちょっと…。

Selenium WebDriver があるとき

Selenium WebDriver は本物のWebブラウザを制御できます。 「このボタンを押すとこのURLにこんなものをPOSTするのかーふむふむ」とか考えなくてよくて、 単に「このボタンをクリックして!」と命令すればよいわけです。

「じゃあここにカーソルのっけて〜、ポップアップしてきたここをクリックして〜、うん、すごくいい、すごくかわいいよ〜」 みたいなあんな事やこんな事がいとも簡単(当社比)にできてしまいます。

Selenium というのが何かは実はよくわかっていませんが、 「selenium-webdriver という gem をインストールすると ruby からブラウザを制御できる!」 というのはおそらく間違いなさそうです。

インストール

gem install selenium-webdriver

これだけでいろいろできます。 ページの内容をごにょごにょしたいときは nokogiri も入れておくとさらに便利です。

操作いろいろ

私の場合は以下のものだけで充分事足りてます。

開始

require "selenium-webdriver"
require "nokogiri"

webdriver = Selenium::WebDriver.for(:firefox)

終了

webdriver.quit

URLを指定して取得

webdriver.get(url)

ページのソースからNokogiriオブジェクト

doc = Nokogiri::HTML(webdriver.page_source)

要素の取得(XPathを使う場合)

element = webdriver.find_element(:xpath, xpath)
  • 要素が見付からない場合は例外 Selenium::WebDriver::Error::NoSuchElementError を投げます。
  • クリックやキー入力といった操作はここで取得したオブジェクトに対して行います。
  • find_elements を使うと要素を配列で返します。複数取得可。見付からない場合は空の配列になります。
  • find_element(s) は要素オブジェクトに対しても使えます。

テキストエリア等に文字列入力

element.send_keys(str)

ドロップダウンメニューの選択(文字列指定の場合)

Selenium::WebDriver::Support::Select.new(element).select_by(:text, str)

チェックボックスの状態取得

element["checked"] # => true or false

マウスカーソルをのせる

webdriver.action.move_to(element).perform

shift押しながらクリック

webdriver.action.key_down(:shift).click(element).key_up(:shift).perform

ウインドウの切り替え

webdriver.switch_to.window(handle)

handle(ウインドウを識別する文字列)の取得は下記のとおり

current_handle = webdriver.window_handle
all_handles = webdriver.window_handles

ウインドウを閉じる

webdriver.close
  • 最後のウインドウを閉じるとブラウザが終了します

注意点

javascriptの実行タイミング

ページの取得(getやリンクのclickなど)は、メソッド呼び出し後、ページの読み込みが終わると制御が戻って、次の処理に進みます。そのため、ページ読み込み後に動き始めるjavascriptの実行後に生成される要素などを読み込み直後に取得しようとすると失敗する場合があります。

下記のようにすると、要素が現れるまでの待ち時間を設定できます(秒)

webdriver.manage.timeouts.implicit_wait = 1

page_sourceで取得するソースもjavascriptの実行結果の反映前後で変わってくる場合があるので同様の注意が必要です。

※私はうまくいかないところにひたすらsleepをつっこんでますけど…

見えない要素はさわれない

マウスカーソルを乗せるとポップアップする要素などは、見えてない状態で操作する事はできません。 ブラウザで操作するときと同じ手順を踏む必要があります。

リファレンス

http://www.rubydoc.info/gems/selenium-webdriver/Selenium/WebDriver/

他にもgoogleで色んなページ見て参考にさせていただいたのですが、控えてませんでした…。