问题排查
为了保持此页面最新,我们主要依赖社区贡献。如果您发现某些内容不再是最新的,请发送 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
变量,允许您更改安装目录。
例如,
- npm
- Yarn
- pnpm
PUPPETEER_CACHE_DIR=$(pwd) npm install puppeteer
PUPPETEER_CACHE_DIR=$(pwd) node <script-path>
PUPPETEER_CACHE_DIR=$(pwd) yarn add puppeteer
PUPPETEER_CACHE_DIR=$(pwd) node <script-path>
PUPPETEER_CACHE_DIR=$(pwd) pnpm add 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
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 的技巧。简而言之,您需要通过以下任一方式安装缺少的依赖项
- 在 WSL 上安装 Chrome 以安装所有依赖项
- 手动安装所需的依赖项:
sudo apt install libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2
。
所需的依赖项列表可能会过时,并且取决于您已安装的内容。
在 CircleCI 上运行 Puppeteer
在 CircleCI 上顺利运行 Puppeteer 需要以下步骤:
- 首先,在您的配置中使用 NodeJS 镜像,如下所示:
docker:
- image: circleci/node:14 # Use your desired version
environment:
NODE_ENV: development # Only needed if puppeteer is in `devDependencies` - 像
libXtst6
这样的依赖项可能需要通过apt-get
安装,因此请使用 threetreeslight/puppeteer orb(说明),或将其 源代码 的部分内容粘贴到您自己的配置中。 - 最后,如果您通过 Jest 使用 Puppeteer,则可能会遇到生成子进程的错误。
这可能是由于 Jest 自动检测整个机器上的进程数 (
[00:00.0] jest args: --e2e --spec --max-workers=36
Error: spawn ENOMEM
at ChildProcess.spawn (internal/child_process.js:394:11)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.json 和 https://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
您需要修补两个地方:
- 您的
gitlab-ci.yml
配置 - 启动 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)带来了挑战。社区汇集了一些可以解决这些问题的资源:
- https://github.com/sparticuz/chromium(一个供应商和框架无关的库,支持现代版本的
chromium
)
在运行 Amazon-Linux 的 AWS EC2 实例上运行 Puppeteer
如果您在 CI/CD 管道中使用运行 amazon-linux 的 EC2 实例,并且如果您想在 amazon-linux 中运行 Puppeteer 测试,请按照以下步骤操作。
-
要安装 Chromium,您必须首先启用
amazon-linux-extras
,它是 EPEL (Extra Packages for Enterprise Linux) 的一部分。sudo amazon-linux-extras install epel -y
-
接下来,安装 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');
})()`);