Docker ( AmazonLinux2 ) 上で Perl から Headless Chrome を操作する
Perl で Headless Chrome を操作する日本語記事がほぼないし、しかもそれを Docker 内で動かすという例がなかったので書いておくことにする。
全部のファイルと、動かし方は gist にまとめておいた。
https://gist.github.com/takaya1992/6fc6878fb936559344fac068ab6e90f2
抜粋して一部を書いておく。
まずは、Dockerfile
FROM amazonlinux:2 WORKDIR /app RUN yum update -y \ && yum install -y perl perl-core perl-App-cpanminus gcc expat-devel \ && rm -rf /var/cache/yum/* \ && yum clean all \ && cpanm Carton COPY google-chrome.repo /etc/yum.repos.d/google-chrome.repo RUN yum install -y google-chrome-stable unzip wget lsof ipa-gothic-fonts ipa-mincho-fonts RUN CHROME_MAJOR_VERSION=$(google-chrome --version | sed -E "s/.* ([0-9]+)(\.[0-9]+){3}.*/\1/") \ && CHROME_DRIVER_VERSION=$(wget --no-verbose -O - "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_${CHROME_MAJOR_VERSION}") \ && echo "Using chromedriver version: "$CHROME_DRIVER_VERSION \ && wget --no-verbose -O /tmp/chromedriver_linux64.zip https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip \ && unzip /tmp/chromedriver_linux64.zip chromedriver -d /usr/local/bin/
先に断っておくと、サンプルとしてわかりやすく書いているので、Dockerfile を書く上でのベストプラクティスは守っていない。 前半は、Perl とそれに必要なパッケージのインストールなのでとくに説明はしない。
重要なのは後半の Chrome と ChromeDriver のインストール部分で、Chrome のインストールから説明していく。
Chrome のインストール
まずは、Chrome のリポジトリを追加するために設定ファイルをコピーしてくる。google-chrome.repo
の中身は以下のようになってる。
[google-chrome] name=google-chrome baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch enabled=1 gpgcheck=1 gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
これを /etc/yum.repo.d/
以下に置くことで、yum のパッケージ検索対象になる。
Chrome は google-chrome-stable
というパッケージ名で登録されている。
残りの Chrome と一緒にインストールしてるパッケージは、次の ChromeDriver のインストールに必要なもの ( unzip
, wget
)と、Chrome を起動するのに必要なもの ( lsof
) 、Chrome で日本語を表示する際に必要なフォント ( ipa-gothic-fonts
, ipa-mincho-fonts
) 。
ChromeDriver のインストール
Chrome を外から操作するために ChromeDriver を使う。
ChromeDriver はブラウザを操作するための WebDriver という仕様に基づいて実装されており、ChromeDriver を起動すると HTTP サーバーが立ち上がり JSON API でブラウザを操作できる。
他のブラウザ、例えば Firefox では geckodriver というドライバーが用意されている。
この WebDriver を使って各ブラウザを操作できるのが Selenium である。
話がそれたけど、ChromeDriver をインストールする。 ChromeDriver は Chrome のバージョンに合ったバージョンをインストールする必要がある。 そのため、 Chrome のバージョンを確認しそのバージョンに対応する ChromeDriver のバージョンを取得し、ダウンロードしている。
Perl から Headless な Chrome を操作する
直接 Docker を実行するのも面倒なので、docker-compose.yml
を用意して Docker Compose で操作できるようにしてある。
$ docker-compose build
でビルドして、
$ docker-compose run --rm app /bin/bash
で Docker コンテナ内に入る。
cpanfile
を用意しているので Docker コンテナ内で以下のコマンドを実行して必要なパッケージ ( Selenium::Remote::Driver
) をインストールしておく。
$ carton install
以下のスクリプトを carton exec -- perl selenium_chrome_test.pl
で実行すると、 Google
と表示されれば成功。
use strict; use warnings; use utf8; use feature qw/say/; use Selenium::Chrome; # Selenium を介さず直接 chromedriver 経由で Chrome を操作する my $driver = Selenium::Chrome->new( extra_capabilities => { 'goog:chromeOptions' => { args => [ 'headless', 'disable-gpu', 'window-size=1920,1080', 'no-sandbox' ], } } ); $driver->get('https://www.google.com'); say $driver->get_title(); # => Google $driver->shutdown_binary();
Headless な Chrome として実行するポイントは、Selenium::Chrome->new
時のオプションで headless
を指定すること。
順番に説明していく。
まず、インストールした Selenium::Remote::Driver
にはいくつかのパッケージが含まれていて、 Selenium::Chrome
もその一つ。
Selenium::Chrome
は Selenium を介さずに前述した ChromeDriver に直接アクセスし、API をコールして Chrome の操作を行う。
Selenium::Chrome
に渡した goog:chromeOptions
は、ChromeDriver へセッションを作成する際に渡され、 goog:chromeOptions
内の args
配列は Chrome 起動時の引数として設定される。
headless
を指定するとヘッドレスな Chrome として起動される。
disable-gpu
は headless
指定時に追加で指定することが推奨されるオプションである。
window-size
はその名の通りウィンドウのサイズを指定している。これは必須ではない。
最後に no-sandbox
は Chrome のサンドボックスを無効化するオプション。詳しく調べられていないけど、これをつけないと実行できなかった。セキュリティはゆるくなるので信用できないサイトを開かないように注意する。