いけむランド

はてダからやってきました

serverless-chrome で日本語を表示できるようにする

本家で issue には挙がってますが、まだ対応しているようではなさそうです。(2017/09/10 現在)

github.com

準備

serverless-chrome を手元に持ってきます。

$ cd /path/to/workdir
$ git clone https://github.com/adieuadieu/serverless-chrome.git

そして、含まれている chrome/chrome-headless-lambda-linux-x64.tar.gz を展開します。今回は必要なものをすべてここに追加して、再アーカイブする方針です。(今回は作業ディレクトリとして /tmp を使用するものとします。)

$ cd /tmp
$ tar xf /path/to/workdir/chrome/chrome-headless-lambda-linux-x64.tar.gz
$ ls headless-chrome
headless_shell libosmesa.so

fontconfig

以前 lambda で phantomjs を使う場合に同じように日本語が表示できないという問題があったようで、その時の対策が fontconfig を同梱して、それで日本語を表示させるというものでした。

www.rco.recruit.co.jp

どうやら今回もこの手法が使えそうなので真似てみます。

fontconfig を lambda で動かすためには amazon linux 向けにビルドする必要があります。そこで今回は docker-lambda を使ってみることにしました。

http://the.nyarla.net/entry/2017/02/06/142900the.nyarla.net

github.com

$ docker run -i -t -v /tmp:/var/task lambci/lambda:build /bin/bash

ビルドに必要なツールをインストールします。(以降 bash-4.2# というプロンプトは docker 内で実行するコマンドです。)

bash-4.2# yum install gperf freetype-devel libxml2-devel
bash-4.2# easy_install pip
bash-4.2# pip install lxml
bash-4.2# cd /tmp

fontconfig を取得して、ビルドし、headless-chrome 配下にインストールします。

bash-4.2# git clone http://anongit.freedesktop.org/git/fontconfig
bash-4.2# cd fontconfig
bash-4.2# git checkout -b 2.12.4 refs/tags/2.12.4
bash-4.2# ./autogen.sh --sysconfdir=/var/task/headless-chrome/fontconfig/etc --prefix=/var/task/headless-chrome/fontconfig/usr --mandir=/var/task/headless-chrome/fontconfig/usr/share/man --enable-libxml2
bash-4.2# make
bash-4.2# make install

インストール状況を確認します。(docker からは一旦 exit)

$ cd /tmp/headless-chrome/fontconfig
$ ls
etc usr

docker で install したため usr/share/fontconfig/conf.avail 配下の symlink が外からは切れて見えます。
リンク切れは serverless で deploy する時に error になってしまうため、それを回避するために、ここではとりあえずコピーして実体を持ってきておきます。(もっとスマートな方法もありそうですが未検討。)

$ cd /tmp/headless-chrome/fontconfig/usr/share/fontconfig/conf.avail
$ rm ./*
$ cp /tmp/headless-chrome/fontconfig/etc/fonts/conf.d/* .

日本語フォントを置くためのディレクトリの設定もしておきます。

$ cd /tmp/headless-chrome/fontconfig/usr/share/fontconfig
$ vi local.conf

以下のファイルを local.conf として置いておきます。

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>/var/task/headless-chrome/fontconfig/usr/share/fonts</dir>
</fontconfig>

日本語フォント

Google Noto Fonts を使用しようと考えていたのですが、サイズが大きそうなので、IPA フォントを使用することにしました。

$ cd /tmp
$ wget http://dl.ipafont.ipa.go.jp/IPAexfont/ipaexg00301.zip
$ unzip ipaexg00301.zip
$ cp ipaexg00301/* /tmp/headless-chrome/fontconfig/usr/share/fonts/

フォントをインストールしたら fontconfig が参照できるようにキャッシュを生成しておきます。fc-cache を docker から叩いておきます。

bash-4.2# /var/task/headless-chrome/fontconfig/usr/bin/fc-cache -sv

strip

これで必要なものは準備できたのですが、このまま再パッケージすると 50MB 制限を超えてしまいます。そこで chrome のバイナリを strip すると、今回の追加分を込みで 50MB に収まるようになります。

bash-4.2# strip /var/task/headless-chrome/headless_shell

再アーカイブ

これで必要なものは準備できたため、再アーカイブします。

$ cd /tmp
$ tar cfvz chrome-headless-lambda-linux-x64.tar.gz headless-chrome

デプロイ

いよいよデプロイです。先程つくった chrome のアーカイブを chrome 配下に置きます。

$ cd /path/to/workdir/serverless-chrome
$ cp /tmp/chrome-headless-lambda-linux-x64.tar.gz chrome

上書きが嫌な場合は別の名前にして、webpack.config.js で指定しているアーカイブ名を別のものにしておきましょう。

const chromeTarball = path.join(__dirname, 'chrome/chrome-headless-lambda-linux-x64.tar.gz')

同梱した fontconfig をロードさせるために環境変数 LD_LIBRARY_PATH も設定しておく必要があります。

  environment:
    CHROME_PATH: ./headless-chrome/headless_shell
    LD_LIBRARY_PATH: /var/task/headless-chrome/fontconfig/usr/lib

あとは serverless-chrome の readme どおりにすれば ok です。

$ yarn deploy

デプロイしたらブラウザでアクセスします。クエリパラメータ url でスクリーンショットを撮りたいサイトを指定します。

撮られたイメージが以下になります。

使用感

とりあえずメジャーなサイトのスクリーンショットを適当に撮ってみたのですが、

  • やっぱりデータ量の多いサイトは (デフォルトのタイムアウト時間である) 15sec 制限にかかってしまう。(これは延長して回避できる。)
  • IPA フォントなので日本語しかいけないのかと思ってたが、韓国語や中国語も読めそう。(ただし一部豆腐あり)

みたいな感じでした。

ローカルで動かす

docker-lambda を使うことで (スマートではないですが) ローカルでも動かすことは可能です。

$ cd /path/to/workdir/serverless-chrome/.serverless
$ unzip serverless-chrome.zip
$ cp handler.js /tmp
$ docker run -i -t -e "CHROME_PATH=./headless-chrome/headless_shell" -e "LD_LIBRARY_PATH=/var/task/headless-chrome/fontconfig/usr/lib" -v /tmp:/var/task lambci/lambda:nodejs6.10 handler.run '{"queryStringParameters": {"url":"http://www.hatena.ne.jp/"}}'

標準出力に html がドバっと出るので、適当に切り出せば読めるはずです。