MAGAZINE

ルーターマガジン

クローリング/スクレイピング

Webブラウザ自動操縦ライブラリchrome_remoteの後継のFerrumの使い方

2024.01.25
Pocket

エンジニアのsassaです。 弊社ブログでは、過去に何度かWebブラウザの自動操縦ライブラリとして「chrome_remote」を取り上げてきました。 chrome_remoteについて知らない方は、自動操縦ライブラリとしてよく名前を挙げられるSeleniumとの違いを過去の記事で紹介しているので、chrome_remoteという選択(脱Selenium大作戦)を見てみてください。

実は、chrome_remoteは2019年からメンテナンスされておらず、READMEを見ると「もうメンテナンスしてないから、後継のFerrumを見てね」と書かれてます。

This library is not actively maintained. Check https://github.com/rubycdp/ferrum for a replacement.

ということで、「Ferrum」を使ってみました。今回は、「chrome_remote」と「Ferrum」の違いを紹介していきます。

chrome_remoteとは

「chrome_remote」は、Ruby言語で開発されたWebブラウザ自動化ライブラリです。Chrome DevTools Protocol(CDP)を利用して、Chromium系ブラウザの操作を自動化することができます。Webアプリケーションの自動テストやスクレイピングに使用されることがあります。 注意点として、chrome_remoteを使用するためにはあらかじめdebugging-portつきでブラウザを開いておく必要があります。

▼Windowsでのchrome起動コマンド例

C:/Program Files/Google/Chrome/Application/chrome.exe --remote-debugging-port=9222

▼Macでのchrome起動コマンド例

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

▼Linuxでのchrome起動コマンド例

google-chrome --remote-debugging-port=9222

Ferrumとは

「Ferrum」は、chrome_remoteと同様にRuby上でCDPを利用して、Chromium系ブラウザの操作を自動化できます。対応しているブラウザは、2024年1月時点でChromeFirefoxのみになります。

Ferrumの使い方

まず、GemfileにFerrumを追記して、bundle installします。

gem "ferrum"

次に、Browserクラスのインスタンスを生成します。このインスタンス生成時に、Ferrum側でブラウザを立ち上げてくれます。 デフォルトではヘッドレスモードなので、今回は動作確認しやすいようにheadless: falseでヘッドフルモードにしています。

▼Chromeの立ち上げから指定のURLに遷移するまでのサンプルコード

require 'ferrum'
browser = Ferrum::Browser.new(headless: false, port: 9222)
browser.go_to('https://rooter.jp/')

chrome_remoteとFerrumの主な違い

OSに依存せずにブラウザ起動してくれる

「chrome_remote」では、ブラウザ起動機能はライブラリ内に組み込まれていないため、あらかじめ操作するブラウザをOS側から起動する必要があります。 ただし、Chromeの起動コマンドがOS毎に異なるため、環境による分岐を別途用意する必要がありました。 これまでは、WindowsならWSLでLinuxに寄せて、MacならLinux起動コマンドでchromeを起動するようシェルコマンドをラッピングして、対応してました。

「Ferrum」では、Browserクラスのインスタンス生成時に、よしなにOS毎の起動コマンドでブラウザを起動してくれます。 ちなみに、中身を覗くとOSごとにChrome起動コマンドのバイナリパスをハードコードして分岐してるだけでした。 ▼ ferrum/lib/ferrum/browser/options/chrome.rb

MAC_BIN_PATH = [
    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
    "/Applications/Chromium.app/Contents/MacOS/Chromium"
].freeze
LINUX_BIN_PATH = %w[chrome google-chrome google-chrome-stable google-chrome-beta
chromium chromium-browser google-chrome-unstable].freeze
WINDOWS_BIN_PATH = [
    "C:/Program Files/Google/Chrome/Application/chrome.exe",
    "C:/Program Files/Google/Chrome Dev/Application/chrome.exe"
].freeze
PLATFORM_PATH = {
mac: MAC_BIN_PATH,
         windows: WINDOWS_BIN_PATH,
         linux: LINUX_BIN_PATH
}.freeze

キャッシュを残さない

