官方英文版API入口:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md.
汉化版API入口:https://yq.aliyun.com/articles/607102.
学习笔记入口:https://blog.csdn.net/qupan1993/article/details/85371556.
具体API我就不解释,在前边第一篇中已经给出学习的目录了,可去看下基础的,API实在是太多了,我这边只列出我自己遇到的有问题的API
先给出具体代码:一个登录163邮箱的例子
看一下效果图
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless:false});
const page = await browser.newPage();
await page.goto('https://mail.163.com/');
await page.setViewport({width:1000,height:800});
//切换iframe框代码
await page.waitFor('#switchAccountLogin');
await page.click('#switchAccountLogin');//默认是扫二维码的,点击密码登录,调出输入框
await page.waitFor('[id*="x-URS-iframe"]');//等待我的iframe出现
const frame = ( await page.frames() )[4];//通过索引得到我的iframe
await frame.waitFor('.j-inputtext.dlemail');//等待用户名输入框出现
await frame.type('.j-inputtext.dlemail','12345');//输入账户
await frame.waitFor('.dlpwd');//等待密码框出现
await frame.type('.dlpwd','12345');//输入密码
//等待3秒后退出浏览器
await page.waitFor(3000);
await browser.close();
})();
1.首先在第9行我加了一个智能等待,等待包含输入账户和密码的iframe框加载出来,有时候网络加载慢这个iframe框还没出来,但是其它的iframe已经加载出来了,而page.frames()语句照样会执行,这样就会找不到我要的iframe了,所以加一下等待
2. 第10行代码我用的是索引的方法直接得到我的iframe,因为没有name的属性不能找到,如果有name的属性可以这样写,如果没有name,返回ID,
原理
const frame = await page.frames().find(f => f.name() === 'name');
另外如果使用索引的时候可以这样查看列表有多少个值:
const frame = await page.frames();//得到所有的frame框
console.log(frames.length);//查看得到的frame列表数量
3.第11行我加了一个智能等待,等输入框出现,这个我不知道为什么?如果我不加的话就会报错,找不到输入框,如果加上的话就没问题,如果谁知道怎么回事可以给我说下。
4.后面的12,13,14行都一样了就是正常的等待和输入就好啦。
这个多层的iframe真的很少了,至少我都是见到的单层的,现在已经很少会有多层了,不过少归少并不代表没有,于是自己写了几个html为大家讲解一下吧!
先看下效果图:
首先新建三个HTML文件和脚本文件,放到一个路径下,输入以下内容:
input.html
<!DOCTYPE html>
<html>
<head>
<title>input</title>
</head>
<body>
<input type="text" id="input_01">
</body>
</html>
frame.html
<!DOCTYPE html>
<html>
<head>
<title>frame</title>
</head>
<body>
<iframe src="input.html" name="mainframe"></iframe>
</body>
</html>
iframe.html
<!DOCTYPE html>
<html>
<head>
<title>iframe</title>
</head>
<body>
<iframe src="frame.html" name="leftframe"></iframe>
<iframe src="frame.html" name="rightframe"></iframe>
<input type="text" id="input_02">
</body>
</html>
多层切换会有一些麻烦,脚本如下(PS:记得把page.goto()中的地址换成自己电脑的,因为是本地文件,每个地址都不同:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless:false});
const page = await browser.newPage();
await page.goto('file:///MAC/Study/27.Puppeteer/case/iframe.html');
await page.setViewport({width:1000,height:800});
// 得到第一个iframe框架
const frame1 = await page.frames().find(f => f.name() === 'leftframe');
// 得到第一个iframe框架的子框架
const childframe1 = ( await frame1.childFrames() )[0];
// 等待输入框出现,输入信息
await childframe1.waitFor('#input_01');
await childframe1.type('#input_01','第一次输入:leftframe');
// 得到第二个iframe框架
const frame2 = await page.frames().find(f => f.name() === 'rightframe');
// 得到第二个iframe框架的子框架
const childframe2 = ( await frame2.childFrames() )[0];
// 等待输入框出现,输入信息
await childframe2.waitFor('#input_01');
await childframe2.type('#input_01','第一次输入:rightframe');
// 在iframe外面的输入框输入文字,直接使用句柄进行操作,不用切换iframe
await page.waitFor('#input_02');
await page.type('#input_02','第一次输入:out');
// 第二次输入
// 在第一个iframe中清空文本再次输入,不用重新获取frame,直接使用句柄操作
await page.waitFor(2000);
await childframe1.waitFor('#input_01');
await childframe1.$eval('#input_01',input => input.value='第二次输入:leftframe')
// 在第二个iframe中清空文本再次输入,不用重新获取frame,直接使用句柄操作
await page.waitFor(2000);
await childframe2.waitFor('#input_01');
await childframe2.$eval('#input_01',input => input.value='第二次输入:leftframe')
// 在iframe最外面的清空文本再次输入,不用重新获取frame,直接使用句柄操作
await page.waitFor(2000);
await page.waitFor('#input_02');
await page.$eval('#input_02',input => input.value='第二次输入:out');
await page.waitFor(3000);
await browser.close();
})();
好啦,脚本和HTML文件完成了,我特意为每个iframe添加了name的属性、自己运行看下效果吧:
那我就开始分析啦:
1、第9行中page.frames()得到所有的frame框架,然后用find(f => f.name() === ‘leftframe’)的函数得到左侧的iframe框架
2、第11行中使用frame1.childFrames()的函数得到儿子iframe框,用的句柄第9行得到的frame1,因为只有一个子框所以我直接用索引的方式得到
3、第13、14行使用儿子iframe框的句柄childframe1进行操作等待和输入文字
4、16-22行脚本是操作第二个框架和第一个iframe是一样的。我就不再多说了,
5、25-26是在最外面的input框输入文字,直接使用page的句柄操作
6、29-40行代码就是第二次输入文本了,为了能看清变化,特地等待了几秒,
7、大家可能会发现,并且疑问我第二次输入是直接进行操作的,没有切换iframe?恩这个值得说明下,我们在用Selenium做UI自动化的时候切换进去一个iframe之后,操作其它的元素必须跳出来才能操作,因为Selenium只有driver一个句柄可以进行操作,但是看看咱们的puppeteer脚本里的句柄:page、frame1、frmae2、childframe1、childframe2,足足五个句柄可以使用呀,当然可以直接进行操作了,手动滑稽。这就是我喜欢这个框架的原因,
8、补充说一下5个句柄代表的含义,如果对句柄这个概念很懵逼的小伙伴自行去百度,我就不过多解释啦!
输入我用了另外一个代码childframe2.$eval(),这个是一个很强大的语法用处很大,后续我会把用法写出来,有兴趣的可以先看下官方API:
childframe2.$eval('#input_01',input => input.value='第二次输入:leftframe')
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless:false});
const page = await browser.newPage();
await page.goto('https://mail.163.com/');
await page.setViewport({width:1000,height:800});
//切换iframe框代码
await page.waitFor('#switchAccountLogin');
await page.click('#switchAccountLogin');//默认是扫二维码的,点击密码登录,调出输入框
await page.waitFor('[id*="x-URS-iframe"]');//等待我的iframe出现
await page.$eval('[id*="x-URS-iframe"]',f => f.setAttribute('name','iframe_01'));//添加name属性
const frame = await page.frames().find(f => f.name() === 'iframe_01');//使用name进行对比
await frame.waitFor('.j-inputtext.dlemail');//等待用户名输入框出现
await frame.type('.j-inputtext.dlemail','12345');//输入账户
await frame.waitFor('.dlpwd');//等待密码框出现
await frame.type('.dlpwd','12345');//输入密码
//等待3秒后退出浏览器
await page.waitFor(3000);
await browser.close();
})();
重要的时下面这两句,不过很遗憾,虽然自己新增了name属性,但是page.frames()函数并没有识别到这个属性,最后以失败告终,小伙伴可以试下
await page.$eval('[id*="x-URS-iframe"]',f => f.setAttribute('name','iframe_01'));//添加name属性
const frame = await page.frames().find(f => f.name() === 'iframe_01');//使用name进行对比
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless:false});
const page = await browser.newPage();
await page.goto('https://mail.163.com/');
await page.setViewport({width:1000,height:800});
//切换iframe框代码
await page.waitFor('#switchAccountLogin');
await page.click('#switchAccountLogin');//默认是扫二维码的,点击密码登录,调出输入框
await page.waitFor('[id*="x-URS-iframe"]');//等待我的iframe出现
//得到ifram里面的title属性,进行对比
const frames = await page.frames();//得到所有的frame和iframe框架
for (var i of frames) { //使用循环取出iframe
if (await i.title() === 'URS组件') {var frame = i;} //使用title()函数得到里面的title标题进行对比
};
await frame.waitFor('.j-inputtext.dlemail');//等待用户名输入框出现
await frame.type('.j-inputtext.dlemail','12345');//输入账户
await frame.waitFor('.dlpwd');//等待密码框出现
await frame.type('.dlpwd','12345');//输入密码
//等待3秒后退出浏览器
await page.waitFor(3000);
await browser.close();
})();
重要的是下列两句,说明一下:
const frames = await page.frames();//得到所有的frame和iframe框架
for (var i of frames) { //使用循环取出iframe
if (await i.title() == 'URS组件') {var frame = i;} //使用title()函数得到里面的title标题进行对比
};
首先使用page.frames()得到所有的iframe,接下来使用for循环取出每个iframe,并得到title,进行对比。
标题是在iframe中年查看的,如下图
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless:false});
const page = await browser.newPage();
await page.goto('https://mail.163.com/');
await page.setViewport({width:1000,height:800});
//切换iframe框代码
await page.waitFor('#switchAccountLogin');
await page.click('#switchAccountLogin');//默认是扫二维码的,点击密码登录,调出输入框
await page.waitFor('[id*="x-URS-iframe"]');//等待我的iframe出现
//得到ifram里面的title属性,进行对比
const url = await page.$eval('[id*="x-URS-iframe"]',f => f.getAttribute('src'));//得到属性值
const frames = await page.frames();//得到所有的frame和iframe框架
for (var i of frames) { //使用循环取出iframe
if (await i.url() == url) {var frame = i;} //使用url()函数得到里面的url标题进行对比
};
await frame.waitFor('.j-inputtext.dlemail');//等待用户名输入框出现
await frame.type('.j-inputtext.dlemail','12345');//输入账户
await frame.waitFor('.dlpwd');//等待密码框出现
await frame.type('.dlpwd','12345');//输入密码
//等待3秒后退出浏览器
await page.waitFor(3000);
await browser.close();
})();
这个和title差不多,只不过使用的是url函数,并且要使用page.$eval()得到src的值作对比:
const url = await page.$eval('[id*="x-URS-iframe"]',f => f.getAttribute('src'));//得到属性值
const frames = await page.frames();//得到所有的frame和iframe框架
for (var i of frames) { //使用循环取出iframe
if (await i.url() == url) {var frame = i;} //使用url()函数得到里面的url标题进行对比
};