Puppeteer 中文文档v25.8.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.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
查看相关讨论
  • #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沙箱

为了保护主机环境免受不可信网页内容的影响,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的提示。简而言之,你需要通过以下任一方式安装缺失的依赖:

  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位于 此处, 使用说明请参阅 集成>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.jsonchrome/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

你需要修改两个地方:

  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'模式。这可以通过将它们作为参数传递给.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测试,请按照以下步骤操作。

  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的一部分来安装,那么由于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');
})()`);