在UI自动化中,一般先要找到需要操作的元素对象,然后进行操作。
定位元素成功与否,决定了你的用例的成败。所以定位元素很重要。
很多同学在用Selenium,Appium等做自动化的时候,有的时候却总能碰到这个熟悉的错误:
NoSuchElementException)
排查了很久,也找不到原因。一下子就懵了,刚才还好好的,这是怎么了?
UI 自动化,先天不足的就是不够稳定。很多因素导致定位不到元素。
可能网速导致还还没加载完全,你却已经操作了。
可能页面渲染慢,资源还没加载完全,特别是弱网情况下明显。
可能有广告等弹出框。
可能你等待时间不够,目标还没出现,你却先下手了。
种种因素,让你获取不到元素,从而报错。
下面我们来分析一下,找不到元素的原因有哪些,并找到解决方案。
1.Frame/Iframe原因定位不到元素:
这个是最常见的原因,首先要理解下frame的实质,frame中实际上是嵌入了另一个页面,而webdriver每次只能在一个页面识别,因此需要先定位到相应的frame,对那个页面里的元素进行定位。
解决方案:
如果iframe有name或id的话,直接使用switch_to_frame(“name值”)或switch_to_frame(“id值”)。如下:
driver=webdriver.Firefox() driver.get(r'http://www.126.com/') driver.switch_to_frame('x-URS-iframe') #需先跳转到iframe框架 username=driver.find_element_by_name('email') username.clear() #driver.switch_to.frame() python3 #driver.switch_to_frame() python2
操作完frame里面的元素后,要跳出来,接着操作其它的元素。
driver.switch_to.default_content()
2.Xpath描述错误原因:
由于Xpath层级太复杂,容易犯错。但是该定位方式能够有效定位绝大部分的元素,建议掌握。
我们定位元素的原则是:越简单越好。如果有id,name,class,css,link等,这是最简单不过了的。如果实在不好定位,我们可以用xpath,xpath建议不要用绝对路径,也不宜过长,一般用模糊匹配就可以了。
如:find_element_by_xpath(“//标签名[@属性=’属性值’]”)
find_element_by_xpath("//*[@id='snake')]")
这个属性可以是id,name,class等任意一种
也可以通过内容来匹配:
如:
find_element_by_xpath("//*[contains(text(),'安蜀黍')]")
也可以通过组合匹配:
如:
find_element_by_xpath("//*[contains(text(),'安蜀黍') and @id=‘snake']")
建议定位完了以后,用我教的那种办法,在浏览器控制台检验一下。
3.页面还没有加载出来,就对页面上的元素进行的操作:
这种情况一般说来,可以设置等待,等待页面显示之后再操作,这与人手工操作的原理一样。
设置一下显式等待时间。
import time time.sleep(3)
设置等待时间可以解决这个问题,缺点是需要设置较长的等待时间,案例多了测试就很慢,而且你不太清楚要等待多久,等待时间长了,浪费时间,等待时间短了,没有作用。
推荐大家用智能等待,在某个时间范围内,如果出现,就直接执行,如果没有出现,就抛出timeout异常,如下所示:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome() driver.get("https://www.csdn.net/") try: element= WebDriverWait(driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR,'iv.J_close.layer_close'))) finally: driver.quit()
4. 不可见元素定位
有些元素,是需要hover在另外一个元素上,才显示。是invisiable,disable的,如下拉列表等。所以我们先要将鼠标移到其父节点上,显示以后才对其定位。移动鼠标可以这样写:
from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Chrome() ActionChains(driver).move_to_element(driver.find_element_by_id('父节点')).perform() element = driver.find_element_by_id("子节点")
5. 广告页面
见到广告页,不要怕,有些是弹框的,有些是浮动的页面。基本都是能定位得到的。
比如这种:
driver.get("https://www.csdn.net/") time.sleep(5) driver.find_element_by_css_selector('div.J_close.layer_close').click()
又如这种:
driver.get("https://www.1688.com/") time.sleep(5) driver.find_element_by_css_selector('i.identity-icon.identity-close-icon').click()
都能定位得到,直接操作一下就可以了,也可以写个公共的方法,来处理这些广告或者弹框。
6. 弹出框
有的时候,会有弹出框来让你confirm,你必须去点击一下,否则页面失去焦点,导致你无法操作
Alert 弹出框:
driver.switch_to.alert.accept() #ok
driver.switch_to.alert.dismiss() #cancel
7. 窗体
有的时候,我们操作的时候,会打开另外的窗体,或者浏览器的其它tab页。
句柄已经切过去了,但是焦点还没切过去,所以需要switch_to.window(),把焦点页切过去,才能对当前页进行操作。
思路是: 获取所有句柄,返回一个list,而要切的对象都是最后一个,所有可以用[-1]直接切过去。
如:
driver.switch_to.window(driver.window_handles[-1])
在实践中,可能会遇到各种问题,只有通过不断积累,才能应付自如。
作者:Snake