Puppeteer のトラブルシューティング

Windows でヘッドレス Chrome が起動しない

一部の Chrome ポリシーでは、特定の拡張機能で Chrome または Chromium の実行が強制されている場合があります。

Puppeteer はデフォルトで --disable-extensions フラグを渡すため、このようなポリシーが有効な場合、Puppeteer の起動に失敗します。

この問題を回避するには、フラグを指定せずに実行してみてください。

const browser = await puppeteer.launch({
  ignoreDefaultArgs: ['--disable-extensions'],
});

コンテキスト: 問題 3681

UNIX でヘッドレス Chrome が起動しない

必要な依存関係がすべてインストールされていることを確認します。Linux マシンで ldd chrome | grep not を実行すると、欠落している依存関係を確認できます。

Debian(Ubuntu)の依存関係

ca-certificates
fonts-liberation
libappindicator3-1
libasound2
libatk-bridge2.0-0
libatk1.0-0
libc6
libcairo2
libcups2
libdbus-1-3
libexpat1
libfontconfig1
libgbm1
libgcc1
libglib2.0-0
libgtk-3-0
libnspr4
libnss3
libpango-1.0-0
libpangocairo-1.0-0
libstdc++6
libx11-6
libx11-xcb1
libxcb1
libxcomposite1
libxcursor1
libxdamage1
libxext6
libxfixes3
libxi6
libxrandr2
libxrender1
libxss1
libxtst6
lsb-release
wget
xdg-utils

CentOS の依存関係

alsa-lib.x86_64
atk.x86_64
cups-libs.x86_64
gtk3.x86_64
ipa-gothic-fonts
libXcomposite.x86_64
libXcursor.x86_64
libXdamage.x86_64
libXext.x86_64
libXi.x86_64
libXrandr.x86_64
libXScrnSaver.x86_64
libXtst.x86_64
pango.x86_64
xorg-x11-fonts-100dpi
xorg-x11-fonts-75dpi
xorg-x11-fonts-cyrillic
xorg-x11-fonts-misc
xorg-x11-fonts-Type1
xorg-x11-utils

依存関係をインストールしたら、次のコマンドを使用して nss ライブラリを更新する必要があります

yum update nss -y

ディスカッションを確認:

  • #290 - Debian のトラブルシューティング
  • #391 - CentOS のトラブルシューティング
  • #379 - Alpine のトラブルシューティング

ヘッドレス Chrome で GPU 合成を無効にする

Chrome と Chromium でヘッドレス モードでの GPU アクセラレーションを有効にするには、--use-gl=egl が必要です。

const browser = await puppeteer.launch({
  headless: true,
  args: ['--use-gl=egl'],
});

Chrome がダウンロードされたが、Node.js で起動できない

Chromium を起動しようとしたときに次のようなエラーが表示された場合:

(node:15505) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!
spawn /Users/.../node_modules/puppeteer/.local-chromium/mac-756035/chrome-mac/Chromium.app/Contents/MacOS/Chromium ENOENT

これは、ブラウザはダウンロードされたものの、正しく抽出できなかったことを意味します。 最も一般的な原因は、Node.js v14.0.0 のバグで、ブラウザのダウンロードを適切な場所に抽出するために Puppeteer が使用するモジュール extract-zip が破損していることです。このバグは Node.js v14.1.0 で修正されているため、このバージョン以降を使用していることを確認してください。

Chrome Linux サンドボックスを設定する

ホスト環境を信頼できないウェブ コンテンツから保護するために、Chrome では複数のサンドボックス レイヤを使用しています。これを正常に機能させるには、最初にホストを構成する必要があります。Chrome に適したサンドボックスがない場合は、No usable sandbox! というエラーでクラッシュします。

Chrome で開くコンテンツを絶対に信頼できる場合は、--no-sandbox 引数を指定して Chrome を起動できます。

const browser = await puppeteer.launch({
  args: ['--no-sandbox', '--disable-setuid-sandbox'],
});

Chromium でサンドボックスを設定する方法は 2 つあります。

Sser 名前空間のクローン作成は最新のカーネルでのみサポートされています。非特権ユーザーの名前空間を有効にしても問題ありませんが、(サンドボックス化されていない)非 root プロセスがカーネル特権に昇格するカーネル攻撃対象領域が増える可能性があります。

sudo sysctl -w kernel.unprivileged_userns_clone=1

[代替] setuid サンドボックスを設定する

setuid サンドボックスはスタンドアロンの実行ファイルとして提供され、Puppeteer がダウンロードする Chromium の隣にあります。同じサンドボックスの実行可能ファイルを異なる Chromium バージョンで再利用しても問題ありません。それによって、ホスト環境ごとに 1 回だけ実行できます。

# cd to the downloaded instance
cd <project-dir-path>/node_modules/puppeteer/.local-chromium/linux-<revision>/chrome-linux/
sudo chown root:root chrome_sandbox
sudo chmod 4755 chrome_sandbox
# copy sandbox executable to a shared location
sudo cp -p chrome_sandbox /usr/local/sbin/chrome-devel-sandbox
# export CHROME_DEVEL_SANDBOX env variable
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox

