【Selenium:无法定位元素的几种解决方案】

时间:2023-02-06 17:17:39

做web应用的自动化测试时,定位元素是必不可少的,这个过程经常会碰到定位不到元素的情况(报selenium.common.exceptions.NoSuchElementException),一般可以从以下几个方面着手解决:

1、定位表达式错误或者定位到多个

试了好几种定位方式了,怎么看这个元素就是这个属性,没错啊!这应该是我们最常遇到了情况。这个时候怎么办呢?很简单。检验你的定位方式到底有没有找到元素
1、打开chrom浏览器
2、F12打开调试模式
3、选择元素右键检查
4、按住ctrl+F键,调试框下方出现一个输入框


【Selenium:无法定位元素的几种解决方案】



5、输入自己的元素定位表达式


【Selenium:无法定位元素的几种解决方案】

image.png

  • 黄色加亮为定位到的元素,2的标识处为定位到的元素个数,如果有唯一元素,说明定位表达式正确
  • 如果有多个元素,需要优化定位表达式直至唯一或用下标精准定位到你要找的元素。
    也可以把定位到的多个元素属性及下标打印出来,对应你要定位到的原元素



  • 如果没有定位到元素,说明元素表达式错误

2、Frame/Iframe原因定位不到元素:

这个是最常见的原因,首先要理解下frame的实质,frame中实际上是嵌入了另一个页面,而webdriver每次只能在一个页面识别,因此需要先定位到相应的frame,对那个页面里的元素进行定位。
解决方案:
如果iframe有name或id的话,直接使用switch_to_frame("name值")或switch_to_frame("id值")。如下:


#切换至id或者name为xf的iframe页面
driver.switch_to.frame('xf')

如果没有可用的id和name属性,可以先定位到frame/iframe,再将定位对象传给switch_to.frame(对象)方法。如下:


#先定位到iframe
xf = driver.find_element_by_xpath('//*[@class="if"]')
#再将定位对象传给switch_to.frame()方法
driver.switch_to.frame(xf)

如果完成操作后,可以通过switch_to.parent_content()方法跳出当前iframe,或者还可以通过switch_to.default_content()方法跳回最外层的页面。


3.页面还没有加载出来,就对页面上的元素进行的操作:

这种情况一般说来,可以设置等待,等待页面或者元素加载完毕后再进行操作
可用的有三种等待方式:

  • WebDriverWait() 显性等待
  • driver.implicitly_wait(秒) 全局隐式等待
  • sleep(秒) 线程等待,休眠固定的时间

具体用法参看另外一篇文章 ​:Selenium:元素定位的各种方法

4、页面元素失去焦点导致脚本运行不稳定

解决方法:driver.switch_to.active_element 遇到脚本不稳定,有时会失去焦点导致测试失败的情况下,可以先切到焦点元素再进行操作。注意.active_element后面不带括号()。


driver.find_element_by_class_name('fnew').click()
time.sleep(2)
driver.switch_to.active_element.send_keys('filename')

5、元素被遮挡,不可用,不可见

5.1窗口最大化

driver.maximize_window() 由于窗口大小改变引起的页面元素布局发生变化,被测元素被遮挡,可以先将窗口最大化,再进行元素定位。

5.2页面有滚动条,元素需要操作滚动条后才可见

# 滚动元素“底端”与当前窗口的“底部”对齐,ele为定位到的元素对象
driver.execute_script("arguments[0].scrollIntoView(false);", ele)

# 滚动元素“底端”与当前窗口的“顶部”对齐
driver.execute_script("arguments[0].scrollIntoView();", ele)

# 滚动到页面底部
driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")

# 滚动到页面顶部
driver.execute_script("window.scrollTo(document.body.scrollHeight,0)")

# 直接点击不可见的目标元素,不再先跳转。
self.driver.execute_script(“arguments[0].click();”, ele)

5.3不可用

对于有些WebDriver没有提供的方法或者无法实现的功能,WebDriver提供了driver.execute_script()方法来执行JavaScript代码。
假设一个输入框可以通过id='text'将其定位,却不能通过send_keys()输入文本内容,可以借助JavaScript代码来实现


text = "input text"
driver.execute_script("var obj=document.getElementById('text'); obj.value=' " + text + " ';")

假如某个元素属性display:none方法是设置元素不可见(display='block'将显示元素),导致通过定位页面元素无法定位。’
对于这种问题,可以通过JavaScript修改页面元素属性来将元素置位可见,然后通过id、classname等方法去定位,示例代码如下


#js改变元素的display属性为'block'
js = "document.getElementById(\"element_id\").style.display='block';"
# 调用js脚本
driver.execute_script(js)
#然后再定位元素
driver.find_element_by_id("txtPassword").send_keys("123456")

当我们想在输入框 是日期类型,并send_keys 的时候发现不能输入,输入框被禁用readonly,处理方式如下


#js改变元素的display属性为'block'
js = 'document.getElementById(\"element_id\").removeAttribute(\"readonly\")'
# 调用js脚本
driver.execute_script(js)
#然后再输入

6、页面跳转到新的标签页,或者弹出的警告框等

在页面操作过程中有时候点击某个链接会弹出新窗口,这时就需要切换焦点到新窗口上进行操作。

窗口切换:

driver.switch_to.window(window_handle)切换到新窗口。
首先获取当前窗口的句柄driver.current_window_handle,接着打开弹出新窗口,获得当前打开的所有窗口的句柄driver.window_handles。通过for循环遍历handle,如果不等于第一次打开窗口的句柄,那么一定是新窗口的句柄,因为执行过程只打开了两个窗口;改变条件,如果等于第一次打开窗口的句柄,那么可以切换回第一次打开的窗口。


from selenium import webdriver
import time
current_handle = driver.current_window_handle
all_handles = driver.window_handles
if len(all_handles)>1:
for handle in all_handles:
if handle != current_handle:
driver.switch_to.window(handle)

#获取到的all_handles是一个列表,也可以用下标进行切换
driver.switch_to.windowall_handles[0])

警告框:

对于JavaScript生成的alert、confirm以及prompt,无法使用前端工具对弹出窗口进行定位的,使用driver.switch_to.alert方法定位弹出框。alert的方法有:


alert = driver.switch_to.alert

alert .accept() '等同于点击“确认”或“OK”'
alert .dismiss() '等同于点击“取消”或“Cancel”'
alert .text '获取alert文本内容,对有信息显示的alert框'
alert .send_keys(text) '发送文本,对有提交需求的prompt框'