全栈测试开发系列----Selenium元素定位大全
目录:
一、webdriver基本定位方式
二、父子定位、二次定位
三、JS定位
四、jQuery定位
元素定位是整个Web自动化中的重点和难点。Selenium实现网页的控制操作主要是通过控制前段的元素来完成的。在这个过程中,元素定位是基础,只有准确的抓取到对应的元素才能进行后续的自动化控制操作。
Selenium Python提供了一种用于定位元素(Locate Elements)的策略,你可以根据所爬取网页的HTML结构选择最适合的方案。定位多个元素时,只需将方法“element”后加s,这些元素将会以列表的形式返回。
定位单个元素的方法 | 定位多个元素的方法 | 方法含义 |
find_element_by_id |
find_elements_by_id | 通过标签id属性定位元素 |
find_element_by_name | find_elements_by_name | 通过标签name属性定位元素 |
find_element_by_xpath | find_elements_by_xpath | 通过xpath路径定位元素 |
find_element_by_link_text | find_elements_by_link_text | 通过显示文本定位元素 |
find_element_by_partial_link_text | find_elements_by_partial_link_text | 通过超链接文本定位元素 |
find_element_by_tag_name | find_elements_by_tag_name | 通过标签名定位元素 |
find_element_by_class_name | find_elements_by_class_name | 通过类名定位元素 |
find_element_by_css_selector | find_elements_by_css_selector | 通过CSS选择器定位元素 |
重点注意:
# 这里selenium最新版已经弃用了find_element_by_*方法,如果继续使用这些方法,selenium需要降低版本,否则会报错
# 报错信息:AttributeError: 'WebDriver' object has no attribute 'find_element_by_*'
使用最新的版本的替换方式: find_element()
使用的时候需要导入模块 from selenium.webdriver.common.by import By
属性定位方法 | 原定位方法find_element_by_* | 推荐定位方法find_element() |
id | find_element_by_id | find_element(By.ID,"element_id") |
name | find_element_by_name | find_element(By.NAME,"element_name") |
xpath | find_element_by_xpath | find_element(By.XPATH,"//标签名[@属性名='属性值']") |
link_text | find_element_by_link_text | find_element(By.LINK_TEXT,"element_link_text") |
partial_link_text | find_element_by_partial_link_text | find_element(By.PARTIAL_LINK_TEXT,"element_partial_link_text") |
tag_name | find_element_by_tag_name | find_element(BY.TAG_NAME,"element_tag_name") |
class_name | find_element_by_class_name | find_element(By.CLASS_NAME, "element_class_name") |
css_selector | find_element_by_css_selector | find_element(By.CSS_SELECTOR, "element_css_selector") |
DSMALL商城登录页面演示
一、webdriver基本定位方式
1、id定位
该方法是通过网页标签的id属性定位元素,它将返回第一个用id属性值匹配定位的元素。如果没有元素匹配id值,将会返回一个NoSuchElementException异常。
from selenium import webdriver import time driver = webdriver.Chrome() driver.get('http://192.168.235.128/index.php/home/login/login.html') # 窗口最大化 driver.maximize_window() time.sleep(2) assert "德尚商城" in driver.title print(driver.title) # 定位id为member_name的元素,并输入账号,截屏 driver.find_element_by_id("member_name").send_keys('test') time.sleep(2) driver.save_screenshot('inputname.png') driver.close() driver.quit()
修改代码后可以使用:
from selenium.webdriver.common.by import By driver.find_element(By.ID,"member_name").send_keys('test')
2、name定位
该方法是通过网页标签的name属性定位元素,它将返回第一个用name属性值匹配定位的元素。如果没有元素匹配name值,会返回NoSuchElementException异常。
driver.find_element_by_name("member_name").send_keys('test') driver.find_element(By.NAME, "member_name").send_keys('test')
3、XPath定位
XPath定位方式由XML(eXtendMarkup Language,可扩展标记语言,也是由一系列标签构成,主要实现数据交换)和 Path 两部分构成,以XML 格式的树状结构形式进行递归逐级定位。
XPath 定位的两种方式:绝对路径定位、相对路径定位。
绝对路径:从顶级父标签到当前标签的整个路径结构称为绝对路径。在使用绝对路径时,如果同级中存在多个相同标签,则通过索引进行具体选择,其索引的初始值是从1开始!但是在实际脚本开发过程中,一般不使用绝对路径,因为绝对路径的跨度较大,只要页面稍微发生变动整个定位就会失败,稳定性极差。
相对路径:表示相对当前标签而言的路径结构。常用的定位方式有以下几种。
(1)属性定位语法://标签名[@属性名=属性值]。
注意
(1)标签名可以具体,也可以使用* (表示任意标签,定义的范围会比具体的标签更广,可能会定位出多个对象)。
(2)属性值如果是字符串则需要使用引号。
(2)使用逻辑运算符可以实现多个属性定位,逻辑运算符有 and、or、not。例如,/inputl@name=uname and@pwd=upasswd7,但是一般组合的属性不会超过2个,因为设定属性越多,对脚本的依赖性就越高。
(3)嵌入函数完成XPath 定位。
text函数定位语法://标签名[text()=对应标签的文本内容]
contains函数定位语法://标签名[contains(@属性名,对应属性名的部分值)]。
starts-with 函数定位语法://标签名[starts-with(@属性名,对应属性名的前面部分值)]
ends-with 函数定位语法: //标签名[ends-with(@属性名,对应属性名的后面部分值)]
注意
一般 starts-with、ends-with 函数能完成定位的,contains 函数也能完成,所以在实际工作中 contains函数应用得更多。
实例代码如下。
driver.find_element_by_xpath("//button[text()=登录]").click() driver.find_element_by xpath("//button[contains(@class,login')]").click() driver.find_element_by xpath("//button[starts-with(text(),登')]").click()
XPath是用于定位XML文档中节点的技术,HTML\XML都采用网页DOM树状标签的结构进行编写的,所以可以通过XPath方法分析其节点信息。Selenium Python也提供了类似的方法来跟踪网页中的元素。
XPath定位元素方法不同于按照ID或Name属性的定位方法,前者更加的灵活、方便。
比如想通过ID属性定位超链接信息,但是ID属性值都是相同,即“link”,如果没有其他属性,那我们怎么实现呢?此时可以借助XPath方法进行定位元素。这也体现了XPath方法的一个优点:
当没有一个合适的ID或Name属性来定位所要查找的元素时,你可以使用XPath去定位这个绝对元素(但作者不建议定位绝对元素),或者定位一个有ID或Name属性的相对元素位置。
driver.find_element(By.XPATH, "//*[@id='member_name']").send_keys('test')
4、链接文本定位超链接:link_text、partial_link_text
当需要定位一个锚点标签内的链接文本(Link Text)时就可以使用该方法。该方法将返回第一个匹配这个链接文本值的元素。如果没有元素匹配这个链接文本,将抛出一个NoSuchElementException异常。
代码示例:
driver.find_element_by_link_text("忘记密码?").click()
driver.find_element_by_partial_link_text("忘记").click()
driver.find_element(By.LINK_TEXT,"忘记密码?").click() driver.find_element(By.PARTIAL_LINK_TEXT,"忘记").click()
partial_link_text是link_text的一种补充,有些文本链接较长时,可以截取一部分进行定位,只要这一部分信息可以唯一的识别出这个链接。
5、tag_name定位
该方法是通过标签名(Tag Name)定位元素,它将返回第一个用Tag Name匹配定位的元素。如果没有元素匹配,将会返回一个NoSuchElementException异常。
用这种定位方法定位通常比较困难,因为同一个页面中相同名称的标签往往比较多。
driver.find_elements_by_tag_name('input') # 获取多个标签之后,通过下标索引获取目标进行操作 driver.find_elements(By.TAG_NAME,"input")[0].send_keys('test')
6、class_name定位
该方法是通过类属性名(Class Attribute Name)定位元素,它将返回第一个用类属性名匹配定位的元素。如果没有元素匹配,将会返回一个NoSuchElementException异常。
用这种定位方法定位通常比较困难,因为同一个页面中相同类名的属性也会比较多。
此处,登录按钮的类名属性应该的是只有一个,可以复制并通过:Ctrl+F搜索该名称看一下
代码示例
driver.find_element_by_class_name("btn login-btn") driver.find_element(By.CLASS_NAME,"btn login-btn")
7、css_selector定位
该方法是通过CSS选择器(CSS Selectors)定位元素,它将返回第一个与CSS选择器匹配的元素。如果没有元素匹配,将会返回一个NoSuchElementException异常。
CSS定位和XPath定位的使用是同等重要的,两者又很多类似的地方,但是无论从性能还是语法上,CSS定位更具优势。CSS选择器定位方法是比较难的一个方法,详细的CSS选择器参考手册:https://www.w3school.com.cn/cssref/css_selectors.asp
# 忘记密码 driver.find_element(By.CSS_SELECTOR,"#login_normal_form > div:nth-child(4) > a:nth-child(1)").click()
# 输入用户名
driver.find_element(By.CSS_SELECTOR,"#member_name").click()
二、父子定位、二次定位
父子定位:如果当前标签或者子标签中不存在任何属性可以作为定位方式,则可以查找当前标签的父标签,是否存在可以定位的属性,如果父级还没有,则可以继续往上一级查找,以此类推,直到查找到可以定位的标签。
二次定位:可以先定位到可以定位的其中一级标签获取对象,然后再通过该对象作为基准再次定位(二次定位实际就是实现多个节点的划分,通过节点进一步明确定位的标签和元素)
当前级中如果没有属性可以定位,则可以寻找上一级父标签进行定位,然后再接子标签;注意父标签的属性是否于其他的标签相同。
# 多观察当前标签周围的标签的属性。
三、JS定位
除此之外,还有一些是使用基础定位方式无法解决的问题,比如Windows窗口、浏览器滚动条等,这时需要使用到JS定位。
JS实际上是使用了DOM树的定位方式
DOM树表示树形展示的层级结构,层级结构很好的体现了元素与元素之间的联系。
常用的几种定位方式如下:
- id定位:document.getElementsById()
- name定位:document.getElementsByName()
- tag定位:document.getElementsByTagName()
- class定位:document.getElementsByClassName()
- CSS定位:document.querySelectorAll()
以上方法属于document对象的方法,document表示当前HTML页面的对象,具体的API可以参考:https://developer.mozilla.org/zh-CN/docs/Web/API
注意:
1、在使用以上方式进行定位的时候,name、tag、class、CSS定位返回的对象都是复数形式,所以需要索引获取其具体的对象。
2、文本赋值使用的是该方法的value属性,如果是按钮则直接使用click()方法
3、如果已通过定位元素获取了对应的对象,但是该对象无法直接完成某些事件的操作,则可以通过该对象调用该标签的中声明的操作事件的属性值(使用JS脚本完成),然后直接使用execute_script执行
代码示例(注意这里的单复数形式)
username_js = "document.getElementById('member_name').value='test';" password_js = "document.getElementById('member_password').value='123456';" driver.execute_script(username_js) driver.execute_script(password_js) username_js2 = "document.getElementsByName('member_name')[0].value='test';" password_js2 = "document.getElementsById('member_password')[0].value='123456';" driver.execute_script(username_js2) driver.execute_script(password_js2)
可以在浏览器控制台验证
四、jQuery定位
有些开发人员开发的系统中,前面的定位方式都不适用,都无法定位出来,此时需要使用jQuerry定位,然后使用键盘操作
常用jQuery定位方法有两种:
使用jQuery选择器来完成元素的选择操作,可以直接获取一个或者一组元素;
通过jQuery遍历来选择元素,这种方式常用在获取层级较为复杂的页面元素的情况。
选择器参考手册:https:www.w3school.com.cn/jqurey/jquery_ref_selectors.asp
jQuery语法实际上是为了HTML元素的选择设置的,不仅可以完成定位,还可以直接堆元素完成一些具体的操作,基础语法:$(selector).action()
通过$符号进行定义,selector选择器主要用于获取具体的HTML元素,而action()用于实现堆获取的元素具体的操作。
常用操作:
$(selector).val('input_value') input_value表示要输入的文本 $(selector).val('') 如果为空,则执行后是清空的意思。 $(selector).click() 表示单击操作
登录框示例代码:
# 定位用户名 username_jq = "$('input:first').val('test')" driver.execute_script(username_jq) # 定位登录按钮 login_jq = "$('.btn login-btn').click()" driver.execute_script(login_jq) # 获取对应标签的文本信息 text_jq = "return $('#属性名').text();" print(driver.execute_script(text_jq)) # 引用CSS样式中的表示形式,获取属性href值 get_herf_script = "return $('#login_normal_form > div:nth-child(4) > a:nth-child(1)').attr('herf')" print(driver.execute_script(get_herf_script))
jQuery常用方法总结:
4.1、核心方法
$('element').length:元素的个数,length是属性
$('element').size():元素的个数,size()是方法
$('element').get():获取元素在页面中的集合,以数组的形式存储
$('element').get(index):功能和上面相同,index表示第几个元素,是数组的下标。
$('element').get().reverse():将得到的数组逆序排列
$('element1').index($('element2')):获取元素2在元素1中的索引值
4.2、基本对象获取
注意:这里获取的都是jQuery对象而不是DOM对象,但是可以互相转换
$("*"):表示获取所有对象,不常用
$("#xxxx"):获得id=xxxx的元素对象(id可以是标签的id也可以是CSS样式的id),常用
$("input[name='username']"):获取input标签中name=’username‘的元素对象,常用
$(".abc"):获得class=’abc‘的元素对象,常用
$("div"):标签选择器,选择所有的div元素,常用
$("#a,.b,span"):表示获得id是a的元素、使用了样式b的元素及所有span元素
$("#a .b p"):表示获得id是a,并且使用了样式b的所有p元素
4.3、层级元素获取
$("element1 element2 element3 ..."):前面是父级,后面是子集
$("div > p"):获取div元素后面的直接后代p元素
$("div + p"):获取div元素后面的第一个p元素
$("div ~ p"):获取div元素之后所有同层级的p元素
4.4、简单对象获取
$("element:first"):HTML页面中某类元素的第一个元素
$("element:last"):HTML页面中某类元素的最后一个元素
$("element:not(selector)"):去除所有与给定选择器匹配的元素,如$("input:not(:checked)")表示选择所有没有选中的复选框
$("element:even"):获得偶数行
$("element:odd"):获得奇数行
$("element:eq(index)"):匹配一个给定索引值的元素
$("element:gt(index)"):匹配所有大于给定索引值的元素
$("element:lt(index)"):匹配所有小于给定索引值的元素
4.5、内容对象的获取和对象可见性
$("element:contains(text)"):获得包含text的元素
$("element:empty"):获得不包含子元素或文本的元素
$("element:partnt"):获得包含子元素或文本的元素
$("element:has(selector)"):获得包含某个元素的元素,如$("p:has(span)")表示所有包含span元素的p元素
$("element:hidden"):选择所有不可见元素
$("element:visible"):选择所有可见元素
4.6、其他对象获取方式
$("element[id]"):获取所有带有id属性的元素
$("element[attribute = youlike]"):获得所有attribute属性为youlike的元素
$("element[attribute != youlike]"):获得所有attribute属性不是youlike的元素
$("element[attribute ^= youlike]"):获得所有attribute属性不是以youlike开头的元素
$("element[attribute $= youlike]"):获得所有attribute属性不是以youlike结尾的元素
$("element[attribute *= youlike]"):获得所有attribute属性包含youlike的元素
$("element[selector1][selector2][...]"):复合属性选择器。例如,$("input[id][name][value=youlike]")表示获得带有id、name属性且value属性是youlike的input元素
4.7、子元素的获取
$("element:nth-child(index)"):选择父级下面的第N个元素
$("element:nth-child(even)"):选择父级下面的偶数
$("element:nth-child(odd)"):选择父级下面的奇数
$("element:nth-child(3n+)"):执行表达式
$("element:first-child"):选择父级下面的第一个子元素
$("element:last-child"):选择父级下面的最后一个子元素
$("element:only-child"):选择父级下面的唯一一个子元素,例如,dt在dl列表中唯一,那么将选择dt
4.8、表单对象获取
$(:input):查找所有的input元素,包括下拉列表、文本域、单选框、复选框等
$(:text):匹配所有的单行文本框
$(:password):匹配所有的密码框
$(:radio):匹配所有的单选按钮
$(:checkbox):匹配所有的复选按钮
$(:submit):匹配所有的提交按钮
$(:image):匹配所有的图像域
$(:reset):匹配所有的重置按钮
$(:button):匹配所有的按钮
$(:file):匹配所有的文件上传域
$(:hidden):匹配所有的不可见元素或者type为hidden的元素
$(:enabled):匹配所有可用的input元素。例如,radio:enabled表示匹配所有可用的单选按钮
$(:disabled):匹配所有的不可用input元素,作用与上相反
$(:checked):匹配所有选中的复选框元素
$(:selected):匹配所有下拉列表
4.9、元素属性的设置与移除
$("element").attr(name):取得第一个匹配的属性值,例如$("img").attr("src")
$("element".attr(key,value)):为某一个元素设置属性
$("element".attr({key:value,key1:value,...})):为某一个元素一次性设置多个属性
$("element").attr(key,function):为某一个元素设置一个计算的属性值
$("element").removeAttr(name):为某一个元素移除name属性
思考:隐藏元素如何操作?
隐藏元素是可以定位的,其定位方式与普通元素的定位方式完全一样,只是隐藏元素无法进行操作。
可以通过JS脚本定位到该元素,获取对应的元素对象,然后通过removeAttribute和setAttribute两个方法完成属性的删除或者重新赋值操作,使当前的属性处于显示状态,即可完成操作。