1. 简介与环境搭建

1.简介与环境搭建

Puppeteer 是一个 Node.js 库,提供了控制 Chrome 或 Firefox 浏览器的高级 API。它默认以无头模式运行,也就是没有图形界面的浏览器,但也可以配置成有界面的模式。简单来说,Puppeteer 让我们能用代码模拟用户在浏览器中的各种操作。

Puppeteer 核心概念与适用场景

什么是 Puppeteer

Puppeteer 的核心价值在于它封装了 Chrome DevTools Protocol 和 WebDriver BiDi 协议,让我们不必关心底层通信细节。它主要用来实现浏览器自动化,比如自动填写表单、截图、生成 PDF、抓取网页数据等。

从架构上看,Puppeteer 通过协议与浏览器进程通信,发送指令并接收结果。这种设计让自动化脚本运行在独立的 Node.js 进程中,即使访问恶意网页也不会影响系统安全。Puppeteer 团队遵循 Chromium 的核心原则:速度、安全、稳定和简洁。

核心概念

使用 Puppeteer 前需要理解几个基础概念:

Browser:代表一个浏览器实例。通过 puppeteer.launch() 启动后,会返回一个 Browser 对象,它是所有操作的入口。一个 Browser 可以包含多个浏览器上下文。

BrowserContext:浏览器上下文,相当于一个独立的会话环境。不同上下文之间的 Cookie、本地存储等数据是隔离的。这在需要多用户场景时特别有用,比如同时测试登录和非登录状态。

Page:代表浏览器中的一个标签页。大部分操作都在 Page 层面执行,比如导航、点击、输入等。一个 BrowserContext 可以创建多个 Page。

Frame:网页中的框架。现代网页经常包含 iframe,每个 iframe 都是一个独立的 Frame 对象。Page 对象本身也是一个 Frame,可以通过 page.mainFrame() 获取。

ElementHandle:指向页面中某个具体 DOM 元素的引用。获取到 ElementHandle 后,可以对其进行点击、输入、截图等操作。

JSHandle:更通用的 JavaScript 对象引用,不仅限于 DOM 元素。它让我们能在 Node.js 环境中持有浏览器中的 JavaScript 对象。

适用场景

Puppeteer 的能力覆盖了很多实际工作场景:

自动化测试:模拟用户操作进行端到端测试。相比单元测试,这种测试更接近真实用户行为,能发现更多集成问题。

网页截图与 PDF 生成:将网页内容转换为图片或 PDF 文档。这在生成报告、发票、证书等场景下很常见。

爬虫开发:抓取动态渲染的网页内容。传统爬虫只能获取静态 HTML,而 Puppeteer 可以执行 JavaScript,抓取单页应用的数据。

性能分析:捕获页面加载的时间线轨迹,分析性能瓶颈。可以生成 Chrome DevTools 兼容的 trace 文件。

Chrome 扩展测试:加载和测试 Chrome 扩展的功能,包括 Service Worker 和弹出页面。

表单自动填写:自动化处理重复性的表单填写工作,比如批量提交数据。

预渲染服务:为单页应用生成静态 HTML,改善 SEO 和首屏加载速度。

安装 Puppeteer 与浏览器驱动

系统要求

Puppeteer 对运行环境有一定要求:

  • Node.js 18 或更高版本。Puppeteer 跟随 Node.js 的维护 LTS 版本。
  • 如果使用 TypeScript,需要 4.7.4 或更高版本,并且目标版本设为 ES2022 或更高。
  • Chrome for Testing 浏览器对操作系统有特定要求,Windows 需要 x64 架构,macOS 支持 x64 和 arm64,Linux 主要支持 Debian/Ubuntu 和 openSUSE/Fedora 的 x64 架构。
  • Firefox 的支持从 Puppeteer v23.0.0 开始,需要 xzbzip2 工具来解压 Linux 版本。

puppeteer 与 puppeteer-core 的区别

Puppeteer 提供了两个 npm 包:puppeteerpuppeteer-core,选择哪个取决于具体需求。

puppeteer 是完整的产品包,安装时会自动下载匹配的 Chrome for Testing 浏览器(约 170-280MB,取决于操作系统)。它提供了合理的默认配置,适合大多数用户。从 v21.6.0 开始,还会额外下载 chrome-headless-shell 二进制文件。

puppeteer-core 则是核心库,不包含浏览器下载。适合以下场景:

  • 连接远程浏览器实例
  • 自行管理浏览器版本
  • 在特殊环境中控制浏览器路径
  • 减小安装包体积

使用 puppeteer-core 时,需要手动指定浏览器可执行文件路径:

import puppeteer from 'puppeteer-core';

const browser = await puppeteer.launch({
  executablePath: '/path/to/chrome'
});

安装过程

安装 Puppeteer 非常简单,执行一条命令即可:

npm i puppeteer

这条命令会完成几件事:

  1. 下载 puppeteer npm 包
  2. 执行 postinstall 脚本,下载匹配的 Chrome for Testing
  3. 将浏览器缓存到默认目录

默认缓存目录从 v19.0.0 开始改为 ~/.cache/puppeteer,这样不同项目可以共享浏览器安装,节省磁盘空间。如果需要在项目内缓存,可以通过配置修改。

浏览器驱动管理

