深入理解Puppeteer的入门教程和实践


Posted in Javascript onMarch 05, 2019

出现的背景

Chrome59(linux、macos)、 Chrome60(windows)之后,Chrome自带headless(无界面)模式很方便做自动化测试或者爬虫。但是如何和headless模式的Chrome交互则是一个问题。通过启动Chrome时的命令行参数仅能实现简易的启动时初始化操作。Selenium、Webdriver等是一种解决方案,但是往往依赖众多,不够扁平。

深入理解Puppeteer的入门教程和实践

Puppeteer是谷歌官方出品的一个通过DevTools协议控制headless Chrome的Node库。可以通过Puppeteer的提供的api直接控制Chrome模拟大部分用户操作来进行UI Test或者作为爬虫访问页面来收集数据。

环境和安装

Puppeteer本身依赖6.4以上的Node,但是为了异步超级好用的async/await,推荐使用7.6版本以上的Node。另外headless Chrome本身对服务器依赖的库的版本要求比较高,centos服务器依赖偏稳定,v6很难使用headless Chrome,提升依赖版本可能出现各种服务器问题(包括且不限于无法使用ssh),最好使用高版本服务器。
Puppeteer因为是一个npm的包,所以安装很简单:

npm i puppeteer

或者

yarn add puppeteer

Puppeteer安装时自带一个最新版本的Chromium,可以通过设置环境变量或者npm config中的PUPPETEER_SKIP_CHROMIUM_DOWNLOAD跳过下载。如果不下载的话,启动时可以通过puppeteer.launch([options])配置项中的executablePath指定Chromium的位置。

使用和例子

Puppeteer类似其他框架,通过操作Browser实例来操作浏览器作出相应的反应。

const puppeteer = require('puppeteer');

(async () => {
 const browser = await puppeteer.launch();
 const page = await browser.newPage();
 await page.goto('http://rennaiqian.com');
 await page.screenshot({path: 'example.png'});
 await page.pdf({path: 'example.pdf', format: 'A4'});
 await browser.close();
})();

上述代码通过puppeteer的launch方法生成了一个browser的实例,对应于浏览器,launch方法可以传入配置项,比较有用的是在本地调试时传入{headless:false}可以关闭headless模式。

const browser = await puppeteer.launch({headless:false})

browser.newPage方法可以打开一个新选项卡并返回选项卡的实例page,通过page上的各种方法可以对页面进行常用操作。上述代码就进行了截屏和打印pdf的操作。
一个很强大的方法是page.evaluate(pageFunction, ...args),可以向页面注入我们的函数,这样就有了无限可能

const puppeteer = require('puppeteer');

(async () => {
 const browser = await puppeteer.launch();
 const page = await browser.newPage();
 await page.goto('http://rennaiqian.com');

 // Get the "viewport" of the page, as reported by the page.
 const dimensions = await page.evaluate(() => {
  return {
   width: document.documentElement.clientWidth,
   height: document.documentElement.clientHeight,
   deviceScaleFactor: window.devicePixelRatio
  };
 });

 console.log('Dimensions:', dimensions);
 await browser.close();
})();

需要注意的是evaluate方法中是无法直接使用外部的变量的,需要作为参数传入,想要获得执行的结果也需要return出来。因为是一个开源一个多月的项目,现在项目很活跃,所以使用时自行查找api才能保证参数、使用方法不会错。

调试技巧

1.关掉无界面模式,有时查看浏览器显示的内容是很有用的。使用以下命令可以启动完整版浏览器:

const browser = await puppeteer.launch({headless: false})

2.减慢速度,slowMo选项以指定的毫秒减慢Puppeteer的操作。这是另一个看到发生了什么的方法:

const browser = await puppeteer.launch({
 headless:false,
 slowMo:250
});

3.捕获console的输出,通过监听console事件。在page.evaluate里调试代码时这也很方便:

page.on('console', msg => console.log('PAGE LOG:', ...msg.args));
await page.evaluate(() => console.log(`url is ${location.href}`));

4.启动详细日志记录,所有公共API调用和内部协议流量都将通过puppeteer命名空间下的debug模块进行记录

# Basic verbose logging
 env DEBUG="puppeteer:*" node script.js

 # Debug output can be enabled/disabled by namespace
 env DEBUG="puppeteer:*,-puppeteer:protocol" node script.js # everything BUT protocol messages
 env DEBUG="puppeteer:session" node script.js # protocol session messages (protocol messages to targets)
 env DEBUG="puppeteer:mouse,puppeteer:keyboard" node script.js # only Mouse and Keyboard API calls

 # Protocol traffic can be rather noisy. This example filters out all Network domain messages
 env DEBUG="puppeteer:*" env DEBUG_COLORS=true node script.js 2>&1 | grep -v '"Network'

爬虫实践

很多网页通过user-agent来判断设备,可以通过page.emulate(options)来进行模拟。options有两个配置项,一个为userAgent,另一个为viewport可以设置宽度(width)、高度(height)、屏幕缩放(deviceScaleFactor)、是否是移动端(isMobile)、有无touch事件(hasTouch)。

const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6'];

puppeteer.launch().then(async browser => {
 const page = await browser.newPage();
 await page.emulate(iPhone);
 await page.goto('https://www.example.com');
 // other actions...
 await browser.close();
});

上述代码则模拟了iPhone6访问某网站,其中devices是puppeteer内置的一些常见设备的模拟参数。

很多网页需要登录,有两种解决方案:

1、让puppeteer去输入账号密码

