故障排除
为了保持本页的最新状态,我们主要依赖社区贡献。如果您发现某些内容不再是最新的,请发送 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
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 的提示。简而言之,您需要通过以下方式安装缺少的依赖项
- 在 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
👋 我们使用 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
您需要修补两个地方
- 您的
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 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)带来了挑战。社区已经整理了一些可以解决这些问题的资源
- https://github.com/sparticuz/chromium(一个与供应商和框架无关的库,支持现代版本的
chromium
) - https://github.com/alixaxel/chrome-aws-lambda(支持高达 puppeteer 10.1 - 已过时)
- https://github.com/adieuadieu/serverless-chrome/blob/HEAD/docs/chrome.md(无服务器插件 - 已过时)
在运行 Amazon-Linux 的 AWS EC2 实例上运行 Puppeteer
如果你在 CI/CD 管道中使用运行 amazon-linux 的 EC2 实例,并且你希望在 amazon-linux 中运行 Puppeteer 测试,请按照以下步骤操作。
-
要安装 Chromium,你必须首先启用
amazon-linux-extras
,它作为 EPEL(企业 Linux 的额外包) 的一部分提供sudo amazon-linux-extras install epel -y
-
接下来,安装 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');
})()`);