模拟浏览器操作神器Puppeteer初探
介绍
刚接触到puppeteer,并不是用来做爬虫,只是想做一个后台数据推送及性能数据抓取的功能,需要模拟登陆内部系统爬取数据截图。一开始想到利用很多方案,像wkhtmltopdf, phantomjs等等,他们都是类浏览器的环境,因为之前使用过,都知道他们或多或少都存在些问题。再后来发现了headless,谷歌的无头浏览器服务,这个最接近真实浏览器的东西(应该说就是个浏览器吧),然后决定采用headless,网上最多的是采用lighthouse作为操作headless的nodejs接口工具,后来看到puppeteer,和lighthouse差不多,但毕竟是谷歌团队自己维护的工具,当然就选择了它,后来看了看,果然还是它用起来比较方便。
官方对于puppeteer的介绍(翻译过来):
puppeteer由Chrome团队开发的Node库。它提供了一个高级API来控制无头(或完整)Chrome。它与Phantom和NightmareJS等其他自动化测试库类似,但它只适用于最新版本的Chrome。
除此之外,Puppeteer还可用于轻松截取屏幕截图,创建PDF,导航页面以及获取有关这些页面的信息。如果您想快速自动化浏览器测试,我建议使用该库。它隐藏了DevTools协议的复杂性,并负责启动Chrome的调试实例等冗余任务>
看起来很强大对不对,实时上除了无头模式,Puppeteer也可以操作真实浏览器进行工作,你会发现它除了可以做完美的爬虫之外,对于自动化的ui测试也是利器。目前也有了中文文档,使用起来也是极其方便的
截图
让我们先来看一下它的api,首先是puppteer对象,用于启动一个Chromium实例,例子:
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.goto('https://www.google.com');
// 其他操作...
await browser.close();
});
上图实际是打开了一个浏览器应用,lauch方法支持一个对象类型的参数,其中可以指定headless为true还是false,当指定为true是,可以观察到实际行为,在我们测试的时候还是很有用处的,当我们设置为true是可以观察到,browser对象针对的是整个浏览器窗口,而page对象针对的是每个tab页签,当关闭browser对象时,所有page对象都会被销毁,如果想要单独销毁page对象可以调用page.close();
在实际应用中,page对象是我们最常用的对象,下面是使用page对象打开一个页面并进行截图的例子:
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.goto('https://www.baidu.com');
// 截图...
await page.screenshot({path:'xx/xx.png',type:'png',quality:100,fullPage:true});
await browser.close();
});
这里介绍下截图的几个参数:
- path:图片存储路径
- type: 图片格式,默认png
- quality: 图片质量0-100的值,png格式无效
- fullpage: 是否截取整个页面,如果不设置,截取的是窗口打开部分,并不是全页面,默认打开的窗口是800x600大小,如果想截取更完美的大小需要调整视窗大小,通过设置launch的defaultViewport参数。
性能数据获取
puppteer也支持我们在打开页面时进行一些性能数据的采集,page.metrics()方法可以帮我们获取到页面打开过程中的性能参数:
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.goto('https://www.target.com');
// 获取页面性能数据...
const result = await page.metrics();
console.log(result);
await browser.close();
});
返回结果包括:
- Timestamp <number> 时间点(when the metrics sample was taken)
- Documents <number> 页面的documents数量。
- Frames <number> 页面的iframe数量。
- JSEventListeners <number> 页面的js事件数量。
- Nodes <number> 页面的dom节点数量。
- LayoutCount <number> 整页面或部分页面的布局数量。
- RecalcStyleCount <number> 页面样式重新计算数量。
- LayoutDuration <number> 页面布局总时间。
- RecalcStyleDuration <number> 页面样式重新计算总时间。
- ScriptDuration <number> 页面js代码执行总时间。
- TaskDuration <number> 页面任务执行总时间。
- JSHeapUsedSize <number> 页面占用堆内存大小。
-
JSHeapTotalSize <number> 总的页面堆内存大小。
还有另外一个api,用于获取我们平时利用浏览器观察页面加载性能的performance视图,它会生成和谷歌浏览器中profile.json一样的一个json数据,我们可以导入浏览器中查看
屏幕快照 2019-04-13 22.14.01.png
相关代码:
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.tracing.start({path: 'trace.json'});
await page.goto('https://www.target.com');
await page.tracing.stop();
await browser.close();
});
我们拿到.json文件后也可以访问DevTools Timeline Viewer导入去查看页面加载的详细信息。
那么对于性能数据来说,我们最常用的手机api就是performance.timing这个对象了,那么我们如果在puppteer中去获取并收集数据呢?下面看一个例子:
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.goto('https://www.target.com',{
waitUntil:'networkidle0'
});
const timing = await page.evaluate(() => (window.performance.timing.toJSON()));
console.log(timing);
await browser.close();
});
上面的goto方法内我加了一个参数networkidle0,意思是不再有任何网络连接时触发(至少500毫秒后)后才算页面跳转完成,这么做是为了等页面完全加载结束,获得最准确的数据,在evaluate方法内可以使用page对象内的上下文,也就是当前页面的dom对象都是可以使用的,这样就拿到了timing数据。
最后
除此之外,也可以利用puppteer的Coverage对象获取js以及css的代码覆盖率,这里就不做过多介绍了,感兴趣的同学可以自行研究。
参考文章:https://developers.google.com/web/updates/2017/04/headless-chrome
https://zhaoqize.github.io/puppeteer-api-zh_CN/#/
网友评论