在实际的测试中,有时候我们会经常遇到找不到元素的问题,这也是自动化测试中经常会遇到的情况,我一般首先考虑的就是是否由于页面中嵌套了frame,从而导致定位不到元素,如果页面代码中没有iframe层,再根据报错信息来考虑原因是否是:元素被隐藏,定位路径不正确等情况。
一般下列情况中页面可能包含frame嵌套:后台管理系统中,页面中有一些独立的功能模块,特征是页面显示切换了模块,但是地址栏的URL并没有变化。或者是一些对接其他系统功能的模块,很可能会包含嵌套。下面我们来学习一下,如何通过切换frame来成功定位元素。
frame标签有frameset、frame、iframe三种,frameset跟其他普通标签没有区别,不会影响到正常的定位,
而frame与iframe对selenium定位而言是一样的,selenium有一组方法对frame进行操作。
一、关于页面frame的方法
备注:部分方法为老写法,官方已经不推荐使用;点击这篇文章查看切换frame的新写法。
切换到一个frame中
-
switch_to.frame(frame_reference): 切换到某一个frame,有多个frame要一层一层进,一层一层出
-
frame_reference:id、name、element(定位的某个元素)、索引 element相当driver(驱动).find_element_by_xpath()等方法找到的元素
-
driver.switch_to.parent_frame() 可以切换到上一层的frame,对于层层嵌套的frame很有用
切换到主界面
-
switch_to.default_content():
注:driver.switch_to.frame(None)等同于driver.switch_to.default_content()
二、关于frame的源码研究
webdriver.py
-
def switch_to_frame(self, frame_reference):
-
""" Deprecated use driver.switch_to.frame
-
"""
-
warnings.warn(
"use driver.switch_to.frame instead", DeprecationWarning)
-
self._switch_to.frame(frame_reference)
-
-
def switch_to_default_content(self):
-
""" Deprecated use driver.switch_to.default_content
-
"""
-
warnings.warn(
"use driver.switch_to.default_content instead", DeprecationWarning)
-
self._switch_to.default_content()
switch_to.py
-
def default_content(self):
-
"""
-
Switch focus to the default frame.
-
-
:Usage:
-
driver.switch_to.default_content()
-
"""
-
self._driver.execute(Command.SWITCH_TO_FRAME, {
'id':
None})
-
-
def frame(self, frame_reference):
-
"""
-
Switches focus to the specified frame, by index, name, or webelement.
-
-
:Args:
-
- frame_reference: The name of the window to switch to, an integer representing the index,
-
or a webelement that is an (i)frame to switch to.
-
-
:Usage:
-
driver.switch_to.frame('frame_name')
-
driver.switch_to.frame(1)
-
driver.switch_to.frame(driver.find_elements_by_tag_name("iframe")[0])
-
"""
-
if isinstance(frame_reference, basestring)
and self._driver.w3c:
-
try:
-
frame_reference = self._driver.find_element(By.ID, frame_reference)
-
except NoSuchElementException:
-
try:
-
frame_reference = self._driver.find_element(By.NAME, frame_reference)
-
except NoSuchElementException:
-
raise NoSuchFrameException(frame_reference)
-
-
self._driver.execute(Command.SWITCH_TO_FRAME, {
'id': frame_reference})
通过观察源码,发现切换frame时,是通过id、name、索引、元素这四种方法来实现switch_to_frame()方法,而default_content的方法默认值为None,所以说我们可以发现switch_to_frame(None)等同于switch_to_default_content()
三、实例验证(1)w3c网站的frame切换
我们通过进入w3cschool网站的实验室例子来实验frame,这个层级关系是有一个父类的frame层级,里面有三个frame的子类层级
代码实例:
-
from selenium
import webdriver
-
-
# 打开Chrome 进入示例页面
-
driver = webdriver.Chrome()
-
driver.get(
"http://www.w3school.com.cn/tiy/t.asp?f=html_frame_cols")
-
-
# 定位父类层级iframe
-
ele_framest = driver.find_element_by_css_selector(
"#result > iframe")
-
-
# 切换到父类层级iframe-通过元素切换
-
driver.switch_to_frame(ele_framest)
-
print(driver.page_source)
-
print(
"----------------------------------------------------------------------------")
-
-
# 切换到第二个子类frame-通过索引切换
-
driver.switch_to_frame(
1)
-
print(driver.page_source)
-
print(
"----------------------------------------------------------------------------")
-
-
# 切换到最上层层级-等同于driver.switch_to_frame(None)
-
driver.switch_to_default_content()
-
print(driver.page_source)
四、实例验证(2)163邮箱首页登录
本来实例只有一个的,因为毕竟目前网站上用frame的不多,但是在一次别人问我的问题,163邮箱登录的问题,琢磨了一段时间,这里把问题展示一下:
我们如果直接定位输入框,会发现无法定位到元素,通过查看源代码发现,原来163把登陆的功能单独调过来,独立的作为一个模块,用frame来隔断了,所以说我们要做的就是先切换到对应的frame后,再去定位
代码演示:
-
# 163邮箱登陆
-
from selenium
import webdriver
-
from time
import sleep
-
driver=webdriver.Chrome()
-
-
# 进入163邮箱首页
-
driver.get(
"http://mail.163.com/")
-
sleep(
2)
-
-
# 切换到包含登录框的frame下
-
driver.switch_to_frame(
0)
-
-
# 通过name定位输入框,并输入数据
-
driver.find_element_by_xpath(
"//input[@name='email']").send_keys(
"4444")