Puppeteer 谷歌开发是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。Puppeteer 默认以 headless 模式运行,但是可以通过修改配置文件运行“有头”模式。

能做啥?

  • 生成页面 PDF,截图。
  • 抓取单页应用执行并渲染
  • 自动提交表单,进行 UI 测试,键盘输入等。
  • 创建一个时时更新的自动化测试环境。
  • 用来帮助分析性能问题。

介绍

Puppeteer API 是分层次的,反映了浏览器结构



这张图需要了解一下,即是puppeteer的结构,也是代码的顺序结构;

// 浏览器实例
const puppeteer = require('puppeteer');
// 启动 返回 browser
const browser = await puppeteer.launch();
// 返回 page
const page = await browser.newPage();
// 跳转
await page.goto('https://www.baidu.com/');
// 返回page的frame
const frame = await page.mainFrame();
// 当前页面的url https://www.baidu.com/
console.log(frame.url());
// 关闭
await browser.close();
复制代码
  • Puppeteer 使用 DevTools 协议 与浏览器进行通信。
  • Browser 实例可以拥有浏览器。
  • BrowserContext 浏览器上下文。
  • Page 至少有一个框架:主框架。 可能还有其他框架由 iframe 或 框架标签 创建。
  • frame 至少有一个执行上下文 - 默认的执行上下文 - 框架的 JavaScript 被执行。 一个框架可能有额外的与 扩展 关联的执行上下文。
  • Worker 具有单一执行上下文,并且便于与 WebWorkers 进行交互。

安装

// 安装puppeteer,如果报错改用 cnpm 安装
cnpm install puppeteer --save
复制代码

常用函数

Puppeteer返回的大部分是Promise支持一下async/await会很方便; 每个功能方法都有很多参数配置,先列举部分

浏览器操作、打开页面

上方代码内容

跳转地址

await page.goto('https://google.com/', {
    // 配置项 等待网络状态为空闲的时候才继续执行
    waitUntil: 'networkidle'
});
复制代码

生成PDF,截图

await page.screenshot({ path: "download/example.png" });
await page.pdf({ 
    path: "download/example_pdf.pdf',
    format: "A4"
});
复制代码

等待

waitFor: 参数可以是<string|number|function> 选择器, 方法 或者 超时时间

// 等待1s
await page.waitFor(1000);
// 等待元素加载完成
await page.waitForSelector("#id");
复制代码

选择器

  • 返回的类型 ElementHandle 表示一个页内的 DOM 元素;
  • 是继承自 JSHandle。而 JSHandle 表示页面内的 JavaScript 对象
  • 其实是执行的document.querySelector,document.querySelectorAll
// 返回单个,如果多个返回第一个,没有返回null
const ElementHandle = await page.$("#js_login_select")
// 返回多个,如果没有返回[]
await page.$$(".js_login_select")
复制代码

page.$eval(selector, pageFunction[, ...args])

此方法在页面内执行 document.querySelector,然后把匹配到的元素作为第一个参数传给 pageFunction

// 获取html
const html = await page.$eval("body", e => e.outerHTML);
console.log(html);
// 获取id
const id = await page.$eval('#div',div => div.id );
//清空输入框的值,获取焦点
await page.$eval('#input',input => {
    input.focus();
    input.value = '';
})
复制代码

evaluate

页面实例上下文中执行的方法

const dimensions = await page.evaluate(param => {
    alert("我的方法dimensions和" + param);
    return {
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight,
        deviceScaleFactor: window.devicePixelRatio
    };
}, "参数");
复制代码

eval与evaluate区别

page.evaluate 意为在浏览器环境执行脚本,可传入第二个参数作为句柄,而 page.$eval 则针对选中的一个 DOM 元素执行操作。

注意:选择器过滤后得到的dom,都是通过执行querySelector或者querySelectorALl得到的,所以页面脚本能执行的方法属性,都可以在这里使用

-常用函数

性能分析

Puppetter当然可以分析网站的性能,不过是创建一个可以在Chrome DevTools中打开的跟踪文件。

// 每个浏览器一次只能激活一条跟踪
await page.tracing.start({
    path: "download/trace.json",
    screenshots: true
});
// 需要关闭
await page.tracing.stop();
复制代码

成功后会在path的位置生成.json的文件,里面一堆的数字和变量~ 打开chrome浏览器的devtools,拖进performance中就OK了~



请求拦截

Puppeteer还可以监听很多事件,load,error,close等等,当然包括request,response

对页面的图片请求拦截,如果不是图片后缀可以找规则筛选

// 启动 request
await page.setRequestInterception(true);
// 监听request
page.on("request", interceptedRequest => {
    if (
        interceptedRequest.url().endsWith(".png") ||    interceptedRequest.url().endsWith(".jpg") ||
        interceptedRequest.url().includes(".jpg")
    ) {
        // 中断
        interceptedRequest.abort();
    } else {
        interceptedRequest.continue();
    }
});
await page.goto("https://www.58pic.com/");
复制代码

注入

  1. addScriptTag:注入一个指定src(url)或者代码(content)的 script 标签到当前页面。
  2. addStyleTag:添加一个指定link(url)的 < link rel="stylesheet" > 标签。 或者添加一个指定代码(content)的 < style type="text/css" > 标签。
await page.addScriptTag({ path: "public/javascripts/test.js" });
await page.addStyleTag({ path: "public/css/test.css" });
复制代码

这样不同站点,只需要注入不同的脚本爬取

模拟浏览器

关于模拟不同的手机,需要不同的参数,Puppeteer准备了很多,可以直接拿来用,在puppeteer/DeviceDescriptors中

const page = await browser.newPage();
// 模拟机型
await page.emulate(iPhone);
复制代码