终于环境搭建好啦,可以开始快乐的玩耍了,开始记录我遇到的问题了

官方英文版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框相关推荐

  1. node.js 学习笔记三:路由url

    一.修改server.js var http = require("http"); var url = require("url"); //导入内置url模块f ...

  2. Puppeteer 学习笔记及基本用法

    Puppeteer 学习笔记及基本用法 Puppeteer 安装 语法 基本语法 API 分层结构 加载导航页面 等待元素.请求.响应 自定义等待 元素定位 用户模拟操作 请求拦截 获取 WebSoc ...

  3. Node.js学习笔记8

    Node.js学习笔记8 HTTP服务器与客户端 Node.js的http模块,封装了一个高效的HTTP服务器和一个简易的HTTP客户端 http.server是一个基于事件的HTTP服务器,核心由N ...

  4. node.js学习笔记5——核心模块1

    node.js学习笔记5--核心模块1 Node.js核心模块主要内容包括:(1)全局对象 (2)常用工具 (3)事件机制 (4)文件系统访问 (5)HTTP服务器与客户端 一: 全局对象 Node. ...

  5. 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 个重要概念 ...

  6. node.js 学习笔记(二)模板引擎和C/S渲染

    node.js 学习笔记(二)模板引擎和C/S渲染 文章目录 node.js 学习笔记(二)模板引擎和C/S渲染 一.初步实现Apache功能 1.1 使用模板引擎 1.2 在 node 中使用模板引 ...

  7. 千锋Node.js学习笔记

    千锋Node.js学习笔记 文章目录 千锋Node.js学习笔记 写在前面 1. 认识Node.js 2. NVM 3. NPM 4. NRM 5. NPX 6. 模块/包与CommonJS 7. 常 ...

  8. 唤醒手腕 - 前端服务器端开发 Node.Js 学习笔记(学习中,更新中)

    唤醒手腕 - Node.Js 学习笔记 唤醒手腕个人的学习记录,时间在2021年12月13日 ~ 2021年12月14日,学习方式看官方文档和B站视频,如有错误或者代码问题的地方,欢迎C站大佬能够帮忙 ...

  9. node入门-学习笔记

    文章目录 node入门-学习笔记 node 启动node服务 重构express-run node入门-学习笔记 node 为什么后端要用node, 因为它是js js运行时环境 主要使用expres ...

  10. Spring框架学习笔记(三)(AOP,事务管理)

    Spring框架学习笔记(三) 九.AOP 9.1 AOP的注解配置 (1) 新建计算器核心功能(模拟:不能在改动核心代码) (2) 建立一个普通的Java类写增强代码(面向切面编程),使用Sprin ...

最新文章

  1. linux 预加载 动态链接库rootkit 简介
  2. python在中小学教学中的应用-小学信息技术教学中进行Python 编程语言教学的策略...
  3. npm运行报错:Error: ENOSPC: System limit for number of file watchers reached
  4. 思科配置计算机ip地址子网掩码,计算机系统与网络技术IP地址 子网掩码 主机号等计算复习...
  5. Android系统(20)---开发android项目的常用jar包
  6. 前端Js框架汇总【转】
  7. 拓端tecdat|R语言使用自组织映射神经网络(SOM)进行客户细分
  8. mysql oracle replay_Oracle 数据库重放(Database Replay)功能演示
  9. VS2017 安装打包插件
  10. python向mysql插入数据
  11. 计算机职业学校教学论文,中等职业学校计算机教学论文
  12. 计算机视觉:步态识别-综述(一)
  13. 群晖套件 Transmission 汉化
  14. git小乌龟的安装和使用教程
  15. FXP登录Linux报错
  16. 小程序 全屏滑动【亲测有效】
  17. S3DIS场景点云数据集
  18. Tomcat绑定IPV4端口
  19. GD32F130 使用ST库开发项目
  20. python爬虫实战之爬取51job前程无忧简历

热门文章

  1. FPGA 等效门数的计算方法
  2. MYSQL选修课的心得体会_选修课心得体会
  3. SVN commit时,提示“remains in conflict”错误:
  4. oceanbase安装
  5. REST API 是什么?
  6. 360云盘上传 计算机文件格式,360云盘怎么上传文件 360云盘上传文件方法
  7. FPI厂商SoleraNetworks被Blue Coat收购
  8. 实战SpringBoot Admin
  9. 简单介绍公有云、私有云、混合云
  10. python 按行读取_python按行读取