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 开始,需要
xz或bzip2工具来解压 Linux 版本。
puppeteer 与 puppeteer-core 的区别
Puppeteer 提供了两个 npm 包:puppeteer 和 puppeteer-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
这条命令会完成几件事:
- 下载
puppeteernpm 包 - 执行 postinstall 脚本,下载匹配的 Chrome for Testing
- 将浏览器缓存到默认目录
默认缓存目录从 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 脚本通常包含以下步骤:
- 启动浏览器
- 创建新页面
- 执行操作
- 关闭浏览器
下面是一个完整的示例,演示如何搜索 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.yamlpuppeteer.config.jspuppeteer.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,又能深入控制浏览器行为。从下一章开始,我们将深入探讨浏览器生命周期管理、页面导航策略等核心话题,逐步掌握更复杂的自动化场景。