人間がブラウザを用いて手作業でできること:
は、プログラミングによって自動化できます。例えばPerlではWWW::Mechanizeというモジュールを用いて上記のような自動操作が可能です。同様にRubyにもMechanizeというライブラリがあります。有益なまとめ記事も多数書かれています。
しかしながら、これらのツールはFirefoxやIEといった各種ブラウザを完全にエミュレートできておらず、例えばJavaScriptが多用されているサイトだと自動化が簡単には達成できなかったりします。そこで、Seleniumという、ブラウザをエミュレートするのではなくブラウザを操作するツールを用いることで作業を自動実行することを試みます。
Seleniumはブラウザを自動操作するためのプロジェクト群です。簡単な自動化であればSelenium IDEというプロジェクトで開発されているFirefoxのアドオンを使うのがよさそうですが、ここではもっと複雑な自動化にも対応できるSelenium WebDriverの使用方法を紹介します。
Java, C#, Python, Ruby, Perl, PHPがサポートされていますが、ここではRubyを用いた例を示すことにします。
このページで紹介する内容を越えるものは、公式ドキュメントを参照してください。
ターミナルで以下のコマンドを実行します。
gem install selenium-webdriver
Seleniumを使用するためには、下記二行が必要です。ただし、ruby1.9以降では"require 'rubygems'"は不要です。
#!/usr/bin/ruby
require 'rubygems' # not required for ruby 1.9 or if you installed without gem
require 'selenium-webdriver'
各種ブラウザを操作するためには、それらが事前にインストールされている必要があります。Googleのトップページを表示するだけのサンプルを示します。実行後にブラウザが自動で閉じるかどうかはドライバーに依存します (明示的に閉じるためには、quitメソッドを用います)。サポートされているブラウザのうち、Firefox、Internet Explorer、Google Chromeの使用例を示します。
Windows、Mac、Linuxで動作が確認されています。
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://google.com"
現在、Internet ExplorerはWindows版しかありませんので、下記サンプルはWindowsでしか動作しません。ブラウザ本体とは別に、IEDriverServerという、SeleniumとブラウザをつなぐソフトをダウンロードしてPATHを通しておく必要があります。2013-8-21(Wed)現在、最新版はGoogle Codeにホスティングされています。こちらからダウンロード可能です。なお、ブラウザ表示倍率の設定を100%以外にしているとエラーが発生することがあります。その場合は100%に設定してからスクリプトを実行してみてください。
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :ie
driver.get "http://google.com"
Linux、Mac、Windowsでの動作が保証されています。GoogleChromeブラウザ本体のインストールとは別に、Chrome Driverという、SeleniumとブラウザをつなぐソフトをダウンロードしてPATHを通しておく必要があります。2015-2-27 現在、最新版はこちらからダウンロード可能です。
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
driver.get "http://google.com"
IEとChromeの場合、システムのプロキシ設定を参照するため、そちらを手動で書き換えればよいです。Firefoxに関してはシステムのプロキシ設定を参照せず、独自の設定を持つため、プログラム上での設定が必要になります。
#!/usr/bin/ruby
require 'selenium-webdriver'
PROXY = 'proxy.example.com:8080'
profile = Selenium::WebDriver::Firefox::Profile.new
profile.proxy = Selenium::WebDriver::Proxy.new(
:http => PROXY,
:ftp => PROXY,
:ssl => PROXY
)
driver = Selenium::WebDriver.for :firefox, :profile => profile
driver.get "http://google.com"
gemなどのプロキシ設定のためにhttp_proxyという環境変数を定義してしまっている場合、その環境変数を削除するかno_proxyという環境変数を別途用意して127.0.0.1を値として代入しておく必要があります。下記のStack Overflowの解決策で記述されているように、ローカルホストで動作しているWebdriverへのアクセスではproxyサーバにアクセスしないようにする必要があるのです。
Acess denied to /hub/session when launching Firefox webdriver
The issue was not directly to do with permissions but with an environment
variable which was not read when we ran as root. We have the http_proxy
environment variable set with no exclusions for localhost. This meant that
the Python client was attempting to connect to the WebDriver via the proxy.
This was not an issue when run as root because http_proxy was not set.
This issue can be fixed by setting the no_proxy environment variable for localhost.
環境によってはページの読み込みが完了する前に次の処理を開始してしまう場合があります。その場合、セレクターを用いてID指定などで検索しても、JavaScriptで動的に生成される要素などを考慮すると、検索対象が存在しないことになり自動化がうまくいきません。これを回避するために、適宜waitを挟みながら処理を進めるようにプログラミングするとよいです。下記サンプルでは、some-dynamic-elementというIDを持つ要素が (JavaScriptで生成されて) 10秒以内にページ内に出現しない限り例外を投げて、driver.quitで処理を中断するように記述されています。Googleのトップページにはそのような要素はありませんので、10秒後処理が中断されYahooのページは表示されません。
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://google.com"
wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
begin
element = wait.until { driver.find_element(:id => "some-dynamic-element") }
ensure
driver.quit
end
# 実行されない
driver.get "http://www.yahoo.co.jp"
sleep 10
すべてのfind_element(s) で上記timeout構文が必要となる場合、下記のようにdriverにwaitを設定することで一括して設定できます。
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.manage.timeouts.implicit_wait = 10 # seconds
driver.get "http://google.com"
element = driver.find_element(:id => "some-dynamic-element")
なお、単純なwaitであれば、rubyのsleepメソッドで事足ります。これは目視でページの確認をしながら先に進めたい場合に有効です。
ドライバーによってはサポートされていませんが、スクリーンショットを撮影できます。
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://google.com"
driver.save_screenshot "/tmp/google.png"
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://www.example.com/"
p driver.current_url
p driver.title
p driver.page_source
セレクタメソッドは二種類あります。
上記セレクタメソッドに与える式としては、XPathやjQueryでもお馴染のCSSセレクタ等があります。
サンプルコード:
#!/usr/bin/ruby
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://www.example.com/"
# 一般のセレクタ
elements = driver.find_elements(:id => "myid") # ID <div id="myid"></div>
elements = driver.find_elements(:class_name => "myclass") # クラス <div class="myclass"></div>
elements = driver.find_elements(:tag_name => "div") # タグの種類 <div class="myclass1"></div>, <div class="myclass2"></div>,...
elements = driver.find_elements(:name => "myname") # 名前 <div name="myname"></div>
elements = driver.find_elements(:xpath => "address") # XPath
elements = driver.find_elements(:css => "ul#smaple-id li") # CSSセレクタ
# <a>タグに特化したセレクタ
elements = driver.find_elements(:link_text => "click") # <a href="">click</a>
elements = driver.find_elements(:partial_link_text => "click") # <a href="">click here</a>
# メソッドチェーン
elements = driver.find_element(:tag_name => "body").find_elements(:xpath => 'div/p/a')
# イテレート
elements.each do |element|
puts element.text.encode('UTF-8')
end
複雑なサイトの自動化を試みる場合、CSSセレクタおよびXPathの設定が困難です。Firefoxのアドオンを利用することで設定の手間が削減できます。
# 指定した要素内のテキストを取得 (jQueryと似ている)
p element.text
p element.attribute("id")
element.click
driver.navigate.back
driver.navigate.forward
# フォームの内容をクリア
element.clear
# inputまたはtextarea要素への値の代入
element.send_keys("my name");
# ラジオボタン
elements[3].click
# ドロップダウンリスト
select = driver.find_element(:tag_name => "select")
all_options = select.find_elements(:tag_name => "option")
all_options.each do |option|
option.click if (true) # 何かしらの条件を指定
end
# ドロップダウンリストの別の記述方法
select = Selenium::WebDriver::Support::Select.new(driver.find_element(:tag_name => "select"))
select.select_by(:index, 0) # 0,1,2,...
# select.select_by(:text, "mytext") # 表示されるテキストによる選択
# select.select_by(:value, "myvalue") # valueによる選択
# サブミット (elementが所属するformのサブミットが行われる)
element.submit
driver = Selenium::WebDriver.for :firefox
driver.get "http://www.example.com/"
driver2 = Selenium::WebDriver.for :firefox
driver2.get "http://www.example.com/"
JavaScriptのalert(), confirm(), prompt()関数によって、ポップアップボックスが表示されるページがあります。Seleniumでそれらを制御するためには、まず
alert = driver.switch_to.alert
によって制御対象をアクティブなポップアップボックスに移します。そして、下記4つのメソッドによって制御ができます。ポップアップボックスが消えた後は自動的に制御はもとのブラウザに戻ります。
なお、これらのメソッドでは基本認証あるいはベーシック認証と呼ばれるものへの入力はできませんのでご注意ください。
CSVやzipファイルをダウンロードする場合、既定では「アプリケーションで開くかダウンロードして保存するか」を確認するポップアップボックスが表示されます。このポップアップボックスを表示されてしまうと以降の自動化ができないため、Firefoxのプロフィール (URLバーにabout:configと入力して確認および変更ができる、Firefoxの挙動の設定群) を設定して指定のディレクトリに確認なしで保存するようにします。
profile = Selenium::WebDriver::Firefox::Profile.new
# ファイルをダウンロードする既定のフォルダ
# - 0: デスクトップ
# - 1: システム既定のダウンロードフォルダ
# - 2: ユーザ定義フォルダ (browser.download.dir で指定)
profile['browser.download.folderList'] = 2
# ダウンロード先を指定 (バックスラッシュが二つ必要。環境によっては1つだけでいいかも)
profile['browser.download.dir'] = 'C:\Users\YourName\Desktop'
# ファイルをポップアップボックスなしで自動的に保存 (CSVファイルの例)
profile['browser.download.useDownloadDir'] = true
profile['browser.helperApps.neverAsk.saveToDisk'] = "text/plain,
application/vnd.ms-excel,text/csv,text/comma-separated-values,application/octet-stream"
# ドライバーを生成
driver = Selenium::WebDriver.for :firefox, :profile => profile
こちらのページを参考にしました。
element = driver.find_element(:name => 'source')
target = driver.find_element(:name => 'target')
driver.action.drag_and_drop(element, target).perform