「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
Puppeteer是什么
谷歌浏览器在17年自行开发了Chrome Headless特性,并与之同时推出了Puppeteer。Puppeteer 是一个 Node 库,可以通过它所提供的 API 来控制Chromium 或 Chrome,所以可以理解成我们日常使用的Chrome的无界面版本以及对其进行操控的js接口套装。
什么是 Headless Chrome
Headless Chrome 是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有 Chrome 支持的特性运行你的程序。相比于现代浏览器,Headless Chrome 更加方便测试 web 应用,获得网站的截图,做爬虫抓取信息等。相比于出道较早的 PhantomJS,SlimerJS 等,Headless Chrome 则更加贴近浏览器环境。
Puppeteer的作用
- 网页截图,生成页面 PDF。
- 抓取 SPA(单页应用)并生成预渲染内容(即“SSR”(服务器端渲染))。
- 自动提交表单,进行 UI 测试,键盘输入等。
- 创建一个时时更新的自动化测试环境。 使用最新的 JavaScript 和浏览器功能直接在最新版本的Chrome中执行测试。
- 捕获网站的 timeline trace,用来帮助分析性能问题。
- 测试浏览器扩展。
安装
当你安装 Puppeteer 时,它会下载最新版本的Chromium
npm i puppeteer
or
yarn add puppeteer
自 1.7.0 版本以来,官方都会发布一个 puppeteer-core 包,是一个的轻量级的 Puppeteer 版本,用于启动现有浏览器安装或连接到远程安装。
npm i puppeteer-core
or
yarn add puppeteer-core
简单使用
使用Browser 创建 Page 的例子
当 Puppeteer 连接到一个 Chromium 实例的时候会通过 puppeteer.launch 或 puppeteer.connect 创建一个 Browser 对象
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
});
通过 node 运行这个文件后,会看不到效果,因为是 headless 无界面模式。
页面导航
- page.goto:打开新页面
- page.goBack :回退到上一个页面
- page.goForward :前进到下一个页面
- page.reload :重新加载页面 为了方便我们观察,我们关闭 headless 无界面模式,且放慢执行速度
// puppeteer.js
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({
headless: false, //有浏览器界面启动
slowMo: 200,
defaultViewport: { width: 1400, height: 900 },
args: ["–no-sandbox", "--window-size=1400,900"], //全屏打开页面
});
const page = await browser.newPage();
await page.goto("https://www.baidu.com");
await page.goto("https://www.zhihu.com/");
await page.goBack(); // 回退
await page.goForward(); // 前进
await page.reload(); // 刷新
await page.close();
await browser.close();
})();
使用 node puppeteer.js 执行后,会跳出浏览器界面,先访问 baidu 再访问 zhihu,再进行后退和前进操作,然后刷新,最后关闭页面和浏览器。
应用
创建pdf
以下代码先跳转到百度页面,然后进行截图,生成一个叫 hn.pdf 的pdf文件
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.baidu.com', {waitUntil: 'networkidle2'});
await page.pdf({path: 'hn.pdf', format: 'A4'});
await browser.close();
})();
打开pdf文件
截图
截图如果要对整个页面进行截图,则使用page.screenshot即可,如果是对其中某个元素进行截图,则需要先通过page.$()获取到这个元素element,再执行element.screenshot
// puppeteer.js
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({
headless: false, //有浏览器界面启动
defaultViewport: { width: 1400, height: 900 },
args: ["--start-fullscreen"], //全屏打开页面
});
const page = await browser.newPage();
await page.goto("https://juejin.cn/", { waitUntil: "networkidle0" });
//对整个页面截图
await page.screenshot({
path: "./temp/capture.png", //图片保存路径
type: "png",
fullPage: true, //边滚动边截图
// clip: {x: 0, y: 0, width: 1920, height: 800} // 指定裁剪区域
});
//对页面某个元素截图
let element = await page.$('.logo');
await element.screenshot({
path: './temp/element.png'
});
await page.close();
await browser.close();
})();
在页面中执行脚本
想要在页面中执行脚步通过 page.evaluate(pageFunction, [...args]),他会返回pageFunction 执行的结果
const puppeteer = require('puppeteer');
(async () => {
// const browser = await puppeteer.launch();
const browser = await puppeteer.launch({
headless: false, //有浏览器界面启动
defaultViewport: { width: 1400, height: 900 },
args: ["--start-fullscreen"], //全屏打开页面
});
const page = await browser.newPage();
await page.goto('https://juejin.cn');
const dimensions = await page.evaluate(() => {
alert(1); // 弹窗
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
console.log('Dimensions:', dimensions);
await page.close();
await browser.close();
})();
这段代码的效果是:打开 juejin 然后执行 page.evaluate 传入的函数,因为这个函数执行了 alert(1),所以会有弹窗弹出,最后在命令行中打印出返回的数据Dimensions。
pageFunction 是运行再所打开页面的上下文中,所以我们可以在里面做一些很灵活的操作来测试我们的页面。除了page.evaluate方法,类似的还有page.evaluateOnNewDocument方法。
页面交互
这个功能非常实用,我再进行自动化测试时,肯定会对页面进行一些交互,主要用的API:
- page.$(selector) 此方法在页面内执行 document.querySelector,获取到元素节点
- elementHandle.type(text[, options]) 聚焦元素,然后为文本中的每个字符发送 keydown,keypress / input 和 keyup 事件。
- elementHandle.click([options]) 点击对应的元素。
通过这些API我们可以模拟一下掘金的登录
// login
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({
headless: false, //有浏览器界面启动
slowMo: 100,
defaultViewport: { width: 1400, height: 900 },
args: ["--start-fullscreen"], //全屏打开页面
});
const page = await browser.newPage();
await page.goto("https://juejin.cn/");
await page.waitForSelector("button.login-button");
const showLoginModalBtnEle = await page.$("button.login-button");
await showLoginModalBtnEle.click();
// 登录弹窗已渲染
await page.waitForSelector("form.auth-form");
// 点击 其他登录方式
const otherLoginTypeEle = await page.$("span.clickable")
await otherLoginTypeEle.click()
// 输入账号
const accountInputEle = await page.waitForSelector("input[name='loginPhoneOrEmail']");
await accountInputEle.type("ceshi@qq.com", { delay: 20 });
// 输入密码
const pwdInputEle = await page.$("input[name='loginPassword']");
await pwdInputEle.type("woshimima111", { delay: 20 });
// 点击 登录 按钮
const submitBtnEle = await page.$("form.auth-form .btn");
await submitBtnEle.click();
await page.waitForTimeout(2000);
await page.close();
await browser.close();
})();
这段代码执行后他就会自动弹窗登录框,切换到密码登录,然后输入账号和密码,最后点击登录
模拟器
模拟iPhone 6设备,打开淘宝
const puppeteer = require("puppeteer");
const iPhone = puppeteer.devices["iPhone 6"]; // puppeteer.devices内置大量设备的预设定值
//使用 puppeteer.launch 启动 Chrome
(async () => {
const browser = await puppeteer.launch({
headless: false, //有浏览器界面启动
});
const page = await browser.newPage();
await page.emulate(iPhone);
await page.goto("https://www.taobao.com", { waitUntil: "networkidle0" });
await browser.close();
})();
总结
通过 Puppeteer 我们可以进行页面自动化性能测试,目前已经有了很多基于 puppeteer 的项目,比如:jest-puppeteer、mocha-headless-chrome、 expect-puppeteer 等等