Puppeteer 指南

流浪小猫的博客

Puppeteer 是 Google Chrome 出品的一个无头浏览器。如果你听说过 Phantomjs 或者 Selenium,那么就应该知道它是做什么的了。Puppeteer 与它们类似,提供了一系列 api,通过 DevTools 协议控制 Chromium/Chrome 浏览器的行为。

什么是无头浏览器

无头浏览器就是没有用户界面的浏览器,即通过写脚本来使用无头浏览器访问网站,还可以做一些点击等行为。

Puppeteer 一般使用无头的模式运行,这样的开销较小。当然也提供了使用完整的 Chromium/Chrome 来运行的模式。

Puppeteer 能做什么

能够做几乎所有浏览器能做的事情。

  • 网页截图,或生成 pdf
  • 爬取 SPA 或 SSR 网站
  • 自动化表单提交,UI测试,键盘输入等
  • 创建一个最新的自动化测试环境。使用最新的 js 和最新的 Chrome 浏览器运行测试用例
  • 捕获网站的时间线,帮助诊断性能问题
  • 测试 Chrome 插件

Puppeteer 与其他无头浏览器有什么区别?

  • Puppeteer 由 Google Chrome 维护,速度快、安全、稳定、易用
  • 其他无头浏览器可以支持多种浏览器环境(Safari, Chrome, Firefox 等),而 Puppeteer 只支持 Chromium/Chrome
  • Puppeteer 有完善的事件系统,不需要频繁的 sleep(1000) 了
  • Puppeteer 的调试功能很强大,还支持在 DevTools 里面调试
  • Puppeteer 能够创建一个「真实」的行为,如点击

安装 Puppeteer

先创建一个测试用的项目,执行 npm init 初始化好 package.json,然后执行以下命令安装 Puppeteer:

npm install puppeteer --save-dev

万事开头难,第一步安装时就会遇到问题(如果没有报错,请跳过这一段)。

Puppeteer 安装过程中会去下载 Chromium,墙内用户则会报错。如果你看到以下信息,说明是下载 Chromium 时连接不上。

ERROR: Failed to download Chromium r588429! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" env variable to skip download.
Error: Download failed: server returned code 502. URL: https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/588429/chrome-win32.zip

或者