デフォルトで CHROME_DEVEL_SANDBOX 環境変数をエクスポートすることもできます。この場合、次のコードを ~/.bashrc または .zshenv に追加します。

export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox

Travis CI で Puppeteer を実行する

v6.0.0 まで Travis CI で Puppeteer のテストを実行した後、GitHub Actions に移行しました。参考として、.travis.yml(v5.5.0)をご覧ください。

ベスト プラクティスをいくつか紹介します。

  • Chromium をヘッドレス以外のモードで実行するには、xvfb サービスを起動する必要があります
  • デフォルトで Travis 上の Xenial Linux で実行
  • デフォルトで npm install を実行する
  • node_modules はデフォルトでキャッシュに保存される

.travis.yml は次のようになります。

language: node_js
node_js: node
services: xvfb

script:
  - npm run test

CircleCI で Puppeteer を実行する

  1. 構成で NodeJS イメージから始めます。yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
  2. libXtst6 のような依存関係は、おそらく apt-get でインストールする必要があります。そのため、threetreeslight/puppeteer ORB を使用するか(instructions)、そのソースの一部を独自の構成に貼り付けます。
  3. 最後に、Jest を介して Puppeteer を使用している場合、子プロセスを生成するときにエラーが発生する可能性があります。 shell [00:00.0] jest args: --e2e --spec --max-workers=36 Error: spawn ENOMEM at ChildProcess.spawn (internal/child_process.js:394:11) これは、Jest がコンテナに許可されている数(2)ではなく、マシン全体のプロセス数(36)を自動検出したことが原因と考えられます。この問題を解決するには、テストコマンドで jest --maxWorkers=2 を設定します。

Docker で Puppeteer を実行する

Docker でヘッドレス Chrome を使い始めるのは簡単ではありません。Puppeteer がインストールするバンドルされた Chromium に、必要な共有ライブラリの依存関係がありません。

この問題を解決するには、Dockerfile に不足している依存関係と最新の Chromium パッケージをインストールする必要があります。

FROM node:14-slim

# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer
# installs, work.
RUN apt-get update \
    && apt-get install -y wget gnupg \
    && 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-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
      --no-install-recommends \
    && rm -rf /var/lib/apt/lists/*

# If running Docker >= 1.13.0 use docker run's --init arg to reap zombie processes, otherwise
# uncomment the following lines to have `dumb-init` as PID 1
# ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_x86_64 /usr/local/bin/dumb-init
# RUN chmod +x /usr/local/bin/dumb-init
# ENTRYPOINT ["dumb-init", "--"]

# Uncomment to skip the chromium download when installing puppeteer. If you do,
# you'll need to launch puppeteer with:
#     browser.launch({executablePath: 'google-chrome-stable'})
# ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true

# Install puppeteer so it's available in the container.
RUN npm init -y &&  \
    npm i puppeteer \
    # Add user so we don't need --no-sandbox.
    # same layer as npm install to keep re-chowned files from using up several hundred MBs more space
    && 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 \
    && chown -R pptruser:pptruser /package.json \
    && chown -R pptruser:pptruser /package-lock.json

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

CMD ["google-chrome-stable"]

コンテナを構築します。

docker build -t puppeteer-chrome-linux .

コマンドとして node -e "<yourscript.js content as a string>" を渡し、コンテナを実行します。

 docker run -i --init --rm --cap-add=SYS_ADMIN \
   --name puppeteer-chrome puppeteer-chrome-linux \
   node -e "`cat yourscript.js`"

App Engine Flex(Node)で実行されているウェブサーバーからこの Dockerfile を実行する方法については、https://github.com/ebidel/try-puppeteer の完全な例をご覧ください。

アルプスランニング

Alpine でサポートされている最新の Chromium パッケージは 100 です。これは、Puppeteer v13.5.0 に対応します。

Dockerfile の例:

FROM alpine

# Installs latest Chromium (100) package.
RUN apk add --no-cache \
      chromium \
      nss \
      freetype \
      harfbuzz \
      ca-certificates \
      ttf-freefont \
      nodejs \
      yarn

...

# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
    PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser

# Puppeteer v13.5.0 works with Chromium 100.
RUN yarn add puppeteer@13.5.0

# Add user so we don't need --no-sandbox.
RUN addgroup -S pptruser && adduser -S -G pptruser pptruser \
    && mkdir -p /home/pptruser/Downloads /app \
    && chown -R pptruser:pptruser /home/pptruser \
    && chown -R pptruser:pptruser /app

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

...

Docker のベスト プラクティス

デフォルトでは、Docker は /dev/shm 共有メモリ空間が 64 MB のコンテナを実行します。この値は Chrome には小さすぎるため、大きなページをレンダリングすると Chrome がクラッシュします。この問題を解決するには、docker run --shm-size=1gb でコンテナを実行して /dev/shm のサイズを増やします。Chrome 65 以降では、この作業は不要になりました。代わりに、--disable-dev-shm-usage フラグを使用してブラウザを起動します。

const browser = await puppeteer.launch({
  args: ['--disable-dev-shm-usage'],
});

これにより、共有メモリファイルが /dev/shm ではなく /tmp に書き込まれます。crbug.com/736452 を確認する。

Chrome の起動時に、他に不審なエラーは表示されますか?ローカルで開発する場合は、docker run --cap-add=SYS_ADMIN を使用してコンテナを実行してみてください。Dockerfile は非特権ユーザーとして pptr ユーザーを追加するため、必要な権限がすべて付与されていない可能性があります。

dumb-init は、Chrome プロセスが残っていて多数のゾンビを見つけているのであれば、チェックする価値があります。PID=1 を使用するプロセスには特別な処理があり、場合によっては(Docker などで)Chrome を適切に終了することが困難になります。

クラウドで Puppeteer を実行する

Google App Engine 上

App Engine スタンダード環境の Node.js ランタイムには、ヘッドレス Chrome の実行に必要なすべてのシステム パッケージが付属しています。

puppeteer を使用するには、モジュールを package.json の依存関係としてリストし、Google App Engine にデプロイします。App Engine で puppeteer を使用する方法について詳しくは、公式チュートリアルをご覧ください。

Google Cloud Functions の場合

Google Cloud Functions の Node.js 10 ランタイムには、ヘッドレス Chrome の実行に必要なすべてのシステム パッケージが付属しています。

puppeteer を使用するには、package.json でモジュールを依存関係としてリストし、nodejs10 ランタイムを使用して関数を Google Cloud Functions にデプロイします。

Google Cloud Run で Puppeteer を実行する

Google Cloud Run のデフォルトの Node.js ランタイムには、ヘッドレス Chrome の実行に必要なシステム パッケージが付属していません。独自の Dockerfile をセットアップし、欠落している依存関係を含めます

Heroku の場合

Heroku で Puppeteer を実行するには、Heroku がスピンアップする Linux ボックスに含まれていない依存関係を追加する必要があります。deploy の依存関係を追加するには、[Settings] > [Buildpacks] で、アプリの Buildpack リストに Puppeteer Heroku Buildpack を追加します。

Buildpack の URL は https://github.com/jontewks/puppeteer-heroku-buildpack です

Puppeteer を起動するときは、必ず '--no-sandbox' モードを使用してください。これを行うには、引数として .launch() 呼び出し(puppeteer.launch({ args: ['--no-sandbox'] });)に渡します。

[Add Buildpack] をクリックし、その URL を入力に貼り付け、[Save] をクリックします。次のデプロイでは、Puppeteer の実行に必要な依存関係もインストールされます。

中国語、日本語、韓国語の文字をレンダリングする必要がある場合は、https://github.com/CoffeeAndCode/puppeteer-herku-buildpack のような追加のフォント ファイルを含む Buildpack を使用する必要があります。

@timleland による別のガイドでも、サンプル プロジェクトが含まれています。

AWS Lambda の場合

AWS Lambda では、デプロイ パッケージのサイズが最大 50 MB に制限されています。このため、Lambda でヘッドレス Chrome(したがって Puppeteer)を実行することが課題となります。この問題の回避に役立つリソースをコミュニティがまとめています。

Amazon-Linux を実行している AWS EC2 インスタンス

CI/CD パイプラインで amazon-linux を実行している EC2 インスタンスがあり、amazon-linux で Puppeteer テストを実行する場合は、次の手順を行います。

  1. Chromium をインストールするには、まず amazon-linux-extras を有効にする必要があります。これは、EPEL(Extra Packages for Enterprise Linux)の一部です。

    sudo amazon-linux-extras install epel -y
    
  2. 次に、Chromium をインストールします。

    sudo yum install -y chromium
    

これで、Puppeteer で Chromium を起動してテストを実行できるようになりました。EPEL を有効にせず、npm install の一部として Chromium のインストールを続行すると、libatk-1.0.so.0 とその他多くのパッケージを使用できないため、Puppeteer は Chromium を起動できません。

コードのトランスパイルに関する問題

babel や TypeScript などの JavaScript トランスパイラを使用している場合は、非同期関数で evaluate() を呼び出せない可能性があります。これは、puppeteerFunction.prototype.toString() を使用して関数をシリアル化しますが、トランスパイラは puppeteer と互換性のない方法で出力コードを変更する可能性があるためです。

この問題の回避策としては、コードを改ざんしないように Transpiler に指示することがあります。たとえば、最新の ecma バージョン("target": "es2018")を使用するように TypeScript を設定します。もう 1 つの回避策は、関数ではなく文字列テンプレートを使用することです。

await page.evaluate(`(async() => {
   console.log('1');
})()`);