常用方法:点击可以使用page.click(selector[, options])方法,也可以选择聚焦page.focus(selector)。
输入可以使用page.type(selector, text[, options])输入指定的字符串,还可以在options中设置delay缓慢输入更像真人一些。也可以使用keyboard.down(key[, options])来一个字符一个字符的输入。

2、如果是通过cookie判断登录状态的可以通过page.setCookie(...cookies),想要维持cookie可以定时访问。

Tip:有些网站需要扫码,但是相同域名的其他网页却有登录,就可以尝试去可以登录的网页登录完利用cookie访问跳过扫码。

简单例子

const puppeteer = require('puppeteer');

(async () => {
 const browser = await puppeteer.launch({headless: false});
 const page = await browser.newPage();
 await page.goto('https://baidu.com');
 await page.type('#kw', 'puppeteer', {delay: 100});
 page.click('#su')
 await page.waitFor(1000);
 const targetLink = await page.evaluate(() => {
  return [...document.querySelectorAll('.result a')].filter(item => {
   return item.innerText && item.innerText.includes('Puppeteer的入门和实践')
  }).toString()
 });
 await page.goto(targetLink);
 await page.waitFor(1000);
 browser.close();
})()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用javascript获得地址栏参数的两种方法
Nov 08 Javascript
js 模拟气泡屏保效果代码
Jul 10 Javascript
Jquery 跨域访问 Lightswitch OData Service的方法
Sep 11 Javascript
jquery查找父元素、子元素(个人经验总结)
Apr 09 Javascript
同一个网页中实现多个JavaScript特效的方法
Feb 02 Javascript
JS实现模拟风力的雪花飘落效果
May 13 Javascript
jquery插件unobtrusive实现片段式加载
Jun 15 Javascript
JSONP基础知识详解
Mar 19 Javascript
JS实现侧边栏鼠标经过弹出框+缓冲效果
Mar 29 Javascript
Js自定义多选框效果的实例代码
Jul 05 Javascript
JavaScript基础进阶之数组方法总结(推荐)
Sep 04 Javascript
通过封装scroll.js 获取滚动条的值
Jul 13 Javascript
[jQuery] 事件和动画详解
Mar 05 #jQuery
Vue 事件处理操作实例详解
Mar 05 #Javascript
Vue插槽原理与用法详解
Mar 05 #Javascript
JavaScript基于遍历操作实现对象深拷贝功能示例
Mar 05 #Javascript
JavaScript函数定义方法实例详解
Mar 05 #Javascript
C#程序员入门学习微信小程序的笔记
Mar 05 #Javascript
JavaScript函数的4种调用方法实例分析
Mar 05 #Javascript
You might like
dedecms 批量提取第一张图片最为缩略图的代码(文章+软件)
2009/10/29 PHP
PHP获取浏览器信息类和客户端地理位置的2个方法
2014/04/24 PHP
php数组合并与拆分实例分析
2015/06/12 PHP
WordPress中自定义后台管理界面配色方案的小技巧
2015/12/29 PHP
深入解析PHP中SESSION反序列化机制
2017/03/01 PHP
Yii Framework框架使用PHPExcel组件的方法示例
2019/07/24 PHP
javascript 函数速查表
2010/02/07 Javascript
js本身的局限性 别让javascript做太多事
2010/03/23 Javascript
基于JQuery的日期联动实现代码
2011/02/24 Javascript
打开新窗口关闭当前页面不弹出关闭提示js代码
2013/03/18 Javascript
css transform 3D幻灯片特效实现步骤解读
2013/03/27 Javascript
javasciprt下jquery函数$.post执行无响应的解决方法
2014/03/13 Javascript
js实现控制textarea输入字符串的个数,鼠标按下抬起判断输入字符数
2016/10/25 Javascript
分分钟玩转Vue.js组件(二)
2017/03/01 Javascript
jquery实现自定义图片裁剪功能【推荐】
2017/03/08 Javascript
AngularJS $http post 传递参数数据的方法
2018/10/09 Javascript
详解React服务端渲染从入门到精通
2019/03/28 Javascript
JavaScript判断数组类型的方法
2019/10/23 Javascript
微信小程序实现导航栏和内容上下联动功能代码
2020/06/29 Javascript
[40:03]RNG vs VG 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
[01:33:25]DOTA2-DPC中国联赛 正赛 Elephant vs IG BO3 第一场 1月24日
2021/03/11 DOTA
给Python初学者的一些编程技巧
2015/04/03 Python
Python类定义和类继承详解
2015/05/08 Python
python三大神器之fabric使用教程
2019/06/10 Python
Python 迭代,for...in遍历,迭代原理与应用示例
2019/10/12 Python
python使用hdfs3模块对hdfs进行操作详解
2020/06/06 Python
简单了解Python多态与属性运行原理
2020/06/15 Python
详解CSS3中nth-child与nth-of-type的区别
2017/01/05 HTML / CSS
css3弹性盒子flex实现三栏布局的实现
2020/11/12 HTML / CSS
印度尼西亚值得信赖的第一家网店:Bhinneka
2018/07/16 全球购物
路政管理毕业自荐书范文
2014/02/10 职场文书
2014年幼儿园元旦活动方案
2014/02/13 职场文书
幼儿园班级管理心得体会
2016/01/07 职场文书
婚前协议书怎么写,才具有法律效力呢 ?
2019/06/28 职场文书
python爬取某网站原图作为壁纸
2021/06/02 Python
一篇文章带你深入了解Mysql触发器
2021/08/02 MySQL