首页
/ Puppeteer 快速上手:从安装到编写第一个浏览器自动化脚本

Puppeteer 快速上手:从安装到编写第一个浏览器自动化脚本

2026-09-07 21:11:56作者:邓越浪Henry

Puppeteer 是面向 Chrome 与 Firefox 的官方 JavaScript 自动化 API,通过同一套异步代码即可完成"启动/连接浏览器 → 创建页面 → 模拟用户操作 → 读取结果"的完整流程。本文以仓库内 docs/guides/getting-started.md 为骨架,逐步拆解官方首个示例脚本的每一行,并结合 packages/puppeteer-core 的源码实现,说明 launch/newPage/gotoLocator::-p- 查询引擎等核心机制的底层原理,读完即可独立编写可运行、可复用的浏览器自动化脚本。

环境准备:安装包与浏览器的关系

本仓库并非单一 npm 包,而是同时产出 puppeteerpuppeteer-core 两个包的 monorepo。两者在"谁来负责浏览器"这件事上分工明确(细节见 docs/guides/installation.md):

  • puppeteer 是一个产品:安装后会自动下载与之匹配的 Chrome for Testing 与 chrome-headless-shell,开箱即用,适合大多数自动化任务。
  • puppeteer-core 是一个纯库:只负责通过 DevTools 协议驱动"任何已经存在的浏览器",安装时不会下载任何浏览器;它适合连接远程浏览器或自己管理浏览器生命周期的场景。

安装命令(项目内选择 puppeteer):

npm i puppeteer

安装过程中浏览器默认被下载到 $HOME/.cache/puppeteer。仓库根目录的 puppeteer.config.js 展示了如何通过 Configuration 控制下载行为——默认对 chromechrome-headless-shellfirefox 三个目标均设置了 skipDownload: false,即默认都会参与下载。更完整的配置项与环境变量说明可参考 docs/guides/configuration.md

注意:如果你的包管理器(npm RFC 新策略、pnpm、Yarn Berry、Bun、Deno 等)默认拦截依赖的 postinstall 脚本,自动下载会被跳过,运行时会报 Could not find Chrome (ver. ...)。此时可在安装完成后手动执行 npx puppeteer browsers install,或在 package.json 中用 allowScripts(npm 为例)为 puppeteer 显式放行脚本。

若选择 puppeteer-core,导入语句需同步修改,且 launch 时必须显式传入 executablePath(或 channel,当浏览器安装在标准位置时):

import puppeteer from 'puppeteer-core';

你的第一个脚本:搜索并抓取文章标题

官方快速上手示例展示了一个完整闭环:打开 Chrome 开发者博客首页 → 用键盘打开站内搜索 → 按可访问名称输入关键词 → 点击第一条结果 → 读取文章完整标题。在浏览器端它完整对应了 page.gotopage.setViewportkeyboardpage.locatorJSHandle.evaluate 等一系列 API。

下面先创建脚本文件(如 search-blog.ts 或转译为 JS 运行),完整代码如下:

import puppeteer from 'puppeteer';
// 使用 puppeteer-core 时改为:
// import puppeteer from 'puppeteer-core';

// 启动浏览器并打开一个新的空白页面。
const browser = await puppeteer.launch();
const page = await browser.newPage();

// 导航页面到目标 URL。
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('The title of this blog post is "%s".', fullTitle);

await browser.close();

这段代码体现了 Puppeteer 与其它浏览器测试框架一致的思维模型:先有 Browser,再从 Browser 派生出 Page,之后所有操作都在 Page 维度上进行。仓库中 browser.newPage()browser.close() 的行为可参考 puppeteer.browser.newpage.mdpuppeteer.browser.close.md

下面把每一段拆开讲透。

启动浏览器:launch 与连接两种入口

const browser = await puppeteer.launch();
const page = await browser.newPage();

launch 的实现位于 packages/puppeteer-core/src/node/PuppeteerNode.ts 及其底层的 ChromeLauncher/FirefoxLauncher(见 node/ChromeLauncher.tsnode/BrowserLauncher.ts)。它的职责是:定位可执行文件 → 以子进程方式拉起浏览器 → 建立 CDP 连接 → 返回 Browser 实例。不传任何参数时,Puppeteer 会使用随 puppeteer 包一起下载、并被验证过兼容性的浏览器版本。

如果浏览器不在默认位置,或你使用了 puppeteer-core,则必须传入 executablePath/channel

const browser = await puppeteer.launch({
  channel: 'chrome', // 使用系统标准位置安装的 Chrome
  // executablePath: '/usr/bin/google-chrome', // 或直接指定二进制路径
  headless: true, // 默认即无头模式
});

launch 的完整参数与语义见 puppeteer.puppeteernode.launch.mdpuppeteer.launchoptions.md

除了启动自有进程,还可以通过 puppeteer.connect 连接一个已经运行的远程浏览器(例如在 CI 中由独立进程管理的浏览器,或 Docker 中的调试端口),返回的同样是 Browser 实例,后续 API 完全一致:

const browser = await puppeteer.connect({browserWSEndpoint: 'ws://...'});

关于两种入口的取舍,仓库内 examples/cross-browser.js 演示了如何针对不同浏览器分别执行任务,examples/webdriver-bidi.mjs 则展示了基于 WebDriver BiDi 协议的新一代连接方式。

