很早很早之前,前端就有了对后端环境调用浏览器页面功能的需求,最多的应用场景有两个

  1. UI 自动化测试:摆脱手工浏览点击页面确认功能模式,使用接口自动化调用界面
  2. 爬虫:模拟页面真实渲染,解决内容异步加载等问题

市场上出现过很多优秀的解决方案,在 Puppeteer 出现之前最常用的是 PhantomJSselenium-webdriver,但两个库有个共同特点——环境安装复杂,API 调用不语义化。
2017 年 Chrome 团队连续放了两个大招 Headless Chrome 和对应的 NodeJS API Puppeteer,直接让 PhantomJS 和 Selenium IDE for Firefox 作者宣布没必要继续维护其产品

Puppeteer

如同其 github 项目介绍:Puppeteer 是一个通过 DevTools Protocol 控制 headless chrome 的 high-level Node 库,提供了高度封装、使用方便的 API 来模拟用户在页面的操作、对浏览器事件做出响应等

  • 生成页面 PDF、截图
  • 自动提交表单,进行 UI 测试,键盘输入等
  • 抓取单页应用并生成预渲染内容(另一种思路的 SSR)
  • 捕获网站的 timeline trace,用来帮助分析性能问题
  • 测试浏览器扩展

手动可以在浏览器上做的大部分行为 Puppeteer 都能通过 API 真实模拟
API 使用非常简单,看下官网对网页截图的示例

  1. const puppeteer = require('puppeteer');
  2. (async () => {
  3. const browser = await puppeteer.launch();
  4. const page = await browser.newPage();
  5. await page.goto('https://example.com');
  6. await page.screenshot({path: 'example.png'});
  7. await browser.close();
  8. })();

哲学

虽然 Puppeteer API 足够简单,但如果是从 webdriver 流转过来的同学会不适应,主要是在 webdirver 中操作网页更多的是从程序的视角,而在 Puppeteer 中网页浏览者的视角。举个例子,对一个表单的 input 做输入。
使用 webdriver 流程

  1. 通过选择器找到页面 input 元素
  2. 给元素设置值

    1. const input = await driver.findElement(By.id('kw'))
    2. await input.sendKeys('test')

    使用 Puppeteer 流程

  3. 光标应该 focus 到元素上

  4. 键盘点击输入
    1. await page.focus('#kw')
    2. await page.keyboard.type('test')
    甚至可以简化为一条语句:向 input 输入字符
    1. await page.type('#kw', 'test');
    可以看到 Puppeteer 的使用流程几乎是在模拟人的操作,在使用过程中可以感受区别,会发现 Puppeteer 的使用自然很多

安装

  1. npm i puppeteer

安装 Puppeteer 时会下载最新版本的 Chromium,从 1.7 开始 Puppeteer 每次发布还会有一个 puppeteer-core 发布,相对于 puppeteer 有两个区别

  1. puppeteer-core 不会自动安装 Chromium
  2. puppeteer-core 忽略所有的 PUPPETEER_* env 变量

    如同 Node.js 启动可以设置环境变量,puppeteer 也支持特定的环境变量

    • HTTP_PROXY, HTTPS_PROXY, NO_PROXY - 定义用于下载和运行 Chromium 的 HTTP 代理设置。
    • PUPPETEER_SKIP_CHROMIUM_DOWNLOAD - 请勿在安装步骤中下载绑定的 Chromium。
    • PUPPETEER_DOWNLOAD_HOST - 覆盖用于下载 Chromium 的 URL 的主机部分。
    • PUPPETEER_CHROMIUM_REVISION - 在安装步骤中指定一个你喜欢 puppeteer 使用的特定版本的 Chromium。
    • PUPPETEER_EXECUTABLE_PATH - 指定一个 Chrome 或者 Chromium 的可执行路径,会被用于 puppeteer.launch。具体关于可执行路径参数的意义,可参考puppeteer.launch([options])

参考资料

《9. Puppeteer: 更友好的 Headless Chrome Node API》