Seleniumで、Chromeをヘッドレスで使用する場合、9つの注意(ツボ)と、私が設定したオプション設定を含めたSeleniumのソースコードを紹介します。
Selenium 9つのツボ
- WebDriverは自動的にバージョンをあわせる
- UserAgentを指定する
- ダウンロードプレファレンスを設定する
- Waitをデフォルトでなく自分で設定する
- Nokogiriでパースする
- 認証があるサイトはリンクでたどる
- ログ表示を調整する
- untilで待っても、sleepを入れたほうが安心
- エラー時でも必ず「quit」する
WebDriverは自動的にバージョンをあわせる
ChromeのバージョンとWebDriverのバージョンが異なるとエラーになってしまいます。めんどうなので自動的にバージョンチェックして、異なっていたらWebDriverをダウンロードするようにします。
UserAgentを指定する
ヘッドレスの場合、UserAgentを設定しないとUserAgentにheadlessであることが記載されてしまいます。するとスクレイピングを防ぐため、その文字を検知して拒否され get できない場合があるので、なにかしら設定しておく必要があります。
ダウンロードプレファレンスを設定する
通常使用のChromeのダウンロード設定とSeleniumのダウンロード設定は別のため、クリック等でファイルをダウンロードしたい場合は、オプションに追加設定する必要があります。
Waitをデフォルトでなく自分で設定する
Seleniumでは、自分でタイムアウト処理は作れないため、デフォルトの設定ではなく自分でタイムアウトとポーリング間隔を設定しておくと便利です。 ちなみに、暗黙的な待機(implicit_wait)は、明示的な待機と混在はしないほうが良いとのことです。
Nokogiriでパースする
Seleniumでは、途中エレメントのHTMLを少しずつ絞っていくなどはできないので page_source ですべて取得したら、Nokogiriでパースするほうが楽です。
認証があるサイトはリンクでたどる
ユーザーIDとパスワードを入力するサイトは、例えログインした後でも、所望のサイトURLを直接 get はできません。セッションが異なるためと思われます。ログインしたサイト上で、リンクをクリックして所望のページにたどっていく必要があります。
ログ表示を調整する
ChromeのログとSeleniumのログは別です。大量に表示されてしまう場合は、ログレベルを調整します。デバッグするときはフルで表示したりもできます。
ちなみにChromeオプションで、 --disable-logging は効きませんでした。
untilで待っても、sleepを入れたほうが安心
wait.until で処理が通っても、その後確実性を求めるのであれば、sleepを少し入れておいたほうが安心です。
エラー時でも必ず「quit」する
Selenumは、途中でエラーになる場合や、デバッグ中強制終了することがあるので、 ensure などで必ず quit しましょう。
Seleniumのソースコード
Seleniumモジュール(Mylibとする)
require 'webdrivers'
require 'selenium-webdriver'
module Mylib
#***********************************************************
# wdRunDuring
# in:
# vv : visible[true/false] (default=false)
# dl_dir : Downloadフォルダー(default=Downloads)
# use:
# wd:webdrivers
# wt:wait
#***********************************************************
def wdRunDuring(vv: false, dl_dir: "#{ENV['USERPROFILE']}\\Downloads")
# Webdrivers.logger.level = :DEBUG # debugする場合
# ChromeとWebDriverのバージョンをあわせる
install_dir = (ENV["USERPROFILE"] + '/webdrivers').gsub!("\\","/")
Webdrivers.install_dir = install_dir
# WebDriver
begin
# Selenium::WebDriver.logger.level = :debug # debugする場合
# Chrome Options
opt = Selenium::WebDriver::Chrome::Options.new
opt.add_argument('--headless') if !vv
opt.add_argument("--user-agent=DUMMY") # UAにHeadlessと書かれるのを防ぐ
opt.add_argument("--log-level=3") # 余計なログを非表示(LOG_FATALにする)
opt.add_argument('--disable-gpu') # いらないと思うが。。
opt.add_argument("--blink-settings=imagesEnabled=false") # 画像を読み込まないで軽くする
opt.add_argument("--disable-extensions") # なくてもエクステンションは起動しない
opt.add_argument('--start-maximized') # 表などが切れるのを防ぐ
# Chrome Options for download
dl_pref = {
'prompt_for_download'=> false,
'default_directory'=> dl_dir.gsub!("/","\\"), # バックスラッシュで絶対Path
'directory_upgrade'=> true
}
opt.add_preference(:download, dl_pref)
# WebDriver Wait
wd = Selenium::WebDriver.for :chrome, options: opt
wt = Selenium::WebDriver::Wait.new(:timeout => 10, :interval => 1) # 秒
yield wd,wt
ensure
wd.quit
print "\nWebDriver Quit"
end
end
module_function :wdRunDuring
end
Selenium使用例
require 'Mylib'
require 'nokogiri'
Mylib.wdRunDuring(){ |wd,wt|
# ログイン例(下記サイトは架空)
wd.get "https://aaa/bbb.php"
wt.until{ wd.find_element(name: 'uname') }.send_keys "namae"
wt.until{ wd.find_element(name: 'pass') }.send_keys "pasuwa-do"
wt.until{ wd.find_element(css: 'th.login input') }.click
pp "login #{wd.title}"
sleep 3
# ダウンロード例
wt.until{ wd.find_element(link_text: '週間データ') }.click
pp "click #{wd.title}"
sleep 3
ee = wt.until{ wd.find_element(css: 'td.weekly_data a') }
ee.click
pp "download #{ee.attribute('title')}"
# リンク取得例(下記サイトは架空)
uu = "https://datalink.com"
wd.get uu
doc = Nokogiri::HTML(wd.page_source)
uuu=[]
doc.css("a").each{ |atag|
hr = atag[:href]
uuu << uu + hr if hr.include?("/gazou")
}
uuu.uniq! # 重複排除
pp "get link #{uuu.size}"
...後処理...
...後処理...
...後処理...
}
uuu << uu + hr if hr.include?("/gazou") は、すべてのリンクから必要な画像リンク(例えばURLに /gazou が含んでいるリンク)のみを選別し、ベースの親URLを追加して完全なリンクにして uuu 配列に格納する例です。(HTMLソース上のURLが相対パスだった場合)