跳到主要内容
版本: 23.11.1

问题排查

注意

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

Cannot find module 'puppeteer-core/internal/...'

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

Could not find expected browser locally

从 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'],
});

上下文:issue 3681

Chrome 在 Windows 上报告沙箱错误

Chrome 在 Windows 上使用沙箱,这需要对下载的 Chrome 文件具有额外的权限。从 Puppeteer v22.14.0 开始,Puppeteer 将尝试通过在安装浏览器期间运行 Chrome 提供的 setup.exe 工具来配置这些权限。

如果您使用的是较旧的 Puppeteer 版本,或者仍然在浏览器输出中看到以下错误

[24452:59820:0508/113713.058:ERROR:sandbox_win.cc(913)] Sandbox cannot access executable. Check filesystem permissions are valid. See https://bit.ly/31yqMJR.: Access is denied. (0x5)

您可以使用 icacls 手动设置权限

icacls %USERPROFILE%/.cache/puppeteer/chrome /grant *S-1-15-2-1:(OI)(CI)(RX)
注意

在高安全性环境中,应使用更严格的 SID,例如来自 installer 的 SID。

有关详细信息,请参阅 https://bit.ly/31yqMJR

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(例如 Ubuntu)依赖项
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 依赖项
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-headless-shell 禁用 GPU 合成

chrome-headless-shell 需要 --enable-gpu在无头模式下启用 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 沙箱

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

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

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

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

推荐的运行 Chrome 的方式是使用沙箱

Ubuntu 上的 AppArmor 问题

Ubuntu 23.10+(或未来可能出现的其他 Linux 发行版)附带一个 AppArmor 配置文件,该配置文件适用于安装在 /opt/google/chrome/chrome(默认安装路径)的 Chrome 稳定版二进制文件。此策略存储在 /etc/apparmor.d/chrome 中。此 AppArmor 策略会阻止 Puppeteer 下载的 Chrome for Testing 二进制文件使用用户命名空间,从而在尝试启动浏览器时导致 No usable sandbox! 错误。

有关解决方法,请参阅 https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md

使用setuid 沙箱

注意

重要提示:Linux SUID 沙箱几乎但并非完全删除。请参阅 https://bugs.chromium.org/p/chromium/issues/detail?id=598454 此部分内容大部分已过时。

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

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

技巧与窍门

  • 应该启动 xvfb 服务,以便在非无头模式下运行 Chrome for Testing
  • 默认在 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

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

在 Docker 中启动并运行无头 Chrome 可能比较棘手。Puppeteer 安装的捆绑 Chrome for Testing 缺少必要的共享库依赖项。

要解决此问题,您需要在 Dockerfile 中安装缺少的依赖项和最新的 Chrome for Testing 包。

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 Chrome for Testing 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 Chrome for Testing 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 上运行

请注意,Chrome 开箱即用不支持 Alpine,因此请确保在 Alpine 上安装了兼容的系统依赖项,并在使用前测试镜像。请参阅 https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/dist_package_provides.jsonhttps://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/dist_package_versions.json,以获取受支持发行版上所需的系统软件包列表。

注意

Alpine 3.20 中的当前 Chromium 版本会导致 Puppeteer 超时问题。降级到 Alpine 3.19 可解决此问题。请参阅 #11640#12637#12189

您需要找到 最新的 Chromium 包,然后查找 Puppeteer 的 支持的浏览器版本,并使用相应的版本。

示例

Alpine Chromium 版本:100

Puppeteer:Puppeteer v13.5.0

Dockerfile

FROM alpine

# Installs 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 libgbm1 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);

它很慢,因为在发送响应后启动 puppeteer 时,GCR 上禁用了 CPU。您想要做的是这个:

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

如果您想在后台运行内容,则需要“始终启用 CPU”(转到 Google Cloud Run Service > Edit & Deploy Revision > CPU 分配和定价),即使在发送响应后也是如此。这应该可以解决问题。

提示

默认情况下,Docker 运行一个容器,其 /dev/shm 共享内存空间为 64MB。这对于 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 的进程有特殊处理,这使得在某些情况下(例如在 Docker 中)很难正确终止 Chrome。

在云中运行 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 运行时附带运行无头 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 运行时不附带运行无头 Chrome 所需的系统软件包。您需要设置自己的 Dockerfile包含缺少的依赖项

在 Heroku 上运行 Puppeteer

在 Heroku 上运行 Puppeteer 需要一些 Heroku 为您启动的 Linux 盒子中未包含的其他依赖项。要在部署时添加依赖项,请在 Settings > Buildpacks 下将 Puppeteer Heroku 构建包添加到您的应用程序的构建包列表中。

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

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

当您单击添加构建包时,只需将该 URL 粘贴到输入框中,然后单击“保存”。在下次部署时,您的应用程序还将安装 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 (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() 可能不起作用。这是因为当 puppeteer 使用 Function.prototype.toString() 来序列化函数时,转译器可能会以不兼容 puppeteer 的方式更改输出代码。

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

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