[ruby]自動ブラウザ操作[Watir]

Watir

rubyのスクレイピングライブラリには「nokogiri」があります。
nokogiriはブラウザページから必要なテキストやデータを吸い取ることができます。「mechanize」と併用すればユーザーエージェントの指定やログイン処理も可能です。私的にnokogiriが素晴らしいと思う点は、スクレイピングのためのコードが感覚的に、簡単に書けるところです。rubyが分からなくても、cssを知っていればそれなりにnokogiriを活用できるかもしれません。

さて、しかしnokogiriは動的なページ、つまりjavascriptが使われたページに対応できません。jsでhtmlやcssを書き換えられた場合、その後のコードを吸い取れないのです。
そこで、これから記述をしていく「watir-webdriver」というライブラリです。こちらは実際にブラウザを立ち上げて自動操作を行うので、jsによるボタンクリック等の挙動にも対応できます。手放しでのネットサーフィンはもちろん、スクレイピングも可能です。

irb

Watirを活用したコードを書き始める場合、まずコマンドプロントからirbを開き、実際にブラウザの挙動を確認しつつコードを作成するのが良いです。
[testing]5分でわかるWatir
irbでのWatir操作は上記のサイトがチュートリアルとして分かりやすいです。

環境

windows7x64

library

lib
gem install watir-webdriver
require 'watir-webdriver'

Classes,Methods,WebDriver

Class List
Watir WebDriver

browser

とにもかくにもブラウザの起動です。
デフォルト設定がfirefoxで、パラメータにはieやchromeが指定できます。
firefox以外のブラウザ操作にはドライバが必要になります。

default(Firefox)

browserMethod(Browser)
b = Watir::Browser.new :firefox

Chrome

chromedriverをダウンロード。(download)
解凍ファイルをパスの通った場所に置く。

browserChrome
b = Watir::Browser.new :chrome

IE

seleniumからIEDriverServerをダウンロード。(download)
『selenium-master > cpp > prebuilt > Win32(x64)』
IEDriverServerをパスの通った場所に置く。
IEを起動し『インターネットオプション > セキュリティ』で
セキュリティゾーン4つ全て、保護モードを有効にするにチェック。

browserInternet Explorer
b = Watir::Browser.new :ie

user-agent

ライブラリ『webdriver-user-agent』で切り替えます。(github)
使い方、オプション等githubに記載されています。
mechanizeでも可能っちゃ可能ですが、併用すると色々とごっちゃになるので今回は止めておきましょう。

library

install
gem install webdriver-user-agent

switching

user-agentMobile Devices
require 'webdriver-user-agent'

driver = Webdriver::UserAgent.driver(
:browser => :chrome,
:agent => :iphone,
:orientation => :landscape
)
b = Watir::Browser.new driver
b.goto('http://www.useragentstring.com/')

#ブラウザバージョンの確認
b.driver.capabilities

element

操作はブラウザ上の要素を指定して処理を行います。また指定方法にも多様のアプローチがあります。要素に対してflashを使用することで、視野的に目的の位置を指定できているのか確認をすることができます。

パフォーマンスの違いは
Are :xpath locators slower?
が参考になります。ケースバイケースでベストな書き方が変わるみたいです。

google
#googleページを開く
require 'watir-webdriver'
url = 'https://www.google.co.jp/'
b = Watir::Browser.new :chrome
b.goto(url)
n番目の要素
#watir
b.div(id: 'gb').a(index: 0).flash
#css
b.a(css: 'div#gb a', index: 0).flash
#xpath
b.a( xpath: '//div[@id="gb"]//a[1]').flash
属性
#watir(ハイフンはアンダーバーに)
b.a(data_pid: '23').flash
#css
b.a(css: 'a[data-pid="23"]').flash
#xpath
b.a( xpath: '//a[@data-pid="23"]').flash
other
#text
b.a(text: 'Gmail').flash
#attr
b.a(href: 'https://mail.google.com/')
#regex
b.a(text: /mail/).flash
b.a(href: /mail\.google\.com/).flash
#next_element
e = b.div(class: 'gb_Q', index: 0)
e.div(xpath: './following-sibling::*[1]').flash
#prev_element
e = b.div(class: 'gb_Q', index: 1)
e.div(xpath: './preceding-sibling::*[1]').flash
複数の要素
#タグ名に複数形の[s]を付ける
links = b.as(css: 'div#gb a')
links.each do |link|
puts link.text.split
end