导航与视口:goto 与 setViewport

await page.goto('https://developer.chrome.com/');
await page.setViewport({width: 1080, height: 1024});
  • page.goto(url) 负责页面级导航并等待加载完成,返回 HTTPResponse;它是 page.goto 在 Page 上的封装,深层语义对应 Frame.goto(见 puppeteer.frame.goto.md),并支持 waitUntiltimeoutreferer 等选项(puppeteer.gotooptions.md)。
  • page.setViewport({width, height}) 指定页面渲染的视口尺寸。示例中特意设成 1080×1024,是为了保证后面通过键盘按下 / 弹出的搜索框、以及搜索结果列表在当前视口内可见可点,这属于可访问性自动化的常见调优手段。

导航阶段的等待策略、超时与重试细节可进一步参考 docs/guides/page-interactions.mdpuppeteer.waitforoptions.md

键盘驱动的 UI 进入:keyboard.press

await page.keyboard.press('/');

很多站点(尤其是文档站)注册了 / 快捷键来聚焦搜索框。Keyboard.press 组合了"按下→触发→抬起"的完整键击序列,其完整能力(含 keytext、修饰键组合等)见 puppeteer.keyboard.md。这里它替代了"用鼠标找到搜索图标再点击"这类脆弱步骤——因为快捷键在页面任何位置都生效,为后续的 Locator 查询提供了稳定前提。

定位元素:Locator 与 ::-p- 查询引擎

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);

这是示例中最值得深入的部分。page.locator(selector) 返回 Locator(实现位于 packages/puppeteer-core/src/api/locators/locators.ts),它把"查找元素 + 等待可交互 + 执行动作 + 失败重试"等自动化中最容易踩坑的逻辑收敛为一个可链式调用的对象。示例中按动作顺序使用了三种:

  • ::-p-aria(Search):按可访问名称(ARIA name)定位,是表单输入框这类"没有稳定文本、CSS 又易变"的元素的首选方案。对应实现见 injected/ARIAQuerySelector.ts
  • .devsite-result-item-link:普通 CSS 选择器,直接透传给页面自身的 querySelectorAll,用于点击结构稳定的第一条结果。
  • ::-p-text(Customize and automate):按可见文本内容定位标题所在的文本节点,配合 .waitHandle() 等待元素出现并返回可执行 evaluate 的句柄。文本匹配实现见 injected/TextQuerySelector.ts

这些 ::-p- 前缀选择器并不是浏览器原生语法,而是由 Puppeteer 注入到页面里的 PSelector 查询引擎负责解析执行的。核心分发逻辑在 injected/PQuerySelector.ts 中:引擎逐段消费复合选择器,遇到 text/xpath/aria 分别路由到对应的查询器,遇到自定义名字则查 customQuerySelectors 表并抛出 Unknown selector type;它还支持两组深组合子——>>>(后代穿透,可跨 open shadow root)与 >>>>(直接子节点穿透)。这意味着 ::-p- 查询天然具备穿透 Shadow DOM 的能力,这是普通 CSS 选择器做不到的。

示例中 textSelector?.evaluate(el => el.textContent) 是典型的"拿到句柄后在页面上下文里执行函数"模式:evaluate 的参数会被序列化到浏览器端执行,返回 Promise 后再回传 Node 端(可参考 JSHandle.evaluatepuppeteer.evaluatefunc.md)。

Locator 的等待与点击行为可配置:例如 .setTimeout() 控制超时、click 前默认确保元素在视口内且稳定。更多动作(fill/click/hover/wait 及选项)见 puppeteer.locator.mdpuppeteer.locator.fill.mdpuppeteer.locatorclickoptions.md

收尾:打印结果与释放浏览器

console.log('The title of this blog post is "%s".', fullTitle);
await browser.close();

browser.close() 会关闭浏览器进程并断开连接,是脚本必须的收尾动作,否则 Node 进程可能因残留的子进程而无法退出。对于更长期的自动化任务,browser.disconnect()(断开但保留浏览器继续运行)则对应另一类场景,详见 puppeteer.browser.close.mdpuppeteer.browser.disconnect.md

运行脚本

保存上述代码后直接运行(若用 TypeScript 需先转译或使用支持 TS 的运行时):

node search-blog.mjs
# 或 npx tsx search-blog.ts

预期输出为抓取到的文章完整标题,例如 The title of this blog post is "Customize and automate..."。将 URL、关键词与选择器替换为你自己的目标站点即可复用到真实场景。

从第一个脚本继续深入

看完上面这 20 多行代码,你已经掌握了 Puppeteer 的最小可用闭环。围绕同一套 Browser → Page → Locator 模型,可以继续扩展的能力包括:

仓库 examples/ 目录还提供了从截图、PDF、图片拦截到扩展/浏览器内运行 Puppeteer 等大量可直接运行的最小示例,适合作为逐个功能实验的起点。结合 docs/guides/installation.mdpuppeteerpuppeteer-core 的取舍说明,你可以根据"是否需要自动下载浏览器""是否连接远程浏览器"这两个问题,快速为自己的项目选定正确的包与启动方式。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391