Puppeteer 从 v20.0.0 开始使用 Chrome for Testing,这是 Google 专门为自动化测试提供的 Chrome 版本,保证了 headless 和 headful 模式的行为一致性。

旧的无头模式现在作为独立的 chrome-headless-shell 二进制文件提供。如果性能优先且不需要完整 Chrome 功能集,可以切换到这种模式:

const browser = await puppeteer.launch({
  headless: 'shell'
});

对于 Firefox 支持,从 v23.0.0 开始,Puppeteer 默认使用 WebDriver BiDi 协议自动化 Firefox,不再需要 nightly 版本。

编写第一个自动化测试脚本

基本结构

一个完整的 Puppeteer 脚本通常包含以下步骤:

  1. 启动浏览器
  2. 创建新页面
  3. 执行操作
  4. 关闭浏览器

下面是一个完整的示例,演示如何搜索 Chrome 开发者博客:

import puppeteer from 'puppeteer';

async function run() {
  // 启动浏览器并打开新页面
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    // 导航到目标页面
    await page.goto('https://developer.chrome.com/');
    
    // 设置视口大小
    await page.setViewport({ width: 1080, height: 1024 });
    
    // 使用键盘快捷键打开搜索菜单
    await page.keyboard.press('/');
    
    // 在搜索框中输入内容
    await page.locator('::-p-aria(Search)').fill('automate beyond recorder');
    
    // 点击第一个搜索结果
    await page.locator('.devsite-result-item-link').click();
    
    // 等待并获取文章标题
    const textSelector = await page
      .locator('::-p-text(Customize and automate)')
      .waitHandle();
    const fullTitle = await textSelector?.evaluate(el => el.textContent);
    
    console.log('文章标题是:"%s"', fullTitle);
  } finally {
    // 确保关闭浏览器
    await browser.close();
  }
}

run().catch(console.error);

代码解析

这段代码展示了 Puppeteer 的核心用法。puppeteer.launch() 启动浏览器,返回 Browser 实例。browser.newPage() 创建新标签页,返回 Page 实例。

page.goto() 导航到指定 URL,支持多种等待选项。page.setViewport() 设置页面尺寸,这在移动设备模拟时特别重要。

page.keyboard.press('/') 模拟键盘按下 / 键,很多网站用这个快捷键激活搜索功能。page.locator() 是推荐的元素定位方式,它返回一个 Locator 对象,支持自动等待和重试。

locator.fill() 方法会自动等待元素可见、可交互,然后填充内容。这比旧版的 page.type() 更可靠。locator.click() 同样会等待元素就绪后再点击。

waitHandle() 方法等待元素出现并返回 ElementHandle,然后可以用 evaluate() 在页面上下文中执行代码,获取元素属性或文本。

最后的 browser.close() 很重要,否则浏览器进程会一直运行,消耗系统资源。使用 try-finally 确保即使出错也能关闭浏览器。

运行脚本

将代码保存为 search.js,然后执行:

node search.js

如果一切正常,控制台会输出搜索到的文章标题。第一次运行可能稍慢,因为需要启动浏览器进程。

配置文件与环境变量

配置方式

Puppeteer 提供了两种配置方式:配置文件和环境变量。配置文件是推荐的方式,更易于管理和版本控制。

配置文件支持多种格式,Puppeteer 会从当前目录向上查找:

  • .puppeteerrc.cjs
  • .puppeteerrc.js
  • .puppeteerrc (YAML/JSON)
  • .puppeteerrc.json
  • .puppeteerrc.yaml
  • puppeteer.config.js
  • puppeteer.config.cjs

常用配置示例

修改缓存目录

如果默认的 ~/.cache/puppeteer 不适合,比如 CI/CD 环境,可以改为项目内缓存:

// .puppeteerrc.cjs
const { join } = require('path');

/**
 * @type {import("puppeteer").Configuration}
 */
module.exports = {
  cacheDirectory: join(__dirname, '.cache', 'puppeteer')
};

修改后需要重新安装 Puppeteer 才能生效。

跳过浏览器下载

在某些场景下,比如 Docker 镜像中已经包含了浏览器,可以跳过下载:

// .puppeteerrc.cjs
module.exports = {
  chrome: {
    skipDownload: true
  }
};

多浏览器支持

从 v23.0.0 开始,Puppeteer 支持同时配置多个浏览器:

// .puppeteerrc.cjs
module.exports = {
  chrome: {
    skipDownload: false
  },
  firefox: {
    skipDownload: false
  }
};

配置完成后,运行 npx puppeteer browsers install 下载配置的浏览器。

环境变量

部分选项只能通过环境变量配置,比如代理设置:

  • HTTP_PROXY:HTTP 代理
  • HTTPS_PROXY:HTTPS 代理
  • NO_PROXY:不使用代理的域名列表

其他选项可以通过 PUPPETEER_ 前缀的环境变量设置,比如 PUPPETEER_CACHE_DIR 覆盖缓存目录。

在 CI/CD 环境中,环境变量特别有用,可以在不修改代码的情况下调整行为。


本章介绍了 Puppeteer 的基本概念、安装方法和第一个脚本。Puppeteer 的强大之处在于它既提供了简单的 API,又能深入控制浏览器行为。从下一章开始,我们将深入探讨浏览器生命周期管理、页面导航策略等核心话题,逐步掌握更复杂的自动化场景。