MAGAZINE
ルーターマガジン
chrome_remoteという選択(脱Selenium大作戦)
結論だけを読みたい人は、「chrome_remoteサンプルソース」を先に見てください。
Webアプリケーションの操作自動化の検討順
Webアプリケーションの操作自動化は以下の順に検討します。
- HTTP GET(POST)だけで操作できるか
→ rubyならopen-uri、phpならfile_get_contents などのファイルを開く機能だけでできる - ログインCookieなどがあればアクセスできるか
→ Mechanizeなどのライブラリを使ってアクセスするという方法 - JSまで実行しないと再現できないか
→ 要は普通のブラウザと同様の動きをするなら、ブラウザ自動実行をする必要があり、一番有名なライブラリがSelenium
簡単にできることは簡単に済ませたいので、ファイルオープンする機能の延長で定期的にHTMLを監視できればそれで実装します。また、Seleniumはプログラミング言語で完結するのではなく、裏でブラウザを起動するためメモリも多く消費しますし、ブラウザのバージョンとの組合わせでも動作が変わってくるため、システムが複雑になります。
Seleniumの良さとツラミ
Seleniumにはライブラリの他に「Webドライバ」という単体のアプリケーションが必要です。各プログラミング言語は、driverに対して通信をし、driverがブラウザを操作します。
chromeを操作するdriverやFirefoxを操作するdriverなど個別に提供されており、プログラミング言語は、ブラウザ個別の仕様を気にすることなく、同じソースで、複数のブラウザのテストなどが可能になります。
しかしながら「ChromeやFirefoxの違いを吸収してくれる!」ってのは、driverが頑張ってるからであって、driverの頑張りにも限界があります。
Chromeのバージョンがあがるごとに、driverのバージョンもあげないとダメです。普段使いの開発マシンのChromeなんて自動的にあがっちゃいますね。これ辛いです。
また、「クロスブラウザのテストをしたい」という時以外はchromeとFirefoxが同じソースで自走操縦可能という価値は過剰です。単にWebアプリケーションを自動化したいなら、Chrome専用ソースでも十分です。
chrome_remote とは
この記事で紹介するのが、 chrome_remote というgemライブラリです。 chrome_remote は chromeをdebbuging-port経由で操作します。 Pythonなど他の言語でもchrome-remote-interface-pythonなどのライブラリ名で存在します。
gem install chrome_remote
Gemfileでインストールしてもいいのですが、普段遣いのライブラリとして、私は標準のrubyにインストールしています。依存関係も少なくてシンプルです。
selenium driverなどの中間層がないため、chromeは予め起動しておく必要があります。
Chrome DevTools Protocolとは
Chrome DevTools Protocol とは、外部からchromeをdebbuging-port経由で操作するためのプロトコルです。 https://chromedevtools.github.io/devtools-protocol/ に、そのレファレンスがあります。逆に言えばここに書かれてる機能しかありません。
Seleniumのようなdom操作の機能もありません。
殆どの操作は、ブラウザ上で動くJSを送ることで実現します。ちょうどDebeloperToolのコンソールでJSを実行するような感覚です。
まずはChromeをdebugging-portつきで起動します
chromeのremote-debbugging-portオプションは、Linux、Mac、Windowsのいずれにも備わっています。
Macの例
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
Linux/Windows(WSL2)の例
google-chrome --remote-debugging-port=9222
chrome_remoteは、Selenumのようにブラウザ起動や終了には関知しません。プロセスの親子関係もありません。
debugging-port つきで起動したら起動しているかどうかを確認します。
http://127.0.0.1:9222/json
にブラウザでアクセス、もしくはcurlでアクセスすると、JSONが帰ってきます。各タブごとにどういうWebScoketエンドポイントでアクセスすればいいかを示してくれます。
よくあるミスが、すでにchromeがdebugging-portオプションなしで起動してるにも関わらず、debugging-portつきで起動しようとして、オプションなしのchromeでもう1タブ開いているだけというのがあります。
またサーバー上で実行する際には、すでにdebugging-portで起動していて同じポートで起動しようとして失敗するケースもあります。
ポートをリッスンしているかどうかは見えにくいため、curlで9222でアクセスするステップを一度はさみましょう。
サンプルソース
おまたせしました。ミニマムのサンプルソースです。9222のポートをlistenしているchromeを起動して以下のrubyスクリプトを起動すると動作が確認できます。
require 'chrome_remote'
# ブラウザのロードが終わってるかどうかをJSを投げることで確認
def wait_for_complete
loop do
sleep(1)
response = @chrome.send_cmd 'Runtime.evaluate', expression: 'document.readyState;'
break if response['result']['value'] == 'complete'
end
end
# 以下にメインロジックを書いていく(基本的にはJSを投げまくればだいたいの操作はできる)
@chrome = ChromeRemote.client
js = "window.location = 'https://www.youtube.com/watch?v=4J6YxGAAido'" # ページを移動
@chrome.send_cmd 'Runtime.evaluate', expression: js
wait_for_complete # ロードが終わるまで待つ
js ="document.querySelector('.view-count').innerText" # 再生回数のCSSセレクタ
response = @chrome.send_cmd 'Runtime.evaluate', expression: js
p response #=> {"result"=>{"type"=>"string", "value"=>"2,637,941 回視聴"}}
また、実務上ではJSからはHTMLを返してもらうだけで、詳細のロジックはRuby+Nokogiriで扱うことが多いです。どうせRDBにはデータを入れるので、ActiveRecordeを使ったロジックとNokogiriを使ったロジックを近くに置くことで、HTMLとRDBというデータ構造の対応をわかりやすくします。
以下が実務上の雛形となります。
require 'chrome_remote'
require 'nokogiri'
# ブラウザのロードが終わってるかどうかをJSを投げることで確認
def wait_for_complete
loop do
sleep(1)
response = @chrome.send_cmd 'Runtime.evaluate', expression: 'document.readyState;'
break if response['result']['value'] == 'complete'
end
end
@chrome = ChromeRemote.client
# ページ移動
@chrome.send_cmd 'Page.navigate', url: 'https://www.youtube.com/watch?v=4J6YxGAAido'
wait_for_complete # ロードが終わるまで待つ
# JSを送ることでHTMLを取得(他にもClickする等の操作もJSONで可能)
response = @chrome.send_cmd 'Runtime.evaluate', expression: 'document.documentElement.outerHTML'
html = response['result']['value']
doc = Nokogiri::HTML.parse(html)
puts doc.at_css('.view-count').text
<h2">補足
2020年の本記事公開当初はWSL1前提での記述でしたが、WSL2がほぼ標準になりWSL2上でChromeも動作するようになったため、WindowsとLinuxは同じ扱いにするように修正しました(2022-09-01)。Windowsで実行する方は予めWSL2上でChromeを起動してください。
CONTACT
お問い合わせ・ご依頼はこちらから