reading

reading
#html
b.div(id:'gb').html
#text
b.div(id:'gb').text
#href
b.div(id:'gb').a(index: 0).href
#attr
b.div(id:'gb').attribute_value('class')
#style
b.div(id: 'hplogo').style('background-image')
#url
b.url

image

SSL証明で引っかかった場合はca-bundle.crtをダウンロードし、環境変数の新規追加から変数名:SSL_CERT_FILEでパスを通してみてください。ググれば詳しく解説してくれているサイトがいっぱいあります。

image
#保存フォルダ
path = File.expand_path('C:\Users\user\Downloads')
#screenshot
b.driver.save_screenshot("#{path}/screenshot.png")
#save(logo)
require "open-uri"
src = b.div(id: 'hplogo').style('background-image')[/\"(.+)\"/, 1]
File.open("#{path}/logo.png", 'wb') do |f|
f.write open(src).read
end

form,input

login
#gmailログインページ
b.div(id: 'gb').a(index: 0).click
b.a(:data_g_label => 'Sign in').click
#メールアドレス入力
address = 'aaa@gmail.com'
b.text_field(name: 'Email').value = address
b.button(name: "signIn").click
#パスワード入力
psw = '1234'
b.text_field(name: 'Passwd').value = psw
b.button(name: "signIn").click
#チェックボックスを外す
if b.checkbox(name: 'PersistentCookie').set?
b.checkbox(name: 'PersistentCookie').clear
end
#ドロップダウンリスト
b.select_list(name: 'list').select_value(value)

exists?

参考:Checking for an element – exists?, visible?, present?

要素が存在しているかを確認できます。
watirで要素を処理する際には、いずれにせよ要素が画面上に見えてなければいけないので基本的に.present?で問います。
nokogiriの場合at_css()で要素を問えばそのまま存在確認ができるので、それに慣れてしまうと多少面倒に感じますね。

Element .exists? .present? .visible?
Displayed true true true
Not Displayed true false false
Non-Existent false false Exception

javascript

js
#js
b.execute_script <<-JS
alert('test');
JS
#jQueryの動作を待つ
Watir::Wait.until(30){
b.execute_script("return jQuery.active") == "0"
}

waiting

参考:Waiting

loop
#例:要素が存在するまで待機
Watir::Wait.until { b.button(class: 'submit').present? }
b.button(class: 'submit').click
#例:要素が出現するまでループ
loop do
b.div(id: 'box').present? ? b.refresh : break
end
#例:要素が出現するまでループ
until b.li(class: 'list').present?
b.button(id:'next').click
end

scroll

watirで要素に対して何かしらする際、ブラウザ上に対象の要素が見えていないとエラーを出されてしまいます。例えばブラウザに見えていない下部のボタンをクリックしたい場合、そのボタンの位置までスクロール処理を行う必要があります。
スクロールはjavascriptあるいはプラグイン『watir-scroll』を活用します。プラグインを利用すれば容易にスクロール処理が行えますので、そちらの方法でいきます。(github)

lib
gem install watir-scroll
watir-scroll
require 'watir-scroll'
btn = b.a(class: 'btn')
b.scroll.to(btn)
sleep(0.2)
btn.click

resize

ウィンドウを広げておくとスクロール処理を減らすことができるかもしれません。

resize
#resize_to(ヨコ, タテ)
b.window.resize_to(1000, 800)