本文主要介绍木偶戏的详细介绍。边肖认为这很好。现在分享给大家,给大家一个参考。来和边肖一起看看吧。
1.木偶师简介
Puppeteer是一个节点库,它提供了一组用于操作Chrome的API。通俗点说就是一个无头的chrome浏览器(当然你也可以配置成有UI,默认是没有的)。既然是浏览器,Puppetteer就可以在浏览器上手动做我们能做的一切。另外,Puppetteer翻译成中文就是“木偶”的意思,所以你可以从名字上看出来,操纵起来非常方便,你可以很轻松地操纵她来实现:
1)生成网页截图或pdf。
2)高级爬虫,可以抓取大量异步渲染内容的网页。
3)模拟键盘输入、自动提交表单、登录网页等。实现UI自动测试。
4)捕获网站的时间线,以便跟踪您的网站并帮助分析网站性能问题。
如果你用过PhantomJS,你会发现它们有些相似,但Puppeteer是由Chrome官方团队维护的。俗话说,意思是“有家室的人”,前景更好。
2、运行环境
查看木偶师的官方API,你会发现全屏异步,等待等等。这些是ES7的规格,因此您需要:
Nodejs的版本不能低于v7.6.0,需要支持async,await。
你需要最新的chrome驱动,当你通过npm安装Puppeteer时会自动下载。
npm安装木偶师-保存
3、基本用法
我们先来看看官方的入门演示
const木偶师=require(木偶师);
(async ()={
const browser=await puppeter . launch();
const page=await browser . new page();
await page . goto( https://example . com );
await page .截图({ path: example . png });
await browser . close();
})();
上面的代码实现了网页的截图。首先,粗略解读上面几行代码:
首先,通过puppeteer.launch()创建一个浏览器实例Browser对象
然后通过Browser对象创建一个Page页面对象。
然后page.goto()跳转到指定的页面。
调用page.screenshot()对页面进行截图。
关闭浏览器。
你以为这么简单?反正我觉得比PhantomJS简单,更不用说和selenium-webdriver比了。以下是一些常见的木偶师API。
3.1 puppeteer.launch(options)
用puppeteer.launch()运行puppeteer,它会返回一个promise,并使用then方法获取浏览器实例。当然,nodejs的更高版本已经支持await特性,所以上面的例子使用了await关键字,需要特别说明。几乎所有木偶师的操作都是异步的。为了通过使用大量的then来降低代码的可读性,本文所有的演示代码都是用async和await实现的。这也是木偶师官方推荐的。在async/await看起来很傻的同学使劲戳这里。
选项参数详细解释
参数名称
参数类型
参数描述
ignoreHTTPSErrors
布尔型
请求过程中是否忽略Https错误信息,默认为false。
不在意的
布尔型
是否以无头模式运行chrome,即不显示UI,默认为真。
可执行路径
线
可执行文件的路径,Puppeteer默认使用自己的chrome webdriver。如果您想要指定自己的webdriver路径,可以通过此参数进行设置。
slowMo
数字
以毫秒为单位减慢木偶操作。如果你想看到木偶师的整个工作过程,这个参数会非常有用。
一个参数名
数组(字符串)
传递给chrome实例的其他参数,比如你可以用“ash-host-window-bounds=1024 x768”来设置浏览器窗口大小。更多参数列表,请参考这里。
handleSIGINT
布尔型
是否允许进程信号控制chrome进程,即是否可以使用Ctrl C关闭和退出浏览器.
超时
数字
等待Chrome实例启动的最长时间。默认值为30000(30秒)。如果传入0,则没有时间限制。
邓皮奥
布尔型
将浏览器进程stdout和stderr导入process.stdout和process.stderr.默认值为false。
用户数据目录
线
设置用户数据目录,默认linux在~/中。默认窗口位于C: users {USER} appdata local Google chrome userdata,其中{ USER }表示当前登录的用户名。
包封/包围(动词envelop的简写)
目标
指定对Chromium可见的环境变量。默认值为process.env
开发工具
布尔型
是否为每个选项卡自动打开DevTools面板,此选项仅在headless设置为false时有效。
3.2 Browser 对象
当Puppeteer连接到Chrome实例时,它将创建一个浏览器对象。有两种方法:
木偶师.启动和木偶师.连接
下面的演示实现了断开连接后重新连接浏览器实例。
const木偶师=require(木偶师);
puppeteer.launch()。然后(异步浏览器={
//保存端点,以便可以重新连接到Chromium。
const browserWSEndpoint=browser . ws endpoint();
//与Chromium断开连接
browser . disconnect();
//使用终结点重新建立与Chromiunm的连接
const browser 2=await puppeter . connect({ browserWSEndpoint });
//关闭铬
await browser 2 . close();
});
浏览器对象API
方法名称
返回值
解释
browser.close()
承诺
关闭浏览器。
browser.disconnect()
空的
断开浏览器。
browser.newPage()
承诺(第页)
创建页面实例
浏览器.页面()
Promise(Array(Page))
获取所有打开的页面实例。
浏览器.目标()
数组(目标)
获取所有活动目标。
浏览器.版本()
承诺(字符串)
获取浏览器的版本。
browser.wsEndpoint()
线
返回浏览器实例的套接字连接URL,通过它可以重新连接chrome实例。
好了,木偶师的API就不一一介绍了。官方提供的详细API,戳这里。
4、Puppeteer 实战
了解了API之后,我们就可以进行一些实战了。在此之前,我们先来了解一下Puppetteer的设计原理。简单来说,Puppetteer与webdriver和PhantomJS最大的区别在于,它是从用户浏览的角度出发的。webdriver和PhantomJS本来是做自动化测试的,所以是从机器浏览的角度来设计的,所以使用了不同的设计理念。举个栗子的例子。我需要打开JD.COM的主页,进行产品搜索,看看分别使用木偶师和webdriver的实现过程:
Puppeteer 的实现流程:
打开JD.COM的主页。
将光标放在搜索输入框上。
键盘点击输入文本。
单击搜索按钮。
webdriver 的实现流程:
打开JD.COM的主页。
找到输入框的输入元素
设置要搜索的文本的输入值。
单机事件触发搜索按钮
感觉木偶师的设计理念更符合任何操作习惯,更自然。
让我们使用一个简单的需求实现来开始使用木偶师。这个简单的要求是:
在JD.COM商城抓取10款手机产品,截图产品详情页。
我们先梳理一下操作流程。
打开JD.COM的主页。
输入关键词“手机”,搜索。
获取前10名产品的A标签,获取href属性值,获取产品详情的链接。
分别打开10个产品的详情页,抓取网页图片。
要实现以上功能,需要找到元素,获取属性,键盘事件等。那我们就一个一个来解释吧。
4.1 获取元素
Page对象提供了两个API来获取页面元素。
(1).页面。$(selector)获取单个元素,底层调用document.querySelector(),所以选择器的选择器格式遵循css选择器规范。
let inputElement=await page。$(#search ,input=input);
//下面的写法是等价的
let inputElement=await page。$( # search );
(2).页面。$$(选择器)获取一组元素,底层调用是document.querySelectorAll()。返回promise (array (Elementandle))元素的数组。
const links=await page。$ $( a );
//下面的写法是等价的
const links=await page。$$(a ,links=links);
最终返回的是ElemetHandle对象。
4.2 获取元素属性
木偶师获取元素属性的逻辑和我们通常在上一段写的js有点不同。按照通常的逻辑,应该是现在获取元素,然后获取元素的属性。但是,从上面我们知道,获取元素的API最终会返回Elementandle对象。如果你看看Elementandle的API,你会发现它没有获取元素属性的API。
事实上,Puppeteer提供了一组API来获取属性,Page。$eval()和Page。$$eval()
(1).页面。$ $ eval (selector,pagefunction [,… args])获取单个元素的属性。这里的选择器和上面的页面一样。$(选择器)。
常量值=等待页面。$eval(input[name=search],input=input . value);
const href=await page。$eval(#a ,ele=ele . href);
const content=await page。$eval(。content ,ele=ele . outer html);
4.3 执行自定义的 JS 脚本
Puppeteer的Page对象提供了一系列的evaluate方法,通过这些方法可以执行一些自定义的js代码,主要提供了以下三个API
(1).page.evaluate (pageFunction,… args)返回可序列化的普通对象。pageFunction表示页面上要执行的函数,args表示传入pageFunction的参数,后面的pagefunction和args表示相同的意思。
const result=await page . evaluate(()={
return promise . resolve(8 * 7);
});
console.log(结果);//打印 56
这个方法很有用。例如,当我们获取页面截图时,默认情况下只捕捉当前浏览器窗口的大小。默认值是800x600,所以如果我们需要获取整个网页的完整截图,就做不到了。Page.screenshot()方法提供了一个可以设置截图区域大小的参数,所以我们只需要在页面加载后获取页面的宽度和高度就可以解决这个问题。
(async ()={
const browser=await puppeter . launch({ headless:true });
const page=await browser . new page();
await page . goto( https://Jr . day i35 . com );
await page . set viewport({ width:1920,height:1080 });
const documentSize=await page . evaluate(()={
返回{
width:document . documentelement . client width,
高度:document.body.clientHeight,
}
})
await page .截图({path:example.png ,clip : {x:0,y:0,width:1920,height:documentsize . height } });
await browser . close();
})();
(2).page . evaluate handle(pageFunction,… args)在页面上下文中执行page function,并返回JSHandle实体。
const aWindowHandle=await page . evaluate handle(()=promise . resolve(window));
aWindowHandle//窗口对象的句柄。
const a handle=await page . evaluate handle( document );//用于“文档”的句柄。
从上面的代码可以看出,page.evaluateHandle()方法也是通过Promise.resolve方法直接返回Promise的最终处理结果,但是将最终返回的对象封装到一个JSHandle对象中。本质上,它与evaluate没有什么不同。
下面的代码实现了页面的动态HTML代码(包括js动态插入的元素)。
const a handle=await page . evaluate handle(()=document . body);
const result handle=await page . evaluate handle(body=body . innerhtml,a handle);
console . log(await result handle . JSON value());
await result handle . dispose();
(3).页面。evaluation connection Document(pageFunction,… args),它在加载文档页面之前调用page function。如果页面中有iframe或frame,函数调用的上下文将成为子页,即iframe或frame。由于是在页面加载之前调用的,所以这个函数一般用来初始化javascript环境,比如重置或者初始化一些全局变量。
4.4 Page.exposeFunction
除此上面三个应用程序界面之外,还有一类似的非常有用的API,那就是Page.exposeFunction,这个应用程序界面用来在页面注册全局函数,非常有用:
因为有时候需要在页面处理一些操作的时候需要用到一些函数,虽然可以通过Page.evaluate() API在页面定义函数,比如:
const docSize=await page。评估(()={
函数getPageSize() {
返回{
宽度:文档。documentelement。客户端宽度,
高度:document.body.clientHeight,
}
}
返回获取pagesize();
});
但是这样的函数不是全局的,需要在每个评价中去重新定义,无法做到代码复用,在一个就是nodejs有很多工具包可以很轻松的实现很复杂的功能比如要实现讯息摘要5加密函数,这个用纯射流研究…去实现就不太方便了,而用nodejs却是几行代码的事情。
下面代码实现给页上下文的窗户对象添加讯息摘要5函数:
常数木偶师=要求(木偶师);
const crypto=require( crypto );
木偶师. launch().然后(异步浏览器={
const page=await浏览器。新页面();
page.on(控制台,msg=控制台。日志(消息。正文));
await page.exposeFunction(md5 ,text=
crypto.createHash(md5 ).更新(文本)。摘要("十六进制")
);
await page.evaluate(async ()={
//使用window.md5计算哈希
const myString=木偶师;
const my hash=await窗口。MD5(myString);
console.log(`md5 of ${myString}是$ {我的哈希} `);
});
等待浏览器。close();
});
可以看出,Page.exposeFunction API使用起来是很方便的,也非常有用,在比如给窗户对象注册读取文件全局函数:
常数木偶师=要求(木偶师);
const fs=require( fs );
木偶师. launch().然后(异步浏览器={
const page=await浏览器。新页面();
page.on(控制台,msg=控制台。日志(消息。正文));
等待页面。公开函数( readfile ,异步文件路径={
返回新承诺((解决,拒绝)={
fs.readFile(filePath, utf8 ,(err,text)={
如果(错误)
拒绝(err);
其他
解决(文本);
});
});
});
await page.evaluate(async ()={
//使用window.readfile读取文件内容
常量内容=等待窗口。readfile(/etc/hosts );
console.log(内容);
});
等待浏览器。close();
});
5、Page.emulate 修改模拟器(客户端)运行配置
操纵木偶的人提供了一些应用程序界面供我们修改浏览器终端的配置
Page.setViewport()修改浏览器视窗大小
Page.setUserAgent()设置浏览器的用户代理信息
Page.emulateMedia()更改页面的半铸钢钢性铸铁(铸造半钢)媒体类型,用于进行模拟媒体仿真。可选值为"屏幕"、"打印"、"空",如果设置为空则表示禁用媒体仿真。
Page.emulate()模拟设备,参数设备对象,比如iPhone,Mac,Android等
page.setViewport({width:1920,height:1080 });//设置视窗大小为1920x1080
页面。setuser代理( Mozilla/5.0(X11;Linux x86 _ 64)苹果WebKit/537.36(KHTML,像壁虎)Chrome/60。0 .3112 .90 Safari/537.36’);
页面。模拟媒体(“打印”);//设置打印机媒体样式
除此之外我们还可以模拟非个人计算机;个人电脑;警察机设备,比如下面这段代码模拟iPhone 6访问谷歌:
常数木偶师=要求(木偶师);
const devices=require( puppet/device descriptor );
const iPhone=devices[ iPhone 6 ];
木偶师. launch().然后(异步浏览器={
const page=await浏览器。新页面();
等待页面。仿真(iPhone);
等待页面。转到( https://www。谷歌。com’);
//其他操作.
等待浏览器。close();
});
操纵木偶的人支持很多设备模拟仿真,比如Galaxy,iPhone,IPad等,想要知道详细设备支持,请戳这里设备描述符。
6、键盘和鼠标
键盘和鼠标的应用程序界面比较简单,键盘的几个应用程序界面如下:
键盘向下(按键[,选项])触发按键事件
键盘.按键(按键[,选项])按下某个键,键表示键的名称,比如向左箭头向左键,详细的键名映射请戳这里
keyboard.sendCharacter(char)输入一个字符
键盘.类型(文本,选项)输入一个字符串
键盘向上键触发击键事件
页面。键盘。按( Shift );//按下变化键
page . keyboard . send character( Hi );
page . keyboard . type( Hello );//一次输入完成。
page.keyboard.type(World ,{ delay:100 });//像用户一样慢慢输入
鼠标操作:
Mouse.click(x,y,[options])将鼠标指针移动到指定位置,然后按鼠标。这实际上是mouse.move和mouse.down或mouse.up的快捷操作
Mouse.down([options])触发mousedown事件,选项是可配置的:
Options.button哪个键被按下,可选值为【左、右、中】,默认值为left,表示鼠标左键。
Options.clickCount计算按下、单击、双击或其他次数。
延迟键延迟时间
Mouse.move(x,y,[options])将鼠标移动到指定位置,options.steps表示移动的步长。
Mouse.up([options])触发mouseup事件。
7、另外几个有用的 API
Puppeteer还提供了几个非常有用的API,例如:
7.1 Page.waitFor 系列 API
page . wait for(selectorfunctionortimeout[,options [,… args]])以下三个全面的API
Page.wait函数(pageFunction [,options [,args]])等待pagefunction完成。
Page.waitForNavigation(options)等待加载页面的基本元素,比如同步的HTML、CSS、JS等代码。
Page.wait for selector (selector [,options])在等待选择器的元素加载后,可以异步加载该元素。这个API很有用,你知道的。
比如我想获取一个通过js异步加载的元素,那么我就不能直接获取。这时候可以用page.waitForSelector来解决:
wait page.waitForSelector(。GL-item’);//等待元素加载,否则无法获得异步加载的元素。
const links=await page。$$eval(。总帐项目。包好。p-img a ,links={
return links.map(a={
返回{
href: a.href.trim(),
名称:a .标题
}
});
});
事实上,上面的代码可以解决我们的顶级需求,并抓取JD.COM的产品。因为它是异步加载的,所以使用此方法。
7.2 page.getMetrics()
通过page.getMetrics(),可以获取一些页面性能数据,捕捉网站的时间轴跟踪,帮助诊断性能问题。
时间戳度量样本的时间戳。
文件页数
框架页面框架编号
JSEventListeners页中事件侦听器的数量
节点页面DOM节点号
LayoutCount页面布局的总数
RecalcStyleCount样式重新计数。
LayoutDuration所有页面布局的总持续时间
所有页面样式重新计算的总持续时间。
所有脚本的持续时间。
任务持续时间所有浏览器任务的持续时间
JSHeapUsedSize JavaScript占用堆大小
JSHeapTotalSize JavaScript堆的总量
8、总结和源码
本文通过一个实际需求学习了一些基本的木偶师常用API,API的版本是v0.13.0-alpha。请参考木偶师的官方API,了解最新的API。
总的来说,木偶戏真的是一个很好的无头工具,操作简单,功能强大。可以用来做UI自动化测试,有些小工具很不错。
下面是我们开始的需求实现的源代码,仅供参考:
//延迟功能
功能睡眠(延迟){
返回新承诺((解决,拒绝)={
setTimeout(()={
尝试{
解决(1)
} catch (e) {
拒绝(0)
}
},延迟)
})
}
const木偶师=require(木偶师);
木偶师. launch({
ignoreHTTPSErrors:true,
无头:假,slowMo:250,
超时时间:0})。然后(异步浏览器={
let page=await browser . new page();
await page . set JavaScript enabled(true);
await page . goto( https://www . JD . com/);
const searchInput=await page。$( # key );
await search input . focus();//导航到搜索框
Await page.keyboard.type(手机);
const searchBtn=await page。$(.按钮’);
await search BTN . click();
wait page.waitForSelector(。GL-item’);//等待元素加载,否则获取未异步加载的元素。
const links=await page。$$eval(。总帐项目。包好。p-img a ,links={
return links.map(a={
返回{
href: a.href.trim(),
标题:a .标题
}
});
});
page . close();
const aTags=links.splice(0,10);
for(var I=1;i aTags.lengthi ) {
page=await browser.newPage()
页面。设置JavaScript enabled(true);
等待页面。设置视口({宽度:1920,高度:1080 });
var a=aTags[I];
await page.goto(a.href,{ time out:0 });//防止页面太长,加载超时
//注入代码,慢慢把滚动条滑到最底部,保证所有的元素被全部加载
设scrollEnable=true
设scrollStep=500//每次滚动的步长
而(可滚动){
滚动lenable=await页面。评估((滚动步骤)={
让顶部滚动=文档。滚动元素。滚动顶部;
文档。滚动元素。scroll top=scroll top滚动步长;
返回文档。身体。客户端高度滚动顶部1080?真:假
},滚动一步);
等待睡眠(100);
}
等待页面。waitforselector( # footer-2014 ,{超时:0 });//判断是否到达底部了
设文件名=图像/项目—“我”.png ;
//这里有个操纵木偶的人的病菌一直没有解决,发现截图的高度最大只能是16384px,超出部分被截掉了。
等待页面。截图({路径:文件名,整页:真});
页面。close();
}
浏览器。close();
});
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。