前言

本篇全面总结关于iframe 的定位,iframe上元素的操作(输入框,点击等),iframe 上的事件监听 与iframe上执行JS脚本的总结。

iframe 对象的定位

定位iframe 对象,总的来说有四种方法

  • page.frame_locator(selector) 通过page对象直接定位iframe 对象,传selector 选择器参数
  • page.locator(selector).frame_locator(selector) 通过page对象定位某个父元素,通过locator定位frame_locator(selector)
  • page.frame(name,url) 通过page对象直接定位iframe 对象,传name 或者url参数
  • page.query_selector(selector).content_frame() 通过query_selector方式,定位到元素,转成frame 对象

page 对象还有2个跟frame 相关的方法

  • page.frames 获取page对象全部iframes,包含page本身的frame对象
  • page.main_frame 获取page的main_frame (page对象本身也是一个frame对象)

什么是iframe

iframe 简单来说就是一个 html 嵌套了另外一个 html。在页面元素上最简单的识别方法,就是看你需要定位的元素外层有没有iframe的标签名称

比如以下网页看到外层就有3个iframe

官网上写了个示例,可以快速查看iframe的层级结构

使用示例

from playwright.sync_api import sync_playwright
# 上海悠悠 wx:283340479  
# blog:https://www.cnblogs.com/yoyoketang/

def dump_frame_tree(frame, indent):
    print(indent + frame.name + '@' + frame.url)
    for child in frame.child_frames:
        dump_frame_tree(child, indent + "    ")


with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("file:///C:/Users/dell/Desktop/home.html")
    dump_frame_tree(page.main_frame, "")
    browser.close()

于是可以看到层级结构如下

也就是主页面(主页面其实也可以看成一个iframe 对象)下有3个iframe,其中最后一个iframe2下又嵌套了一层iframe。

下面示例用到了3个基本方法

  • page.main_frame 获取page对象本身的 frame 对象
  • page.frames 获取page对象全部frames 包含page本身的frame对象
  • frame.child_frames 获取frame下的全部子 frame 对象
from playwright.sync_api import sync_playwright
# 上海悠悠 wx:283340479  
# blog:https://www.cnblogs.com/yoyoketang/

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("file:///C:/Users/dell/Desktop/home.html")
    print('获取page对象本身的frame对象')
    print(page.main_frame)
    print('获取page对象全部frames 包含page本身的frame对象 ')
    print(page.frames)
    print('获取page对象子frame ')
    print(page.main_frame.child_frames)
    browser.close()

运行结果

获取page对象本身的frame对象
<Frame name= url='file:///C:/Users/dell/Desktop/home.html'>
获取page对象全部frames 包含page本身的frame对象 
[<Frame name= url='file:///C:/Users/dell/Desktop/home.html'>, <Frame name=yoyo url='http://127.0.0.0/login.html'>, <Frame name=iframe-auto2834976.42 url='file:///C:/Users/dell/Desktop/down.html'>, <Frame name=yoyo2 url='file:///C:/Users/dell/Desktop/iframe.html'>, <Frame name= url='file:///C:/Users/dell/Desktop/down.html'>]
获取page对象子frame 
[<Frame name=yoyo url='http://127.0.0.0/login.html'>, <Frame name=iframe-auto2834976.42 url='file:///C:/Users/dell/Desktop/down.html'>, <Frame name=yoyo2 url='file:///C:/Users/dell/Desktop/iframe.html'>]

从运行结果可以看出,iframe 对象有2个重要的属性name和url, 可以直接打印出来看看

    for frame in page.frames:
        print(frame.name)
        print(frame.url)

运行结果

file:///C:/Users/dell/Desktop/home.html
yoyo
http://47.108.155.10/login.html
iframe-auto9389828.64
file:///C:/Users/dell/Desktop/down.html
yoyo2
file:///C:/Users/dell/Desktop/iframe.html

file:///C:/Users/dell/Desktop/down.html

从结果可以看出。iframe 元素的name和id属性,都会被作为那么属性打印出来,如果2个属性都没有,那么获取的name属性为空字符

E page.frame_locator(selector) 使用

如下图定位iframe上的输入框,并输入内容

基本语法

page.frame_locator(selector)

使用示例,可以直接定位到frame对象,继续定位元素操作

    # 直接定位输入
    page.frame_locator('[name="yoyo"]').locator("#username").fill('yoyoketang')
    page.frame_locator('[name="yoyo"]').locator("#password").fill('123456')

或者先定位到iframe对象,再通过frame对象操作

# frame 对象操作
frame = page.frame_locator('[name="yoyo"]')
frame.locator("#username").fill('yoyoketang')
frame.locator("#password").fill('123456')

只需要定位到frame 对象,后面的元素定位操作都基本一样了。

也可以通过先定位外层的元素,再定位到frame对象,使用基本语法

