很早很早之前,前端就有了对后端环境调用浏览器页面功能的需求,最多的应用场景有两个
- UI 自动化测试:摆脱手工浏览点击页面确认功能模式,使用接口自动化调用界面
- 爬虫:模拟页面真实渲染,解决内容异步加载等问题
市场上出现过很多优秀的解决方案,在 Puppeteer 出现之前最常用的是 PhantomJS 和 selenium-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 使用非常简单,看下官网对网页截图的示例
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});
await browser.close();
})();
哲学
虽然 Puppeteer API 足够简单,但如果是从 webdriver 流转过来的同学会不适应,主要是在 webdirver 中操作网页更多的是从程序的视角,而在 Puppeteer 中网页浏览者的视角。举个例子,对一个表单的 input 做输入。
使用 webdriver 流程
- 通过选择器找到页面 input 元素
给元素设置值
const input = await driver.findElement(By.id('kw'))
await input.sendKeys('test')
使用 Puppeteer 流程
光标应该 focus 到元素上
- 键盘点击输入
甚至可以简化为一条语句:向 input 输入字符await page.focus('#kw')
await page.keyboard.type('test')
可以看到 Puppeteer 的使用流程几乎是在模拟人的操作,在使用过程中可以感受区别,会发现 Puppeteer 的使用自然很多await page.type('#kw', 'test');
安装
npm i puppeteer
安装 Puppeteer 时会下载最新版本的 Chromium,从 1.7 开始 Puppeteer 每次发布还会有一个 puppeteer-core 发布,相对于 puppeteer 有两个区别
- puppeteer-core 不会自动安装 Chromium
- 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])
。