ERROR: Failed to download Chromium r588429! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" env variable to skip download.
{ Error: connect ETIMEDOUT 172.217.24.48:443

如提示所说,设置 PUPPETEER_SKIP_CHROMIUM_DOWNLOAD 可以跳过安装 Chromium。

PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 npm install puppeteer --save-dev

此时可以安装成功,但是使用 Puppeteer 时会由于找不到 Chromium 而报错。可以创建一个文件 test.js,内容如下:

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

然后执行 node test.js 则会报错:

$ node test.js
(node:18368) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Chromium revision is not downloaded. Run "npm install" or "yarn install"
(node:18368) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

一个解决方案是先手动下载 Chromium,然后在执行时通过配置指定 Chromium 位置,这篇文章给出了解决步骤。

但是我更倾向于还原 Puppeteer 安装时的过程。但是由于一些环境原因,即便翻墙了也只能手动下载 Chromium,无法在安装 Puppeteer 时自动下载 Chromium。

读了一下源码之后,可以这么解决:

  1. 安装 Puppeteer,安装失败,提示无法下载 https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/588429/chrome-win32.zip
  2. 使用 PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 npm install puppeteer --save-dev 成功安装 Puppeteer
  3. 执行 node test.js 提示无法找到 Chromium
  4. 手动下载步骤 1 中的 chrome-win32.zip,注意不同版本、不同系统的下载地址是不一样的
  5. 开启一个静态文件服务,使得 http://127.0.0.1:8000/chrome-win32.zip 指向需要下载的文件
  6. 改写 downloadURL 方法,使其直接返回 http://127.0.0.1:8000/chrome-win32.zip
  7. 执行 node ./node_modules/puppeteer/install.js 完成安装
  8. 执行 node test.js 测试能否成功生成截图

至此,成功完成了 Puppeteer 的安装。

Puppeteer 结构

Puppeteer 通过 DevTools 协议控制 Chromium/Chrome 浏览器。它的结构和浏览器结构类似。

下图中淡化显示的可以忽略

Puppeteer 结构

  • Puppeteer 通过 DevTools 协议控制 Chromium/Chrome 浏览器
  • 一个浏览器(Browser)实例可以包含多个浏览器上下文(Browser contexts),就像我们打开一个普通的 Chrome 之后又打开一个隐身模式的 Chrome
  • 一个浏览器上下文(BrowserContext)可以包含多个页面(Pages)
  • 一个页面(Page)包含至少一个主 frame,也可以包含其他 frames(在主 frame 中通过 iframe 或 frame 标签创建的)
  • 一个 frame 包含至少一个执行上下文(Execution context),也可以包含其他执行上下文(由 Chrome 插件创建的)
  • 一个 Workder 包含一个执行上下文,由 WebWorker 创建

Puppeteer API

Puppeteer 的大部分 API 的返回值都是 Promise,故推荐使用 async await 来处理异步操作。Puppeteer 的 API 包含以下类:

类名描述Puppeteer主要用于创建一个浏览器实例,也可以用来下载新的 Chromium,或者设置浏览器的默认参数BrowserFetcher用于下载和管理 ChromiumBrowser可以创建一个或多个 PageBrowserContext创建一个隐身模式的浏览器时需要用到Page主要 API,用于操作一个页面,后面会详细介绍Worker用于处理 WebWorkerKeyboard可以触发键盘按键Mouse可以触发鼠标动作TouchScreen可以触发触摸屏的动作Tracing用于分析性能Dialog存在于 page 的 dialog 事件回调中,表示调用弹窗后的对象,包括 alert, beforeunload, confirm 和 promptConsoleMessage存在于 page 的 console 事件回调中,表示调用 console.log 等方法后的对象Frame常用于处理包含多个 frame 的页面。page 中的很多方法就是直接调用的主 frame 的方法ExecutionContext执行上下文存在于 frame、浏览器插件、worker 中。可以用来直接执行一段 jsJSHandle通过 page.evaluateHandle 生成,用于将页面中的 handler 挑出来传递使用ElementHandle通过 page.$ 生成,用于将页面中某个元素的 handler 挑出来传递使用Request在 page.setRequestInterception 方法中使用,可以处理页面的请求Response表示页面接收到的响应SecurityDetails表示页面的安全信息Target可以是 page, background_page, service_worker, browser 等CDPSession用于直接和 Devtools 通信Coverage用于分析 js 和 css 的代码被页面使用的比例TimeoutError超时错误Page

Page 是 Puppeteer 中最重要的一个 API,也是它的核心所在,这里会介绍一些常用的 Page API。

设置页面环境方法名描述page.emulate设置 viewport 和 uapage.setViewport设置 viewportpage.setUserAgent设置 uapage.setRequestInterception中断所有请求,并可以修改请求的返回值page.addScriptTag添加 js 脚本page.addStyleTag添加 csspage.setContent设置整个 htmlpage.setCacheEnabled设置缓存是否开启page.setExtraHTTPHeaders设置额外的 http 头page.setGeolocation设置地理位置page.setJavaScriptEnabled设置 js 是否开启page.setOfflineMode设置离线模式page.deleteCookie删除 cookiespage.setCookie设置 cookies模拟动作

一般会先滚动视窗到相应元素那,再执行动作。

方法名描述page.click点击page.tap手指点击page.focus聚焦page.hoverhoverpage.type在指定元素中输入内容page.select选中 <select> 的某个选项等待方法名描述page.waitFor等待某个元素渲染出来,或者某个函数执行之后返回 true,或者直接等待指定的时间page.waitForSelector等待某个元素被渲染page.waitForFunction等待某个函数执行之后返回 truepage.waitForNavigation等待页面跳转page.waitForRequest等待某个特定的请求被发出page.waitForResponse等待某个特定的请求收到了回应执行脚本方法名描述page.$使用 document.querySelector 获取结果,会返回 ElementHandle,可以传递使用page.$$同上,不过使用的是 document.querySelectorAllpage.$eval将 document.querySelector 获取的结果传递给 pageFunctionpage.$$eval同上,不过使用的是 document.querySelectorAllpage.evaluate直接执行脚本page.evaluateHandle执行脚本,返回的是 JSHandle,可以传递使用page.evaluateOnNewDocument在下个 frame 执行脚本page.exposeFunction将函数注入到 window 对象上page.queryObjects获取所有属于这个类的对象,可以传递使用页面跳转方法名描述page.goto跳转页面page.close关闭page.goBack后退page.goForward前进page.reload刷新page.setDefaultNavigationTimeout设置页面跳转的超时时长获取内容方法名描述page.screenshot截屏page.pdf生成 pdfpage.content获取整个页面内容page.title获取页面 titlepage.url获取页面 urlpage.viewport获取页面 viewportpage.cookies获取 cookies事件事件名描述page.on('console')监听 console.log 等的调用page.on('dialog')监听页面的 alert, beforeunload, confirm 和 prompt 弹窗page.on('load')监听页面的加载page.on('domcontentloaded')监听页面 dom 加载完成page.on('pageerror')监听页面错误page.on('request')监听页面发送的请求page.on('requestfailed')监听失败的请求page.on('requestfinished')监听完成的请求page.on('response')监听页面接受到的响应命名空间

通过一些命名空间可以快速访问到该页面下的其他实例。

属性名描述page.keyboard访问到页面的 Keyboard 对象page.mouse访问到页面的 Mouse 对象page.touchscreen访问到页面的 TouchScreen 对象

参考

本文由 黑白世界4648 第一时间收藏到GET,原文来自 → blog.xcatliu.com

「GetParty」

关注微信号,推送好文章

微信中长按图片即可关注

更多精选文章

评论
微博一键登入