dockerコンテナ上で puppeteerを動かす


docker上でpuppeteerを動かすにあたって、なんだかすごく時間がかかった。

前提

docker+Express環境を立ち上げ済み

やること

dockerfileを適切に記述して、あとは普通に立ち上げるだけです。
ゾンビプロセスのせいで凄まじくゴミが溜まるので、それ用にdump-initも入れています。

Dockerfile

FROM node:8-slim

# See https://crbug.com/795759
RUN apt-get update && apt-get install -yq libgconf-2-4

# chromeをダウンロード
RUN apt-get update && apt-get install -y wget --no-install-recommends \
    && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
    && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
    && apt-get update \
    && apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
      --no-install-recommends \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get purge --auto-remove -y curl \
    && rm -rf /src/*.deb

# ゾンビプロセス対策に必要
ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init
RUN chmod +x /usr/local/bin/dumb-init

RUN npm i puppeteer

# Add user so we don't need --no-sandbox.
RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
    && mkdir -p /home/pptruser/Downloads \
    && chown -R pptruser:pptruser /home/pptruser \
    && chown -R pptruser:pptruser /node_modules

# Run everything after as non-privileged user.
USER pptruser

ENTRYPOINT ["dumb-init", "--"]
CMD ["google-chrome-unstable"]

# 環境変数設定
ENV NODE_ENV="development"

# 作業ディレクトリ作成&設定
WORKDIR /src

puppeteer起動

main.js
    const browser = await puppeteer.launch({
      args: [
        // Required for Docker version of Puppeteer
        '--no-sandbox',
        '--disable-setuid-sandbox',
        // This will write shared memory files into /tmp instead of /dev/shm,
        // because Docker’s default for /dev/shm is 64MB
        '--disable-dev-shm-usage',
      ],
      executablePath: 'google-chrome-unstable',
    })

    const page = await browser.newPage()

以上。

(余談)$$evalの注意点

page.$$eval('.hogeclass', elm =>{...})

余談ですが、$$eval内の関数はchrome上で実行されるもので、この中で事前に定義した変数とか使おうとするとエラる。
これが、util is not definedという謎なお返事を返してくれる。
参考:https://stackoverflow.com/questions/49429488/error-evaluation-failed-referenceerror-util-is-not-defined/49429927

失敗例

const lololo = "aaa"
page.$$eval('.hogeclass', elm =>{
 console.log(lololo) //ここでエラー
})

$$eval内は、dom取得意外に余計な処理を行わない

複数要素取得したい時は、取得後、なんかしらの処理を加えたいときが多いと思うので、

const elm = await page.$$('.hogeclass')

で要素を取得して、取得後にループ。

単一の要素の場合は、

let hogehoge = await page.$eval(.hogeclass, (item) => {
 return textContent
})

みたいな感じで。とにかくeval内でゴネゴネやらなければOk。