Puppeteer 是 Chrome 开发团队在 2017 年发布的一个 Node.js 包,同时还有 Headless Chrome。用来模拟 Chrome 浏览器的运行。它提供了高级API来通过 DevTools 协议控制无头 Chrome 或 Chromium ,它也可以配置为使用完整(非无头)Chrome 或 Chromium。
学习 Puppeteer 之前我们先来了解一下 Chrome DevTool Protocol 和 Headless Chrome。
Chrome DevTool Protocol 是什么
- CDP 基于 WebSocket,利用 WebSocket 实现与浏览器内核的快速数据通道。
- CDP 分为多个域(DOM,Debugger,Network,Profiler,Console…),每个域中都定义了相关的命令和事件(Commands and Events)。
- 我们可以基于 CDP 封装一些工具对 Chrome 浏览器进行调试及分析,比如我们常用的 “Chrome 开发者工具” 就是基于 CDP 实现的。
- 很多有用的工具都是基于 CDP 实现的,比如 Chrome 开发者工具,chrome-remote-interface,Puppeteer 等。
Headless Chrome 是什么
- 可以在无界面的环境中运行 Chrome。
- 通过命令行或者程序语言操作 Chrome。
- 无需人的干预,运行更稳定。
- 在启动 Chrome 时添加参数 --headless,便可以 headless 模式启动 Chrome。
- chrome 启动时可以加一些什么参数,大家可以点击这里查看。
总而言之 Headless Chrome 就是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有 Chrome 支持的特性运行你的程序。
Puppeteer 是什么
- Puppeteer 是 Node.js 工具引擎。
- Puppeteer 提供了一系列 API,通过 Chrome DevTools Protocol 协议控制 Chromium/Chrome 浏览器的行为。
- Puppeteer 默认情况下是以 headless 启动 Chrome 的,也可以通过参数控制启动有界面的 Chrome。
- Puppeteer 默认绑定最新的 Chromium 版本,也可以自己设置不同版本的绑定。
- Puppeteer 让我们不需要了解太多的底层 CDP 协议实现与浏览器的通信。
Puppeteer 能做什么
官方介绍:您可以在浏览器中手动执行的大多数操作都可以使用 Puppeteer 完成!示例:
- 生成页面的屏幕截图和PDF。
- 爬取 SPA 或 SSR 网站。
- 自动化表单提交,UI测试,键盘输入等。
- 创建最新的自动化测试环境。使用最新的JavaScript和浏览器功能,直接在最新版本的Chrome中运行测试。
- 捕获站点的时间线跟踪,以帮助诊断性能问题。
- 测试Chrome扩展程序。
- …
Puppeteer API 分层结构
Puppeteer 中的 API 分层结构基本和浏览器保持一致,下面对常使用到的几个类介绍一下:
- Browser: 对应一个浏览器实例,一个 Browser 可以包含多个 BrowserContext
- BrowserContext: 对应浏览器一个上下文会话,就像我们打开一个普通的 Chrome 之后又打开一个隐身模式的浏览器一样,BrowserContext 具有独立的 Session(cookie 和 cache 独立不共享),一个 BrowserContext 可以包含多个 Page
- Page:表示一个 Tab 页面,通过 browserContext.newPage()/browser.newPage() 创建,browser.newPage() 创建页面时会使用默认的 BrowserContext,一个 Page 可以包含多个 Frame
- Frame: 一个框架,每个页面有一个主框架(page.MainFrame()),也可以多个子框架,主要由 iframe 标签创建产生的
- ExecutionContext: 是 javascript 的执行环境,每一个 Frame 都一个默认的 javascript 执行环境
- ElementHandle: 对应 DOM 的一个元素节点,通过该该实例可以实现对元素的点击,填写表单等行为,我们可以通过选择器,xPath 等来获取对应的元素
- JsHandle:对应 DOM 中的 javascript 对象,ElementHandle 继承于 JsHandle,由于我们无法直接操作 DOM 中对象,所以封装成 JsHandle 来实现相关功能
- CDPSession:可以直接与原生的 CDP 进行通信,通过 session.send 函数直接发消息,通过 session.on 接收消息,可以实现 Puppeteer API 中没有涉及的功能
- Coverage:获取 JavaScript 和 CSS 代码覆盖率
- Tracing:抓取性能数据进行分析
- Response: 页面收到的响应
- Request: 页面发出的请求
Puppeteer 安装与环境
注意:在v1.18.1之前,Puppeteer至少需要Node v6.4.0。从v1.18.1到v2.1.0的版本依赖于Node 8.9.0+。从v3.0.0开始,Puppeteer开始依赖于Node 10.18.1+。若要使用 async / await,只有Node v7.6.0或更高版本才支持。
Puppeteer是一个node.js包,所以安装很简单:
npm install puppeteer
// 或者
yarn add puppeteer
npm 在安装 puppeteer 的时候可能会报错!这是由于外网导致,使用科学上网或者使用淘宝镜像 cnpm 安装可解决。
安装Puppeteer时,它将下载 Chromium 的最新版本。从1.7.0版开始,官方发布了该 puppeteer-core 软件包,默认情况下不会下载任何浏览器,用于启动现有的浏览器或连接到远程浏览器。需要注意安装的 puppeteer-core 版本与打算连接的浏览器兼容。
Puppeteer 使用
Case1: 截图
我们使用 Puppeteer 既可以对某个页面进行截图,也可以对页面中的某个元素进行截图:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
//设置可视区域大小,默认的页面大小为800x600分辨率
await page.setViewport({
width: 1920, height: 800});
await page.goto('https://www.baidu.com/');
//对整个页面截图
await page.screenshot({
path: './files/baidu_home.png', //图片保存路径
type: 'png',
fullPage: true //边滚动边截图
// clip: {x: 0, y: 0, width: 1920, height: 800}
});
//对页面某个元素截图
let element = await page.$('#s_lg_img');
await element.screenshot({
path: './files/baidu_logo.png'
});
await page.close();
await browser.close();
})();
我们怎么去获取页面中的某个元素呢?
page.$('#uniqueId')
:获取某个选择器对应的第一个元素page.$$('div')
:获取某个选择器对应的所有元素page.$x('//img')
:获取某个 xPath 对应的所有元素page.waitForXPath('//img')
:等待某个 xPath 对应的元素出现page.waitForSelector('#uniqueId')
:等待某个选择器对应的元素出现
Case2: 模拟用户操作
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
slowMo: 100, //放慢速度
headless: false, //开启可视化
defaultViewport: {
width: