跳至主要内容
版本:22.5.0

故障排除

注意

为了保持本页的最新状态,我们主要依赖社区贡献。如果您发现某些内容不再是最新的,请发送 PR。

找不到模块“puppeteer-core/internal/...”

如果你的 Node.js 版本低于 14 或你正在使用自定义解析器(例如 jest-resolve),则可能会出现这种情况。对于前者,我们不支持已弃用的 Node.js 版本。对于后者,通常升级解析器(或其父模块,例如 jest)即可(例如 https://github.com/puppeteer/puppeteer/issues/9121

找不到本地预期浏览器

从 v19.0.0 开始,Puppeteer 将使用 os.homedir 将浏览器下载到 ~/.cache/puppeteer 中,以在 Puppeteer 升级之间实现更好的缓存。通常,主目录定义明确(即使在 Windows 上),但偶尔主目录可能不可用。在这种情况下,我们提供了 PUPPETEER_CACHE_DIR 变量,允许你更改安装目录。

例如,

PUPPETEER_CACHE_DIR=$(pwd) npm install puppeteer
PUPPETEER_CACHE_DIR=$(pwd) node <script-path>

你还可以使用以下内容在应用程序根目录创建一个名为 .puppeteerrc.cjs(或 puppeteer.config.cjs)的配置文件

const {join} = require('path');

/**
* @type {import("puppeteer").Configuration}
*/
module.exports = {
cacheDirectory: join(__dirname, '.cache', 'puppeteer'),
};

你需要重新安装 puppeteer 才能使配置生效。有关更多信息,请参阅 配置 Puppeteer

Chrome 无头模式在 Windows 上无法启动

一些 Chrome 政策 可能强制使用某些扩展运行 Chrome/Chromium。

Puppeteer 默认传递 --disable-extensions 标志,并且当此类政策处于活动状态时,它将无法启动。

要解决此问题,请尝试在没有标志的情况下运行

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

上下文:问题 3681

Chrome 在 Linux 上无法启动

确保安装了所有必需的依赖项。你可以在 Linux 机器上运行 ldd chrome | grep not 来检查缺少哪些依赖项。下面提供了常见依赖项。此外,请参阅 https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/dist_package_versions.json 以获取 Chrome 安装程序声明的最新依赖项列表。

注意

Chrome 目前不为 Linux 提供 arm64 二进制文件。只有适用于 Mac ARM 的 arm64 二进制文件。这意味着默认下载的 Linux 二进制文件在 Linux arm64 上无法运行。

Debian (e.g. Ubuntu) Dependencies
ca-certificates
fonts-liberation
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 Dependencies
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

After installing dependencies you need to update nss library using this command

yum update nss -y
Check out discussions
  • #290 - Debian troubleshooting
  • #391 - CentOS troubleshooting
  • #379 - Alpine troubleshooting

chrome-headless-shell 禁用 GPU 合成

chrome-headless-shell 需要 --enable-gpu在 headless 模式下启用 GPU 加速

const browser = await puppeteer.launch({
headless: 'shell',
args: ['--enable-gpu'],
});

使用 Chrome 设置 GPU

通常,如果系统具有适当的驱动程序,Chrome 应该能够检测并启用 GPU。有关其他提示,请参阅以下博客文章 https://developer.chrome.com/blog/supercharge-web-ai-testing

设置 Chrome Linux 沙盒

为了保护主机环境免受不受信任的网络内容的侵害,Chrome 使用多层沙盒。为了正常工作,应首先配置主机。如果没有可供 Chrome 使用的良好沙盒,它将崩溃并显示错误 No usable sandbox!

如果您绝对信任在 Chrome 中打开的内容,则可以使用 --no-sandbox 参数启动 Chrome

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

强烈不建议在没有沙盒的情况下运行。请考虑配置沙盒。

有 2 种方法可以在 Chromium 中配置沙盒。

用户命名空间克隆仅受现代内核支持。通常可以启用非特权用户命名空间,但在某些情况下,它们会为(未沙盒化的)非 root 进程提升到内核权限打开更多内核攻击面。

sudo sysctl -w kernel.unprivileged_userns_clone=1

[备选] 设置setuid 沙盒

setuid 沙盒作为一个独立的可执行文件提供,并且位于 Puppeteer 下载的 Chrome 旁边。对不同的 Chrome 版本重复使用相同的沙盒可执行文件是可以的,因此以下操作只能在每个主机环境中执行一次

# cd to Puppeteer cache directory (adjust the path if using a different cache directory).
cd ~/.cache/puppeteer/chrome/linux-<version>/chrome-linux64/
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

或添加到您的 Dockerfile

ENV CHROME_DEVEL_SANDBOX /usr/local/sbin/chrome-devel-sandbox

在 Travis CI 上运行 Puppeteer

👋 我们在 Travis CI 上运行了 Puppeteer 测试,直到 v6.0.0(当我们迁移到 GitHub Actions 时) - 请参阅我们的历史 .travis.yml (v5.5.0) 以供参考。

提示和技巧

  • xvfb 服务应启动,以便在非 headless 模式下运行 Chromium
  • 默认情况下在 Travis 上的 Xenial Linux 上运行
  • 默认情况下运行 npm install
  • 默认情况下缓存 node_modules

.travis.yml 可能如下所示

language: node_js
node_js: node
services: xvfb
script:
- npm test

在 WSL(适用于 Linux 的 Windows 子系统)上运行 Puppeteer

请参阅 此线程,其中包含一些特定于 WSL 的提示。简而言之,您需要通过以下方式安装缺少的依赖项

  1. 在 WSL 上安装 Chrome 以安装所有依赖项
  2. 手动安装所需的依赖项:sudo apt install libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2
注意

所需依赖项的列表可能会过时,并且取决于您已安装的内容。

在 CircleCI 上运行 Puppeteer

在 CircleCI 上顺利运行 Puppeteer 需要以下步骤

  1. 在您的配置中从 NodeJS 镜像 开始,如下所示
    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(说明),或将它的 源代码 的部分粘贴到您自己的配置中。
  3. 最后,如果您通过 Jest 使用 Puppeteer,那么您可能会遇到生成子进程的错误
    [00:00.0]  jest args: --e2e --spec --max-workers=36
    Error: spawn ENOMEM
    at ChildProcess.spawn (internal/child_process.js:394:11)
    这可能是因为 Jest 自动检测了整个机器上的进程数(36),而不是容器允许的进程数(2)。要解决此问题,请在您的测试命令中设置 jest --maxWorkers=2

在 Docker 中运行 Puppeteer

👋 我们使用 Cirrus Ci 在 Docker 容器中运行 Puppeteer 测试,直到 v3.0.x - 请参阅我们的历史 Dockerfile.linux (v3.0.1) 以供参考。从 v16.0.0 开始,我们通过 GitHub 注册表发布 Docker 镜像。Dockerfile 位于 此处,使用说明在 README.md 中。如果您正在构建自己的镜像,以下说明可能仍然有用。

在 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_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`"

https://github.com/ebidel/try-puppeteer 有一个完整示例,展示了如何从运行在 App Engine Flex(Node)上的 Web 服务器运行此 Dockerfile。

在 Alpine 上运行

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_EXECUTABLE_PATH=/usr/bin/chromium-browser

# Puppeteer v13.5.0 works with Chromium 100.
RUN yarn add [email protected]

# 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

...

在 GitlabCI 上运行 Puppeteer

这与上面的一些说明非常相似,但需要稍微不同的配置才能最终成功。

通常问题如下所示

Error: Failed to launch chrome! spawn /usr/bin/chromium-browser ENOENT

您需要修补两个地方

  1. 您的 gitlab-ci.yml 配置
  2. 启动 Puppeteer 时的参数列表

gitlab-ci.yml 中,我们需要安装一些软件包,以便可以在您的 Docker 环境中启动无头 Chrome

before_script:
- apt-get update
- apt-get install -yq gconf-service libasound2 libatk1.0-0 libc6 libcairo2
libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4
libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 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 ca-certificates fonts-liberation libnss3 lsb-release
xdg-utils wget

接下来,您必须在启动 Puppeteer 时使用 '--no-sandbox' 模式和 '--disable-setuid-sandbox'。可以通过将它们作为参数传递给 .launch() 调用来完成此操作:puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });

在 Google Cloud Run 上运行 Puppeteer

在向客户端写入 HTTP 响应后,Google Cloud Run 默认禁用 CPU。这意味着如果您在写入响应后“在后台运行 Puppeteer”,Puppeteer 将显得非常慢(启动需要 1-5 分钟)。

因此,此简单的 express 应用程序将明显变慢

import express from 'express';

const app = express();

app.post('/test-puppeteer', (req, res) => {
res.json({
jobId: 123,
acknowledged: true,
});

puppeteer.launch().then(browser => {
// 2 minutes later...
});
});

app.listen(3000);

它之所以慢是因为在响应发送后,GCR 上的 CPU 被禁用,因为 puppeteer 已启动。您要执行的操作如下

app.post('/test-puppeteer', (req, res) => {
puppeteer.launch().then(browser => {
// A second later...
res.json({
jobId: 123,
acknowledged: true,
});
});
});

如果您想在后台运行该内容,则需要在发送响应后“始终启用 CPU”。这应该可以解决问题。

提示

默认情况下,Docker 使用 64MB 的 /dev/shm 共享内存空间运行容器。这通常对于 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'],
});

这会将共享内存文件写入 /tmp 而不是 /dev/shm。有关更多详细信息,请参阅 crbug.com/736452

在启动 Chrome 时看到其他奇怪的错误?在本地开发时,尝试使用 docker run --cap-add=SYS_ADMIN 运行容器。由于 Dockerfile 将 pptr 用户添加为非特权用户,因此它可能没有所有必需的特权。

如果您遇到大量僵尸 Chrome 进程滞留,那么dumb-init 值得一试。对于 PID=1 的进程有特殊处理,这使得在某些情况下难以正确终止 Chrome(例如,在 Docker 中)。

在云中运行 Puppeteer

在 Google App Engine 上运行 Puppeteer

App Engine 标准环境 的 Node.js 运行时附带了运行无头 Chrome 所需的所有系统包。

要使用puppeteer,请在package.json中将该模块指定为依赖项,然后通过在应用程序根目录中包含一个名为.puppeteerrc.cjs的文件来覆盖puppeteer缓存目录,内容如下

const {join} = require('path');

/**
* @type {import("puppeteer").Configuration}
*/
module.exports = {
cacheDirectory: join(__dirname, 'node_modules', '.puppeteer_cache'),
};

[!NOTE] Google App Engine在构建之间缓存您的node_modules。将Puppeteer缓存指定为node_modules的子目录可缓解Puppeteer由于未运行postinstall而找不到浏览器可执行文件的问题。

在Google Cloud Functions上运行Puppeteer

Google Cloud Functions 的Node.js运行时附带运行Headless Chrome所需的所有系统包。

要使用puppeteer,请在package.json中将该模块指定为依赖项,然后通过在应用程序根目录中包含一个名为.puppeteerrc.cjs的文件来覆盖puppeteer缓存目录,内容如下

const {join} = require('path');

/**
* @type {import("puppeteer").Configuration}
*/
module.exports = {
cacheDirectory: join(__dirname, 'node_modules', '.puppeteer_cache'),
};

[!NOTE] Google Cloud Functions在构建之间缓存您的node_modules。将puppeteer缓存指定为node_modules的子目录可缓解在命中缓存时puppeteer安装过程不运行的问题。

在Google Cloud Run上运行Puppeteer

Google Cloud Run 的默认Node.js运行时不附带运行Headless Chrome所需的所有系统包。您需要设置自己的Dockerfile包含缺少的依赖项

在Heroku上运行Puppeteer

在Heroku上运行Puppeteer需要一些Heroku为您启动的Linux系统中未包含的附加依赖项。要在部署时添加依赖项,请将Puppeteer Heroku构建包添加到“设置”>“构建包”下应用的构建包列表中。

构建包的网址是https://github.com/jontewks/puppeteer-heroku-buildpack

启动Puppeteer时,请确保使用'--no-sandbox'模式。可以通过将它作为参数传递给.launch()调用来完成此操作:puppeteer.launch({ args: ['--no-sandbox'] });

当您单击添加构建包时,只需将该网址粘贴到输入框中,然后单击保存。在下次部署时,您的应用还将安装Puppeteer运行所需的依赖项。

如果您需要渲染中文、日文或韩文,您可能需要使用带有附加字体文件的构建包,例如https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack

还有另一篇来自 @timleland 的 简单指南,其中包括一个示例项目:https://timleland.com/headless-chrome-on-heroku/

在 AWS Lambda 上运行 Puppeteer

AWS Lambda 限制部署包大小约为 50MB。这给在 Lambda 上运行无头 Chrome(因此也运行 Puppeteer)带来了挑战。社区已经整理了一些可以解决这些问题的资源

在运行 Amazon-Linux 的 AWS EC2 实例上运行 Puppeteer

如果你在 CI/CD 管道中使用运行 amazon-linux 的 EC2 实例,并且你希望在 amazon-linux 中运行 Puppeteer 测试,请按照以下步骤操作。

  1. 要安装 Chromium,你必须首先启用 amazon-linux-extras,它作为 EPEL(企业 Linux 的额外包) 的一部分提供

    sudo amazon-linux-extras install epel -y
  2. 接下来,安装 Chromium

    sudo yum install -y chromium

现在 Puppeteer 可以启动 Chromium 来运行你的测试。如果你没有启用 EPEL,并且你继续安装 chromium 作为 npm install 的一部分,则 Puppeteer 无法启动 Chromium,因为缺少 libatk-1.0.so.0 和许多其他包。

代码转换问题

如果你正在使用 JavaScript 转换器(例如 babel 或 TypeScript),则使用异步函数调用 evaluate() 可能无法正常工作。这是因为 puppeteer 使用 Function.prototype.toString() 来序列化函数,而转换器可能会以与 puppeteer 不兼容的方式更改输出代码。

解决此问题的某些方法是指示转换器不要弄乱代码,例如,将 TypeScript 配置为使用最新的 ecma 版本("target": "es2018")。另一种解决方法是使用字符串模板而不是函数

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