できればMechanizeで動かしたいけど、Watirでブラウザのオートパイロットが楽だしねぇ。
なんでWatirなの?
Q:Watir骨董品じゃん?Capybara-webkitで良いんじゃない?
- A1: Capybara 機能多すぎてスクレーピングには初期学習コストが高そう。
- A2: Capybara はテストフレームワークであってオートパイロットシステムでもない、そのため検索してもスクレーパー以外の記事が多い
- A3: Selenium には wait_until_present? という超便利な関数がない。
- A4: PhantomJS も自動操縦向きじゃない(PDF取得とか)
というわけで、レガシィな物を持ち出しましたら、意外に使えrfdsふぁsd(っていうか中身Seleniumだし)
すいません、嘘です。OSXでCapybaraインストールにコケたので一昨年からWatir使ってました。
インストール
gem install watir watir-webdriver
WindowsならWin32OLEで更に遊べる。
初回起動
require "watir" require "watir-webdriver" profile = Selenium::WebDriver::Firefox::Profile.new browser = Watir::Browser.new :firefox, :profile => profile browser.goto "https://id.auone.jp/"
Headless(xvfb)経由で起動
X Virtual Framebuffer の仮想X11 ( DISPLAY:99 ) で起動する xvfb は X11 の vfb のこと。vfb は virtual frame bufferのこと。
require 'headless' require "watir" require "watir-webdriver" Headless.new headless.start profile = Selenium::WebDriver::Firefox::Profile.new b = Watir::Browser.new :firefox, :profile => profile headless.destroy
自動処理の時はブラウザ画面を起動せない。
Windows/mac OS X では、そのままではxvfb-run は使えない。Xvfbを動かすにはx11が必要。
IEをWin32 OLEで使うのなら IE を非表示できるが、OSXとSafariだとどうやるかはちょっとわからないです。
ブラウザのバージョンを調べる
b.driver.capabilities
もしくは、exec_script を通してwindow.navigator.userAgentを実行しUAなどを見る
watir でXPATH
browser.element( xpath: "//a[contains(.,'ポイント利用状況')]" ).click
watir でCSSセレクタ
browser.element( css: "input[type='password']" )
watir でJS実行
browser.execute_script("document.querySelector('div#side').remove()")
watir で表示中のHTML取得
browser.html
watir で要素のHTMLの取得
browser.(css:"#sample").html
watir で特定属性にアクセス
browser.a(:css => "div.foo").html browser.a(:css => "div.foo").href browser.a(:css => "div.foo").text
特定のタグとCSSセレクタの組合せ
browser.h1( css: ".header" ) browser.a(:css => "div.foo")
特定のタグとXpathの組合せ
browser.h1( xpath: "./a[contains...]" )
正規表現で要素を絞る
browser.a( html: /login/ )
browser.a( text: /ログイン/ )
JSで要素へ命令文
browser.execute_script("document.querySelector('a#side').click()")
フォームに値を仕込む
browser.element( css: "input[type='password']" ).set "mypassword"
基本設計としてリードメインで作られているらしいWatirなので、#set
経由でないと値を送れない。
キーボードイベントが発生しないので特定のフォームでは動かない可能性
JSを使っても出来る
browser.execute_script( "document.querySelector('input[type=password]').value='%s'" % password )
ページの更新reload
browser.refresh
もちろんjsでも出来る。
要素の属性値の書換
js でしか出来ない
browser.a( href: /dpoint/).target="" # 出来ない
Javascriptでやる。
ret = document.evaluate("(//a[contains(@href,'dpoint')])[1]",document,null,XPathResult.ANY_TYPE,null); a = ret.iterateNext(); a.target=''
面倒だけど他に方法がない。
Ajax メインのページのロード待ち
JSライブラリで出来たページは、 browser.wait
では検出できない
特定の要素が現れるまで待つ。
browser.div(css:"#active_content").wait_until_present
逆に、特定の要素が消えるまで待つ。
browser.div(css:"#deActive_content").wait_while_present
これで待つしか無いようだ。
exec_scriptのjsを通してブラウザの状態を取得することもできるが、特定の要素の出現を待つほうが確実でした。
特定の要素を消す。
remove系は存在しない。DOM操作はJSから作業する
browser.execute_script("document.querySelector('#{selector}').remove();")
要素があるかないか
未ログインやログイン済、ロード済の判定など、指定の要素がDOMに存在スルか調べるとき
if my.browser.img(alt:/未認証/).present? then do_something end
要素の存在・表示・可視のチェック3種
exits?は、ほとんど使うことがないとおもう。
Element | .exists? | .present? | .visible? |
---|---|---|---|
Displayed (browser.div(:id => 1)) | true | true | true |
Not Displayed (browser.div(:id => 2)) | true | false | false |
Non-Existent (browser.div(:id => 3)) | false | false | exception |
wait / wait{while,until}present の違い
- wait は、
document.readyState == 'complete' or timeout 5 sec
が発火条件 - wait{while,until}presentは、Elementsが表示されるのが発火条件
wait よりもwait{while,until}present を使うほうが無難なことが多い。
元のウインドウに戻して!
いまどき別ウインドウをあげちゃうようなサイトがあるので、もとのウインドウに戻して
browser.windows[n].use
ウインドウをアクティブにして
browser.activate
watir 起動が面倒くさい
rake irb を使って自動処理しておく
takuya@~:$ rake irb >> init >> browser >> browser.goto "http://au.kddi.com"
Rakefile
task :pry do require 'pry' $LOAD_PATH << "./lib" def init; # 初期化処理 require 'headless' require "watir" require "watir-webdriver" # よく使う関数とか end binding.pry end
irb ならこんな感じ
task :irb do ARGV.clear IRB.conf[:SAVE_HISTORY] = 100000 IRB.conf[:PROMPT_MODE] = :SIMPLE IRB.conf[:AUTO_INDENT] = true IRB.start end
困ったときのDocument
watir-webdriverを見ると何となくわかってくる。
それでも困ったら
Selenium Driverを使う限りSeleniumのドキュメント見たほうが速いです。
https://github.com/SeleniumHQ/selenium/wiki/Ruby-Bindings
参考資料
http://stackoverflow.com/questions/9296353/running-javascript-in-watir-webdriver
http://stackoverflow.com/questions/9302737/ruby-watir-to-get-html-of-a-page
http://stackoverflow.com/questions/12531705/how-to-get-element-with-css-in-pageobject
https://jkotests.wordpress.com/2012/11/02/checking-for-an-element-exists-visible-present/