Node+puppeteer学习笔记(三)--API问题解决--切换frame和iframe框
终于环境搭建好啦,可以开始快乐的玩耍了,开始记录我遇到的问题了
官方英文版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
1、切换单层iframe框架
先给出具体代码:一个登录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];//通过索引得到我的iframeawait 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();
})();
当时学习这个可真是苦恼我了,查了好多资料,看了好多教程都没有解决切换的问题,都只是说使用page.frames()得到所有的frame框返回一个列表来使用,但是并没有什么例子,看的我云里来雾里去的,幸好我有UI自动化的功底,有一些思路,于是自己摸索出来了,话不多说、上分析吧:
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行都一样了就是正常的等待和输入就好啦。
2、切换多层iframe框架
这个多层的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外面的输入框输入文字,直接使用句柄进行操作,不用切换iframeawait 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个句柄代表的含义,如果对句柄这个概念很懵逼的小伙伴自行去百度,我就不过多解释啦!
- page:代表整个页面的句柄,从标签body开始的,可以进行所有的操作
- frame1:代表第一个左侧的,iframe框架的句柄,只能操作左侧iframe框架里面的元素
- frmae2:代表第二个右侧的,iframe框架的句柄,只能操作右侧iframe框架里面的元素
- childframe1:代表第一个左侧的iframe框架子元素的句柄,只能操作子元素里面的元素
- childframe2:代表第二个右侧的iframe框架子元素的句柄,只能操作子元素里面的元素
输入我用了另外一个代码childframe2.$eval(),这个是一个很强大的语法用处很大,后续我会把用法写出来,有兴趣的可以先看下官方API:
childframe2.$eval('#input_01',input => input.value='第二次输入:leftframe')
3、当iframe没有name属性、也不想用索引查找时。解决方法如下:使用frame.title()和frame.url()两个函数
思路一:使用page.$eval()函数为iframe框架添加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出现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进行对比
最后找到了失败原因:框架只能附加到页面一次,
思路二:使用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 frames = await page.frames();//得到所有的frame和iframe框架for (var i of frames) { //使用循环取出iframeif (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) { //使用循环取出iframeif (await i.title() == 'URS组件') {var frame = i;} //使用title()函数得到里面的title标题进行对比};
首先使用page.frames()得到所有的iframe,接下来使用for循环取出每个iframe,并得到title,进行对比。
标题是在iframe中年查看的,如下图
思路三:使用page.$eval()函数得到iframe框架的src属性,使用url()函数得到网址,判断(成功)
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) { //使用循环取出iframeif (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) { //使用循环取出iframeif (await i.url() == url) {var frame = i;} //使用url()函数得到里面的url标题进行对比};
Node+puppeteer学习笔记(三)--API问题解决--切换frame和iframe框相关推荐
- node.js 学习笔记三:路由url
一.修改server.js var http = require("http"); var url = require("url"); //导入内置url模块f ...
- Puppeteer 学习笔记及基本用法
Puppeteer 学习笔记及基本用法 Puppeteer 安装 语法 基本语法 API 分层结构 加载导航页面 等待元素.请求.响应 自定义等待 元素定位 用户模拟操作 请求拦截 获取 WebSoc ...
- Node.js学习笔记8
Node.js学习笔记8 HTTP服务器与客户端 Node.js的http模块,封装了一个高效的HTTP服务器和一个简易的HTTP客户端 http.server是一个基于事件的HTTP服务器,核心由N ...
- node.js学习笔记5——核心模块1
node.js学习笔记5--核心模块1 Node.js核心模块主要内容包括:(1)全局对象 (2)常用工具 (3)事件机制 (4)文件系统访问 (5)HTTP服务器与客户端 一: 全局对象 Node. ...
- K8S 学习笔记三 核心技术 Helm nfs prometheus grafana 高可用集群部署 容器部署流程
K8S 学习笔记三 核心技术 2.13 Helm 2.13.1 Helm 引入 2.13.2 使用 Helm 可以解决哪些问题 2.13.3 Helm 概述 2.13.4 Helm 的 3 个重要概念 ...
- node.js 学习笔记(二)模板引擎和C/S渲染
node.js 学习笔记(二)模板引擎和C/S渲染 文章目录 node.js 学习笔记(二)模板引擎和C/S渲染 一.初步实现Apache功能 1.1 使用模板引擎 1.2 在 node 中使用模板引 ...
- 千锋Node.js学习笔记
千锋Node.js学习笔记 文章目录 千锋Node.js学习笔记 写在前面 1. 认识Node.js 2. NVM 3. NPM 4. NRM 5. NPX 6. 模块/包与CommonJS 7. 常 ...
- 唤醒手腕 - 前端服务器端开发 Node.Js 学习笔记(学习中,更新中)
唤醒手腕 - Node.Js 学习笔记 唤醒手腕个人的学习记录,时间在2021年12月13日 ~ 2021年12月14日,学习方式看官方文档和B站视频,如有错误或者代码问题的地方,欢迎C站大佬能够帮忙 ...
- node入门-学习笔记
文章目录 node入门-学习笔记 node 启动node服务 重构express-run node入门-学习笔记 node 为什么后端要用node, 因为它是js js运行时环境 主要使用expres ...
- Spring框架学习笔记(三)(AOP,事务管理)
Spring框架学习笔记(三) 九.AOP 9.1 AOP的注解配置 (1) 新建计算器核心功能(模拟:不能在改动核心代码) (2) 建立一个普通的Java类写增强代码(面向切面编程),使用Sprin ...
最新文章
- linux 预加载 动态链接库rootkit 简介
- python在中小学教学中的应用-小学信息技术教学中进行Python 编程语言教学的策略...
- npm运行报错:Error: ENOSPC: System limit for number of file watchers reached
- 思科配置计算机ip地址子网掩码,计算机系统与网络技术IP地址 子网掩码 主机号等计算复习...
- Android系统(20)---开发android项目的常用jar包
- 前端Js框架汇总【转】
- 拓端tecdat|R语言使用自组织映射神经网络(SOM)进行客户细分
- mysql oracle replay_Oracle 数据库重放(Database Replay)功能演示
- VS2017 安装打包插件
- python向mysql插入数据
- 计算机职业学校教学论文,中等职业学校计算机教学论文
- 计算机视觉:步态识别-综述(一)
- 群晖套件 Transmission 汉化
- git小乌龟的安装和使用教程
- FXP登录Linux报错
- 小程序 全屏滑动【亲测有效】
- S3DIS场景点云数据集
- Tomcat绑定IPV4端口
- GD32F130 使用ST库开发项目
- python爬虫实战之爬取51job前程无忧简历