page.locator(selector).frame_locator(selector)

page.frame(name,url) 的使用

page.frame 和 page.frame_locator 使用差异

  • page.frame_locator('') 返回的对象只能用locator() 方法定位元素然后click()等操作元素
  • page.frame() 返回的对象能直接使用fill() 和 click() 方法

page.frame(name,url) 方法可以使用frame的name属性或url属性定位到frame对象

方法一:name 属性可以是iframe 元素的name 或id属性

使用name属性定位示例

    # name 属性定位
    frame = page.frame(name="yoyo")
    print(frame)

运行结果

<Frame name=yoyo url='http://47.108.155.10/login.html'>

name 属性不能模糊匹配,只能绝对匹配字符串

iframe元素没有那么属性,有id属性,也可以用

    # id 属性也能定位
    frame = page.frame(name="yoyo2")
    print(frame)

运行结果

<Frame name=yoyo2 url='file:///C:/Users/dell/Desktop/iframe.html'>

方法二:url属性定位

url属性的值,就是我们看到页面上的src属性,可以支持模糊匹配

    # url 支持模糊匹配
    frame = page.frame(url='login.html')
    print(frame)

iframe 上的动态id属性如何定位

有时候,我们看到的iframe 的id 不是固定的,是动态的一个id, 每次刷新,它的值都不一样(一般前面一部分是固定的)

可以用css的正则匹配元素属性

css的正则匹配

语法 描述
$('[name^="value"]') 匹配 name 以 value 开头的元素
$('[name$="end"]') 匹配 name 以 end 结尾的元素
$('[class*="text"]') 匹配class属性包含text的元素

使用示例

 # css 正则匹配属性
    frame = page.frame_locator('[id^="iframe-auto"]')
    print(frame)
    frame.locator('#username').fill('yoyo')

也可以使用xpath的contains 包含属性

# xpath的contains 包含属性
    frame = page.frame_locator('//*[contains(@id, "iframe-auto")]')
    print(frame)
    frame.locator('#username').fill('yoyo')

2层iframe 如何操作

iframe 下嵌套另外一个iframe

解决办法没什么技巧,一层一层定位即可

    # 一层一层定位
    frame = page.frame_locator('#yoyo2').frame_locator('[src="down.html"]')
    frame.locator('#username').fill("yoyoketang")

page.query_selector(selector).content_frame() 方法

还有一个不怎么常用的方法,使用page.query_selector(selector)元素句柄的方式定位到iframe,然后使用content_frame() 方法切换到iframe对象上
语法规则

page.query_selector(selector).content_frame() 
````

使用示例

```python
    # query_selector 方法
    iframe = page.query_selector('[src="down.html"]').content_frame()
    print(iframe)

监听 iframe 上的事件

事件可以通过page对象直接监听到

使用示例

from playwright.sync_api import sync_playwright
# 上海悠悠 wx:283340479  
# blog:https://www.cnblogs.com/yoyoketang/

def handler(dialog):
    print("监听dialog 事件")
    print(dialog.message)
    # dialog.accept()


with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("file:///C:/Users/dell/Desktop/home.html")

    page.on('dialog', handler)

    # 一层一层定位
    frame = page.frame_locator('#yoyo2')
    frame.locator('#alert').click()

    page.pause()
    browser.close()

运行结果可以看到page.on()可以截图到iframe上的事件

监听dialog 事件
删除后无法还原

其它的下载事件,文件上传监听方法都一样。

在iframe 上执行JavasScript 代码

page.evaluate(js代码)方法可以直接在page对象上执行JavasScript 代码

from playwright.sync_api import sync_playwright
# 上海悠悠 wx:283340479  
# blog:https://www.cnblogs.com/yoyoketang/

def handler(dialog):
    print("监听dialog 事件")
    print(dialog.message)
    # dialog.accept()


with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("file:///C:/Users/dell/Desktop/home.html")

    page.on("dialog", handler)

    # 执行JavaScript
    page.evaluate("alert('hello world')")

    page.pause()
    browser.close()

在iframe上执行JavaScript代码,需在iframe对象上执行

from playwright.sync_api import sync_playwright
# 上海悠悠 wx:283340479  
# blog:https://www.cnblogs.com/yoyoketang/

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("file:///C:/Users/dell/Desktop/home.html")

    iframe = page.frame(url='down.html')
    # 执行js 给输入框输入内容
    js = "document.getElementById('username').value='yoyo';"
    iframe.evaluate(js)

    page.pause()
    browser.close()

如果需要在iframe上执行js代码,必须使用page.frame()方法定位到iframe对象,才有iframe.evaluate(js) 方法执行js。
page.frame_locator() 方法只能定位元素操作元素,没有执行js的方法

文章转自:https://www.cnblogs.com/yoyoketang/p/17325352.html