「chrome_remote」では、Chromeの起動を繰り返していると、キャッシュが/tmp/.com.google.Chrome.*に溜まり続けて行きます。なので、Chromeの起動オプションで、ユーザーデータディレクトリを指定し、定期的にキャッシュを削除する処理を書くなどして対応が必要になります。過去に長期運用をしていて、Chromeのキャッシュだけでディスクを圧迫するなんてこともありました。

一方、「Ferrum」では、デフォルトで、ユーザーデータディレクトリを一時ファイルに指定してブラウザを起動してくれているため、キャッシュが溜まり続けることはありません。ただし、制約として、こちら側からユーザーデータディレクトリを指定することができないです。おそらく、本来の用途としては自動テストを想定しているため、このような設計になっているのだと推測してます。スクレイピング観点では、ログイン情報を維持したいケースもあるので、ユーザーデータディレクトリを指定できない点は一長一短とも言えます。

ferrum/lib/ferrum/browser/process.rb

tmpdir = Dir.mktmpdir("ferrum_user_data_dir_")
ObjectSpace.define_finalizer(self, self.class.directory_remover(tmpdir))
@user_data_dir = tmpdir
@command = Command.build(options, tmpdir)

いちいちJavaScriptやCDPを書かなくて良い

「chrome_remote」では、CDPをRubyで直接呼び出し利用するため、CDPのリファレンスを参照したり、頻繁に使用されるDOM操作用のJavaScriptを記述する必要があります。

一方で、「Ferrum」では、ページ遷移やJS実行などをあらかじめラッピングしてれており、DOM操作などはNokogiriに似た書き方で行えるため、操作がより簡単で直感的です。

ページ遷移やHTML取得などWebスクレイピングでよく使う操作の「chrome_remote」 → 「Ferrum」の書き換え例にまとめました。

Ferrumの書き換え集

▼「chrome_remote」の場合

require 'chrome_remote'

## chromeブラウザを起動
system("google-chrome --disable-gpu --remote-debugging-port=9222 --user-data-dir=./tmp_Chrome_config --no-sandbox &")
chrome = ChromeRemote.client
chrome.send_cmd("Page.enable")
## ページ遷移
chrome.send_cmd 'Page.navigate', url: 'https://rooter.jp/'  
## HTML取得
html = chrome.send_cmd('Runtime.evaluate', expression: "document.getElementsByTagName('html')[0].innerHTML", returnByValue: true)
## クリック
chrome.send_cmd('Runtime.evaluate', expression: "document.querySelector("クリックしたいCSSセレクタ").click()")

## Network内の特定のリクエスト取得
chrome.send_cmd("Network.enable")
chrome.on("Network.responseReceived") do |params|
  if params["response"]["url"].include?("rooter")
    response_body = chrome.send_cmd("Network.getResponseBody", requestId: params["requestId"])
    p response_body
  end
end
sleep 1
chrome.send_cmd("Page.navigate", url: "https://rooter.jp/")

▼「Ferrum」の場合

require 'ferrum'

## chromeブラウザを起動
browser = Ferrum::Browser.new(headless: false, port: 9222)
page = browser.create_page
page.go_to('https://rooter.jp/') ## ページ遷移
html = page.body ## HTML取得
page.at_css("クリックしたいCSSセレクタ").click() ## クリック

## Network内の特定のリクエスト取得
page = browser.create_page
page.go_to('https://rooter.jp/')
page.network.traffic.find{ |exchange| exchange.url.include?('rooter') }.response.body

最後に

今回は、ブラウザ自動操作ライブラリである「chrome_remote」の後継にあたる「Ferrum」の使い方を紹介しました。

Ferrumは、OSに依存せずにブラウザを起動し、簡潔なコードでWebスクレイピングや自動テストを行うことができます。しかしながら、ブラウザの設定に関する自由度はやや限定されます。そのため、より詳細な設定が求められる場合には、chrome_remoteを利用すると良いでしょう。

ぜひFerrumを使って、快適なスクレイピングライフを送ってみてください。

Pocket

CONTACT

お問い合わせ・ご依頼はこちらから