[2017-06-22 追記] chromeバージョン59の正式リリースに伴い、記事の内容を全面刷新しました。
[2017-07-27 追記] selenium使用時にsend_keys
が使用できない問題を記載しておりましたが、Chromedriver2.31のリリースに伴い問題が解消しました!
はじめに注意
chromeはバージョン59より正式にヘッドレスモードを搭載しました。
それを試そうと「chrome headless」などで検索すると、古いバージョンのchromeをXvfbなどの仮想ディスプレイを通してLinux上で動かす記事が沢山でてきますが、これらはheadless chromeに関しての記事ではなく、普通のGUI chromeをどうにかしてcui上で動かそうという取り組みの記事です。
chromeのヘッドレスモードでは、PhantomJSと同じようにXvfbはありません。googleの中の人も公式に名言しています。もし参考にしている記事でXvfbという単語が出てきたら、少し古い情報かもしれませんのでご注意下さい。
環境
環境構築方法は後述しますが、先に本記事の実行環境を書いておきます。
# cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
# google-chrome-beta --version
Google Chrome 59.0.3071.36 beta
# chromedriver -v
ChromeDriver 2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5)
# ruby -v
ruby 2.2.5p319 (2016-04-26 revision 54774) [x86_64-linux]
# cat Gemfile.lock
GEM
remote: https://rubygems.org/
specs:
childprocess (0.7.0)
ffi (~> 1.0, >= 1.0.11)
ffi (1.9.18)
mini_portile2 (2.1.0)
nokogiri (1.7.2)
mini_portile2 (~> 2.1.0)
rubyzip (1.2.1)
selenium-webdriver (3.4.0)
childprocess (~> 0.5)
rubyzip (~> 1.0)
websocket (~> 1.0)
websocket (1.2.4)
PLATFORMS
ruby
DEPENDENCIES
nokogiri
selenium-webdriver
BUNDLED WITH
1.12.5
事前準備
必要なライブラリをインストール
$ sudo apt-get update
$ sudo apt-get install -y libappindicator1 fonts-liberation
ヘッドレスモードを使用できるchromeをインストール
$ curl -O https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
$ sudo dpkg -i google-chrome-stable_current_amd64.deb
コマンドラインからChromeをヘッドレスで実行する
遷移先ページのHTMLボディを標準出力する
$ google-chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/ > test.html
遷移先ページのスクリーンショットを撮影
$ google-chrome --headless --disable-gpu --screenshot https://www.chromestatus.com/
Seleniumを通してRubyからheadless chromeを動かす
chromedriverのインストール
chromedriverはChromeバージョン59をサポートしている2.30以降をインストールして下さい。
[2017-07-27追記]
Chromedriverは2.31以降をインストールしてください。2.30以前ですと、後述のseleniumでsend_keys
が使えない問題が発生していたのですが、2.31でこの問題が解消しています!
# curl -O https://chromedriver.storage.googleapis.com/2.29/chromedriver_linux64.zip
# unzip chromedriver_linux64.zip
# chmod +x chromedriver
# mv -f chromedriver /usr/local/share/chromedriver
# ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
# ln -s /usr/local/share/chromedriver /usr/bin/chromedriver
# chromedriver
Starting ChromeDriver 2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5) on port 9515
Only local connections are allowed.
RubyでChromeをヘッドレスモードで実行
サンプルソース
以下のサンプルソースでUbuntu上で正常にfacebookのトップページにアクセスできていることは確認できています。ただ、フォームの入力を行う際に後述のエラーが発生するという状態です。
source "https://rubygems.org"
# gem "rails"
gem "selenium-webdriver"
gem 'nokogiri'
require 'selenium-webdriver'
require 'nokogiri'
require 'pp'
ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36"
caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {binary: '/usr/bin/google-chrome-beta', args: ["--headless", "--disable-gpu", "--user-agent=#{ua}", "window-size=1280x800"]})
session = Selenium::WebDriver.for :chrome, desired_capabilities: caps
session.manage.timeouts.implicit_wait = 30
session.navigate.to "https://www.facebook.com/"
session.save_screenshot('page.png')
id = session.find_element(:name, 'email')
id.send_keys('[email protected]') # <= 現在はsend_keysでエラーが発生
session.quit
フォームの入力(send_keys)でエラーが出る
Macで試したような他の記事を読むと正常に実行できているようなのですが、LinuxなどCUI上で実行した場合は、フォームの入力の際にselenium-webdriver Gemが以下のようなエラーを吐きます。
同じ問題で困っている人もたくさんいるようです。原因がChromeDriver側にあるのかSelenium側にあるのか不明ですが、いずれにしてもまだ議論段階であるようです。
# bundle exec ruby test_headless_chrome.rb
/root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok': unknown error: an X display is required for keycode conversions, consider using Xvfb (Selenium::WebDriver::Error::UnknownError)
(Session info: headless chrome=59.0.3071.36)
(Driver info: chromedriver=2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5),platform=Linux 4.4.0-21-generic x86_64)
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/response.rb:32:in `initialize'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/http/common.rb:83:in `new'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/http/common.rb:83:in `create_response'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/http/default.rb:107:in `request'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/http/common.rb:61:in `call'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/bridge.rb:678:in `raw_execute'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/bridge.rb:656:in `execute'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/remote/bridge.rb:448:in `send_keys_to_element'
from /root/tmp/headless_chrome_with_selenium/.bundle/ruby/2.2.0/gems/selenium-webdriver-3.4.0/lib/selenium/webdriver/common/element.rb:156:in `send_keys'
from test_headless_chrome.rb:42:in `<main>'
フォーム入力のsend_keysメソッドの代替手段
send_keysを使用せずにフォームを入力するため、回避策としてselenium上で任意のjavascriptを実行するexecute_script
メソッドを使用します。javascriptが実行できれば、テキストボックスをcssセレクタで選択してsetAttribute
でフォーム入力を行うことができます。
海外でも現状この集団でフォーム入力の不具合を回避している模様です。
https://bugs.chromium.org/p/chromedriver/issues/detail?id=1772#hc15
require 'selenium-webdriver'
require 'nokogiri'
require 'pp'
ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36"
caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {binary: '/usr/bin/google-chrome', args: ["--headless", "--disable-gpu", "--user-agent=#{ua}", "window-size=1280x800"]})
session = Selenium::WebDriver.for :chrome, desired_capabilities: caps
session.manage.timeouts.implicit_wait = 30
### facebookトップページに遷移
session.navigate.to "https://www.facebook.com/"
### フォーム入力、クリックによるページ遷移
#session.find_element(:name, "email").send_keys('[email protected]') # <- send_keysメソッドが現状使用不可
# フォーム入力の代替手段
session.execute_script('document.getElementById("email").setAttribute("value", "[email protected]");')
session.execute_script('document.getElementById("pass").setAttribute("value", "testpass");')
session.find_element(css: '#u_0_q').click
session.save_screenshot('page.png')
session.quit