selenimu找不到元素的几种情况和解决办法
一、页面刷新(StaleElementReferenceException: Message: Element not found in the cache...)
原因:页面被刷新了。
在当前页面找不到这个元素了,但是你自己手动复制到页面开发者工具上查看明明有啊,为啥在代码里面就找不到了呢?这时,你还可能会问“可是明明元素就在那里,没有变,甚至我是回退回来的,页面都没有变,怎么会说是新页面?”。
其实呢是在操作的过程中页面发生了变化,刷新了,虽然表面上看起来两个元素长得一模一样,事实上是每一个元素都有自己的一个ID号。
用代码(Python)来证明!
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://cn.bing.com/')
print(driver.find_element_by_id('sb_form_q')) # sb_form_q before refresh
driver.refresh() #刷新
#driver.back() #此处不管是刷新还是点击了其他操作又会回到这个页面都是一样的
print(driver.find_element_by_id('sb_form_q')) # sb_form_q after refresh
driver.quit()
结果如下图:很明显,element有一个对应的ID是不一样的,只能在当前页面时去使用、存取才会有效。

分析:
refresh,不论你是主动刷新还是页面自动刷新
back,已经跳转到了其他页面,然后你用driver.back()跳回来,这也是一张新的页面了
跳转到了新的页面,但这张新页面上有一些元素跟之前页面是长得一样的,这也是一张新的页面了。比如:一排分页按钮,你点击下一页跳转到了第二页,想要还用原来的元素操作到下一页,那也是不可能的了。
解决:
只要刷新页面之后重新获取元素就行,不要提前获取一组元素,然后去循环操作每一个元素,这种情况还是获取元素的个数,然后在循环中获取相应位置的元素,在用的时候才去获取,这样你就获取到最新的id了,也不会出现找错人的尴尬了。
我今天就遇到一个,其实之前也遇到了,只是没有着重记录一下而已。
有一个四个菜单,分别要去带四个传入的数据点击四次,检查是否能到另外一个页面,页面返回是否正常。
我就使用了一个循环,但是最开始没细看,直接循环成了元素,到新页面验证完成之后又返回原来的页面继续定位,发现定位不了了;这时我才想起来不应该循环元素,应该循环元素的个数,在这个循环的过程中再来定位获取元素。

二、iframe原因定位不到元素需要切换Iframe
这种情况一般发生在有内嵌的iframe的情况下,需要切换一下iframe
另外注意的是有的页面会有多个iframe,找不到元素同样是没有切换iframe,切换即可
- 通过iframe的索引(从0开始)、id、name、WebElement
driver.switch_to.frame(driver.find_element_by_xpath("//div[@id='loginDiv']/iframe"))
- 从frame中切回主文档
switch_to.default_content()
- 嵌套frame的操作(switch_to.parent_frame())
<html>
<iframe id='frame1'>
<iframe id='frame2'></iframe>
</iframe>
</html>
- 从主文档切到frame2,一层层切进去
driver.switch_to.frame("frame1")
driver.switch_to.frame("frame2")
- 从frame2再切回frame1,这里selenium给我们提供了一个方法能够从子frame切回到父frame,而不用我们切回主文档再切进来
driver.switch_to.parent_frame()
三、隐藏元素
#如果界面有隐藏元素:可以调用js脚本,处理隐藏元素
js = "document.getElementById(\"normalLoginTab\").style.display='block';"
driver.execute_script(js)
sleep(4)
四、页面刷新(StaleElementReferenceException: Message: Element not found in the cache...)
这个需要增加一定等待时间,显示等待时间可以通过WebDriverWait 和util来实现
-
添加固定的休眠时间,引入time包
这个只能大概估算一下,给个固定值,不是很推荐使用这个,不灵活。
Python:time.sleep(10)
Java:Thread.sleep(1000);//单位是毫秒,1000毫秒=1秒 -
添加智能等待,隐式的等待一个元素被发现或一个命令完成
webdriver提供的超时等待,implicitly_wait()方法
Python:driver.implicitly_wait(20) -
添加智能等待时间,在设置的时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。
示例代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("http://www.126.com")
print('---------登录前----------')
# 获取当前title
print("title:" + driver.title)
# 获取当前URL
print("URL:" + driver.current_url)
#默认为二维码登录,获取html元素,通过点击事件切换到 使用密码登录
driver.find_element_by_id('switchAccountLogin').click()
#等待界面渲染完成
sleep(4)
#如果界面有隐藏元素:可以调用js脚本,处理隐藏元素
#js = "document.getElementById(\"normalLoginTab\").style.display='block';"
#driver.execute_script(js)
#sleep(4)
#我们需要获取的元素在iframe标签的,必须切换到iframe标签,才能获取html元素
print(driver.switch_to.frame(driver.find_element_by_xpath("//div[@id='loginDiv']/iframe")))
sleep(4)
#测试一下该元素能否获取
a = driver.find_element_by_id('account-box')
print(a)
#填充邮箱账号密码
driver.find_element_by_xpath("//input[@name='email']").clear()
driver.find_element_by_xpath("//input[@name='email']").send_keys('testing111111')
driver.find_element_by_xpath("//input[@name='password']").clear()
driver.find_element_by_xpath("//input[@name='password']").send_keys('123')
driver.find_element_by_id('dologin').click()
#睡眠一会,等待登录后的提示信息渲染完成后,再获取提示语
sleep(4)
print('--------登录后---------')
print(driver.title)
print(driver.current_url)
print(driver.find_element_by_xpath("//div[@id='nerror']/div[2]").text)