疑难排查
为了让本页面保持最新,我们主要依赖社区的贡献。 如果你发现某些内容已过时,请发送一个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.js(或puppeteer.config.js)的配置文件,内容如下:
import {join} from 'path';
/**
* @type {import("puppeteer").Configuration}
*/
export default {
cacheDirectory: join(import.meta.dirname, '.cache', 'puppeteer'),
};
你需要重新安装puppeteer才能使配置生效。更多信息请参阅配置Puppeteer。
安装脚本被阻止
如果你使用的包管理器默认阻止依赖安装脚本(例如新RFC下的npm、pnpm、Yarn Berry、Bun或Deno),那么自动下载浏览器的postinstall脚本将不会运行。
要手动下载所需的浏览器,请运行:
npx puppeteer browsers install
或者,你也可以通过更新项目的package.json(针对npm)来重新选择运行postinstall脚本:
{
"allowScripts": {
"puppeteer": true
}
}
在Chrome中导航到HTTP URL时出现net::ERR_BLOCKED_BY_CLIENT
Chrome正在推出一个名为HttpsFirstBalancedModeAutoEnable的功能,当用户导航到HTTP站点时,该功能会向用户显示警告。该功能在Puppeteer默认使用的Chrome for Testing构建版本中默认启用。
该功能会使对HTTP URL的导航请求产生net::ERR_BLOCKED_BY_CLIENT错误,该错误可以被捕获并从中恢复。当错误发生时,会向用户显示一个带有继续导航按钮的警告页面。该按钮可以通过Puppeteer点击。本地HTTP主机不会触发警告,但远程主机可能会。更多详情请参阅https://crbug.com/378022921
可以通过在启动Chrome时传入--disable-features=HttpsFirstBalancedModeAutoEnable参数来禁用此Chrome功能:
const browser = await puppeteer.launch({
args: ['--disable-features=HttpsFirstBalancedModeAutoEnable'],
});
Chrome在Windows上无法启动
某些Chrome政策可能会强制要求Chrome/Chromium附带某些扩展运行。
Puppeteer默认会传入--disable-extensions标志,当此类政策生效时,启动将会失败。
要解决此问题,请设置enableExtensions选项:
const browser = await puppeteer.launch({
enableExtensions: true,
});
背景: 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,例如 安装程序中的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沙箱
为了保护主机环境免受不可信网页内容的影响,Chrome使用了多层沙箱机制。要使其正常工作,应首先对主机进行配置。如果没有合适的沙箱可供Chrome使用,它将会崩溃并报出No usable sandbox!错误。
如果你绝对信任在Chrome中打开的内容,可以使用--no-sandbox参数启动Chrome:
const browser = await puppeteer.launch({
args: ['--no-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
👋 我们在Travis CI上运行Puppeteer的测试直到v6.0.0(当时我们迁移到了GitHub Actions)——请参阅我们历史版本中的
.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
👋 我们使用Cirrus Ci在Docker容器中运行Puppeteer的测试直到v3.0.x——请参阅我们历史版本中的
Dockerfile.linux(v3.0.1) 作为参考。从v16.0.0开始,我们通过GitHub容器注册表发布Docker镜像。Dockerfile位于 此处, 使用说明请参阅 集成>Docker。如果你正在构建自己的镜像,下面的说明可能仍然有用。
让无头Chrome在Docker中运行起来可能有些棘手。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。
在只读容器中运行
Chrome在启动期间会写入配置文件、配置和缓存文件。在只读容器中,或者在只挂载了特定可写位置的容器中,请确保这些路径指向可写目录。否则Chrome可能会在Puppeteer连接之前失败;一个常见错误是chrome_crashpad_handler: --database is required。
如果你的容器提供了可写的/tmp,请将Chrome的XDG配置和缓存位置指向那里:
ENV XDG_CONFIG_HOME=/tmp/.chromium
ENV XDG_CACHE_HOME=/tmp/.chromium
Puppeteer还需要一个可写的用户数据目录。默认情况下,Puppeteer会在操作系统临时目录下创建临时配置文件,但如有需要,你可以设置一个显式的位置:
const browser = await puppeteer.launch({
userDataDir: '/tmp/.puppeteer-profile',
});
或者,将这些目录挂载为可写卷,并确保运行Chrome的用户拥有它们。
在Alpine上运行
请注意,Chrome开箱即用地不支持Alpine,因此请确保你在Alpine上安装了兼容的系统依赖,并在使用前测试该镜像。请参阅chrome/installer/linux/rpm/dist_package_provides.json和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 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
...
在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'模式。这可以通过将它们作为参数传递给.launch()调用来实现:puppeteer.launch({ args: ['--no-sandbox'] });。
在Google Cloud Run上运行Puppeteer
Google Cloud Run在向客户端写入HTTP响应后默认会禁用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);
它之所以慢,是因为CPU在GCR上被禁用,而puppeteer又是在响应发送之后才启动的。你应该这样做:
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 allocation and pricing)。这样就可以解决问题。
提示
启动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.js的文件来覆盖puppeteer缓存目录,内容如下:
import {join} from 'path';
/**
* @type {import("puppeteer").Configuration}
*/
export default {
cacheDirectory: join(import.meta.dirname, 'node_modules', '.puppeteer_cache'),
};
[!NOTE] Google App Engine会在构建之间缓存你的
node_modules。 将Puppeteer缓存指定为node_modules的子目录 可以缓解因postinstall未运行而导致Puppeteer找不到浏览器可执行文件的问题。
在Google Cloud Functions上运行Puppeteer
Google Cloud Functions的Node.js运行时附带运行无头Chrome所需的所有系统包。
要使用puppeteer,请在package.json中将该模块指定为依赖,然后在应用程序的根目录包含一个名为.puppeteerrc.js的文件来覆盖puppeteer缓存目录,内容如下:
import {join} from 'path';
/**
* @type {import("puppeteer").Configuration}
*/
export default {
cacheDirectory: join(import.meta.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 buildpack添加到应用的buildpack列表中。
该buildpack的URL是 https://github.com/jontewks/puppeteer-heroku-buildpack
请确保在启动Puppeteer时使用'--no-sandbox'模式。这可以通过将其作为参数传递给.launch()调用来实现:puppeteer.launch({ args: ['--no-sandbox'] });。
当你点击添加buildpack时,只需将该URL粘贴到输入框中,然后点击保存。在下一次部署时,你的应用也会安装Puppeteer运行所需的依赖。
如果你需要渲染中文、日文或韩文字符,可能需要使用带有额外字体文件的buildpack,例如 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测试,请按照以下步骤操作。
-
要安装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的一部分来安装,那么由于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');
})()`);