Dockerで日本語対応のHeadless Chrome + puppeteerを立ち上げ


docker-composeで日本語対応のHeadless Chromeを「puppeteer」で操作する手順です。

😎 Dockerfile

日本語対応のChromeの入ったDockerfileは次のように記述します。

FROM ubuntu:latest
MAINTAINER morizyun <@zyunnosuke>
# For Japan
RUN sed -i -E "s@http://(archive|security)\.ubuntu\.com/ubuntu/@http://ftp.jaist.ac.jp/pub/Linux/ubuntu/@g" /etc/apt/sources.list
# Basic
RUN apt-get update \
&& apt-get install -y sudo curl wget zip unzip git nodejs npm fontconfig \
&& apt-get purge -y nodejs npm \
&& curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - \
&& apt-get install -y nodejs \
&& npm install -g yarn
# Chrome
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - \
&& sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' \
&& apt-get update \
&& apt-get install -y google-chrome-stable
# Remove cache & workfile
RUN rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# Japanese font
RUN mkdir /noto
ADD https://noto-website.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip /noto
WORKDIR /noto
RUN unzip NotoSansCJKjp-hinted.zip && \
mkdir -p /usr/share/fonts/noto && \
cp *.otf /usr/share/fonts/noto && \
chmod 644 -R /usr/share/fonts/noto/ && \
/usr/bin/fc-cache -fv
WORKDIR /
RUN rm -rf /noto
# Work dir
RUN mkdir -p /app
WORKDIR /app
COPY . /app/

🐞 docker-compose.yml

docker-compose.ymlは次のように記述します。

version: '3'
services:
app:
build: .
working_dir: "/app"
tty: true
stdin_open: true
volumes:
- .:/app
shm_size: 512 # 一時ファイル領域(MB)

あとはdocker-compose buildでイメージを作成します。

🤔 puppeteer

puppeteerをインストールします。

yarn init
yarn add puppeteer

package.jsonに以下を追加します。

"scripts": {
"puppeteer": "node puppeteer.js"
}

ブラウザを操作するpuppeteer.jsはこちら。

const puppeteer = require('puppeteer');
const cheerio = require('cheerio');
(async() => {
const browser = await puppeteer.launch({args: ['--no-sandbox']});
const page = await browser.newPage();
await page.setRequestInterceptionEnabled(true);
await page.goto('https://google.com', {waitUntil: 'networkidle'});
// Type our query into the search bar
await page.type('puppeteer');
await page.click('input[type="submit"]');
// Wait for the results to show up
await page.waitForNavigation();
await page.screenshot({path: `puppeteer_${(new Date()).getTime()}.png`});
browser.close();
})();

作成した結果を試してみます。

# bashを起動
docker-compose run --rm --entrypoint /bin/bash app
yarn run puppeteer

これでブラウザの操作結果のスクリーンショットを取得できます。

🐠 Dockerfile

GitHubとDocker Hubにファイルを公開しました。

🐯 補足:puppeteerの使い方

タイムアウトを長くする

タイムアウトを長くしたい場合は、page.gotoのオプションでtimeoutを指定すると良さそうです。

await page.goto('https://example.com/', { timeout: 3000000 });

スクリーンサイズの変更

スクリーンのサイズは次の設定で指定できます。できるだけメモリ消費を抑えるために小さめで設定した方が良さそうです。

page.setViewport({ width: 320, height: 640 });

Deviceのエミュレート

スマートフォンやタブレットのブラウザをエミュレートすることも比較的容易です。次のように記述します。

const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6'];
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.emulate(iPhone);
await page.goto('https://www.google.com');
// other actions...
await browser.close();
});

Page crash対策

Dockerがデフォルトで準備する一時ファイル領域は64MBで、一般的な装飾のサイトではこの容量を超えてしまい、Page crashすることがありました。docker runコマンドの場合はオプション--shm-size=256mdocker-composeファイルの場合はshm_size: 256を指定すると良さそうです。

Chromeエラーのダンプ

Chromeのアウトプットを標準出力煮出す場合はこちら。

const browser = await Puppeteer.launch({ dumpio: true });

🗽 参考リンク

🏈 その他ツール

📚 おすすめの書籍

🖥 サーバについて

このブログでは「Cloud Garage」さんのDev Assist Program(開発者向けインスタンス無償提供制度)でお借りしたサーバで技術検証しています。 Dev Assist Programは、開発者や開発コミュニティ、スタートアップ企業の方が1GBメモリのインスタンス3台を1年間無料で借りれる心強い制度です!(有償でも1,480円/月と格安)