Selenium之元素定位几种方式

落花浮王杯 提交于 2019-12-06 02:48:12

selenium提供了8种定位方式,下表列出了各个定位方式和在Python中对应的方法:

selenium中各定位方式对应的Python方法
id find_element_by_id()
name find_element_by_name()
class name find_element_by_class_name()
tag name find_element_by_tag_name()
link text find_element_by_link_text()
partial link text find_element_by_partial_link_text()
xpath find_element_by_xpath()
css selector find_element_by_css_selector()

接下来,具体的示例来学习一下各定位方式是如何使用的。

通过id获取元素

打开Pythonav的注册页面。键盘F12查看源码,定位到用户注册输入框(其他输入框都是这么玩的),如下图。

运行如下代码:

from selenium import webdriver

# 创建Chrome WebDriver实例
browser = webdriver.Chrome()
# 获取URL网页
browser.get('https://pythonav.com/register/')
# 根据id获取对应的元素
username = browser.find_element_by_id('id_username')
# 打印获取结果
print(username)  # <selenium.webdriver.remote.webelement.WebElement (session="2e4891511f03cc1baaa12c0426dc7b05", element="0.5508062663208302-1")>
# 打印id
print(id(username))  # 61771472
# 打印类型
print(type(username))  # <class 'selenium.webdriver.remote.webelement.WebElement'>
# 关闭浏览器
browser.quit()

会看到,打开的浏览器,很快就关闭了,但我们成功的打印出结果,也就是获取到了想要的元素了。

为指定元素的属性赋值

现在,已经能定位到用户名的输入框,也就可以自动的填写该input框,来试试看:

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.pythonav.com/register/')
# 根据id获取对应的元素
username = browser.find_element_by_id('id_username')
# 为元素的value属性赋值
username.send_keys('张开')
# 睡3秒
time.sleep(3)
# 清空元素的value属性
username.clear()
# 再睡2秒
time.sleep(2)
# 关闭浏览器
browser.quit()

模拟短信验证操作

继续来模拟一下发送短信验证码的操作:

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get("http://www.pythonav.com/register/")
# 根据id获取对应的元素
phone = browser.find_element_by_id('id_telephone')
# 回填手机号
phone.send_keys('18xxooxxoo98k')  # 这里填写一个真实可用的手机号
# 睡一会儿
time.sleep(5)
# 获取发送短信的按钮id
ele = browser.find_element_by_id('smsBtn')
# 模拟按钮的点击事件
ele.click()
time.sleep(2)
browser.quit()

自动化注册Pythonav网站

继续完善自动用户注册的代码:

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get("http://www.pythonav.com/register/")
# 填写用户名
browser.find_element_by_id('id_username').send_keys('张开11')
# 填写密码
browser.find_element_by_id('id_password').send_keys('zhangkai111')
# 确认密码
browser.find_element_by_id('id_confirm_password').send_keys('zhangkai111')
# 回填手机号
browser.find_element_by_id('id_telephone').send_keys('18xxooxx0098k')  # 需要一个真实的手机号
# 点击获取短信验证码
browser.find_element_by_id('smsBtn').click()
# 睡它40秒,这里是为了等发送短信验证码,然后我们手动填进去
time.sleep(40)
# 最后点击注册按钮
browser.find_element_by_id('submit').click()
# browser.find_element_by_id('submit').submit()
time.sleep(100)
browser.quit()

如果顺利的收到验证码,并且在40秒内填进去了,然后就会发现,注册成功了!下图为证!

需要注意的是:按钮提交时,除了click之外,还有一个submit,区别是click就是单纯的点一下。而submit是完成了表单提交,需要携带表单信息进行提交的。如果是button按钮的话,只能使用click,而不能使用submit

name

通过PythonAV来学习name定位是什么鬼。

import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://pythonav.com/login/')
# 通过标签的name属性
img = browser.find_element_by_name("username")  
print(img.tag_name)  # input
time.sleep(2)
browser.quit()

上例,通过获取用户名的inpu框来完成定位。

需要注意的是:browser.find_element_by_name("username")这种定位方式只能通过name属性来定位。

通过class name获取单个元素属性

通过路飞学城的登录页面来学习通过class name定位。

由上图可以看到,页面中的记住密码前的复选框,是由span标签通过css样式实现的。我们用class name定位来完成自动勾选任务。

import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.luffycity.com/signin')
# 通过class获取span标签
span = browser.find_element_by_class_name(name='no')
# 点击事件
span.click()
print(span.get_attribute(name='class'))  # no yes
# 获取当前元素的内容
print(span.text)  # ✓
time.sleep(3)
# 再次点击span标签
span.click()
time.sleep(5)
browser.quit()

实现相对简单,再来点有点难度的,我们通过class name定位来获取路飞学城首页轮播图信息。

import time
from selenium import webdriver
browser = webdriver.Chrome()

browser.get('https://www.luffycity.com/home')
# 根据class获取img元素
img = browser.find_element_by_class_name('banner')
# 获取元素的指定(src)属性
print(img.get_attribute('src'))  # https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png
print(img.get_property('src'))  # https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png
time.sleep(5)
browser.quit()

通过打印结果来看,我们已经成功的获取到了指定的元素属性。这里需要补充的是:get_attribute方法获取元素的特性,类似js中的setattribute("",""),自定义属性设置,而get_property方法也是获取当前元素的属性,但一般都是获取原生的属性,两种方法有一定的互通性

通过class name获取多个元素属性

上例代码没有问题,但我们仅获取了一个图片的属性,那如何获取多个呢?

import time
from selenium import webdriver
browser = webdriver.Chrome()

browser.get('https://www.luffycity.com/home')
# 根据class获取img元素
imgs = browser.find_elements_by_class_name('banner')
# 获取元素的指定(src)属性
print([i.get_attribute(name='src') for i in imgs])
time.sleep(5)
browser.quit()
'''打印结果
[
    'https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png', 
    'https://hcdn1.luffycity.com/static/frontend/index/PCbanner_1548828967.892487.jpeg', 
    'https://hcdn1.luffycity.com/static/frontend/activity/banner1_1553586989.5872993.png', 
    'https://hcdn1.luffycity.com/static/frontend/index/banner1(4)_1539945492.0492468.png', 
    'https://hcdn1.luffycity.com/static/frontend/index/banner_1553684636.466433.png', 
    'https://hcdn1.luffycity.com/static/frontend/index/banner11_1538122470.2779157.png', 
    'https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png', 
    'https://hcdn1.luffycity.com/static/frontend/index/PCbanner_1548828967.892487.jpeg'
]
'''

上例中,获取单个元素使用browser.find_element_by_class_name('banner'),而要想获取多个,就要使用browser.find_elements_by_class_name('banner'),别忘了加s!而当加了s后,获取到的是一个列表,而列表的话,不能直接img.get_attribute('src'),而是想方设法的取列表中的每一个元素,我们对每一个元素再使用i.get_attribute(name='src')就没问题了。


see also:luffycity

限定span标签范围

还是通过路飞学城首页的菜单列表来完成tag name演示,我们要使用selenium自动完成点击任务。

首先,要获取所有的span标签,但我们思考,这个页面中有很多个span标签,而只需要对这个几个span标签做点击事件,所以,我们首先要限定范围。经过一番观察,先获取所有span标签外部的nav标签。

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.luffycity.com/home')
nav = browser.find_element_by_tag_name(name='nav')
print(nav.text)
time.sleep(3)
browser.quit()
'''打印结果
免费课
轻课
学位课
题库
'''

循环绑定click事件

有了范围就好办了,再从nav标签内部找所有的span标签,然后给每一个span标签绑定一个click事件,然后自动执行就完成了。

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.luffycity.com/home')
# 获取span的外部nav标签
nav = browser.find_element_by_tag_name(name='nav')
# 然后再获取nav内部的所有span标签
span_list = nav.find_elements_by_tag_name(name='span')
for i in span_list:
    i.click()
    time.sleep(3)
time.sleep(3)
browser.quit()
'''
selenium.common.exceptions.WebDriverException: Message: unknown error: Element <span _v-0ec42e90="">...</span> is not clickable at point (197, 45). Other element would receive the click: <div class="mbox" _v-5f981f7a="" style="">...</div>
  (Session info: chrome=73.0.3683.86)
  (Driver info: chromedriver=2.46.628402 (536cd7adbad73a3783fdc2cab92ab2ba7ec361e1),platform=Windows NT 10.0.14393 x86_64)
'''

处理弹出框

上例代码逻辑没有问题!但是报错了,观察报错信息(说的好像看的懂似的!),每当打开路飞学城首页的时候,首先会弹出一个弹出框,不把它叉掉就没有办法干别的,肯定是它搞的鬼,必须先干掉他才能执行后续的点击事件!

import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.luffycity.com/home')
time.sleep(1)
# 首先关闭弹出框
browser.find_element_by_class_name('guan').click()
time.sleep(1)
# 获取span的外部nav标签
nav = browser.find_element_by_tag_name(name='nav')
# 然后再获取nav内部的所有span标签
span_list = nav.find_elements_by_tag_name(name='span')
for i in span_list:
    i.click()
    time.sleep(3)
time.sleep(3)
browser.quit()

上例代码,首先关闭弹出框,在为nav标签内部的所有span标签绑定事件,然后一切就会循环起来了。

see also:luffycity | pythonav

通过link text定位

通过PythonAV来学习link text/partial link text定位。

由上图可以看到,PythonAV和路飞学城在标签切换这里是不一样的,PythonAV是a标签(毕竟是av嘛)!而路飞学城是span标签。所以,针对PythonAV,采用另一个元素定位方式来完成。

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://pythonav.com/index/')
time.sleep(2)
# 获取超链接,并且点击
browser.find_element_by_link_text('免费视频').click()
time.sleep(3)
browser.quit()

上例,使用browser.find_element_by_link_text('免费视频').click()来完成定位和点击任务。

通过partial link text定位

partial link text是什么鬼?

import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://pythonav.com/index/')
browser.find_element_by_partial_link_text('免费').click()
time.sleep(2)
browser.find_element_by_partial_link_text('知识库').click()
time.sleep(2)
# browser.find_element_by_link_text('内部').click()  # 报错
browser.find_element_by_link_text('内部资料').click()  # 报错
time.sleep(2)
browser.quit()

由上例可以看到,使用partial link text定位时,使用标签连接内容部分内容即可,而通过link text定位时,链接内容必去是完全匹配才能成功。

小结:

  • link text/partial link text都仅能用于超链接标签的定位,其他的标签都不可以。
  • link text是精确定位,也就是说超链接内的内容必须完全匹配才能定位成功。
  • partial link text是模糊定位,只要超链接的内容包含该指定内容既可以匹配到。根据不同的场景,需谨慎使用。

css selector

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.luffycity.com/home')
time.sleep(1)
# 首先关闭弹出框
browser.find_element_by_class_name('guan').click()
# 通过css selector
res = browser.find_element_by_css_selector('.luffy-home .title p[_v-2f0761bc]')
print(res.tag_name)  # p
time.sleep(2)
browser.quit()
除此之外,css selector还可以这么用:

import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.luffycity.com/home')
time.sleep(1)
# 首先关闭弹出框
browser.find_element_by_css_selector("img[class='guan']").click()
time.sleep(2)
browser.quit()

虽然css selector定位不如id class tag等定位方法直接,但是css selector性能高,尤其是在IE浏览器中(老版IE浏览器对XPath支持不太好,尤其是IE11之前)。并且selenium也推荐使用css selector定位。


see also:【Selenium专题】元素定位之CssSelector

XPath

选取节点

XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下表列举了常用的路径表达式:

表达式描述
node name 选取此节点的所有子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性

实例

下表列举了一些表达式的应用及描述:

路径表达式描述
bookstore 选取bookstore元素的所有子节点
/bookstore 选取根元素bookstore。如果路径起始于/,则此路径始终代表到某元素的绝对路径
bookstore/book 选取属于bookstore元素下的所有book元素
//book 选取book子元素,而不管它们在文档中的位置
bookstore//book 选择属于bookstore元素的所有book元素,而不管它们位于bookstore之下的什么位置
//@book 选取名为book的所有属性

谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
下表列举了带有谓语的一些路径表达式,以及表达式结果:

路径表达式结果
/bookstore/book[1] 选取属于bookstore子元素的第一个book元素
/bookstore/book[last()] 选取属于bookstore子元素的最后一个book元素
/bookstore/book[last()-1] 选取属于bookstore子元素的倒数第二个book元素
/bookstore/book[position()<3] 选取最前面的两个属于bookstore元素的子元素的book元素
//title[@book] 选取所有拥有名为book的属性的title元素
//title[@book='lang'] 选取所有book属性为lang的title元素
/bookstore/book[price>35.00] 选取bookstore元素的所有book元素,并且其中price属性的值必须大于35.00
/bookstore/book[price>35.00]/title 选取bookstore元素中的title元素,过滤条件是price元素值必须大于35.00

选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符描述
* 匹配任何元素节点
@* 匹配任何属性节点
node() 匹配任何类型的节点

实例
下表中列举了一些路径表达式及对应的结果:

路径表达式结果
/bookstore/* 选取bookstore元素的所有子元素
//* 选取文档中的所有元素
//title[@*] 选取所有带有属性的title元素

选取若干路径

通过在路径表达式中使用|运算符,我们可以选取若干个路径。

实例

下表中列举了一些路径表达式及对应的结果:

路径表达式结果
//book/title | //book/price 选取book元素下所有title和price元素
//title | //price 选取文档中所有title和price元素
/bookstore/book/title | //price 选取属于bookstore元素下的book元素中的所有title元素,以及文档中所有price元素

是时候写点代码了:

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.luffycity.com/home')
time.sleep(1)
# 首先关闭弹出框
browser.find_element_by_css_selector("img[class='guan']").click()
img = browser.find_element_by_xpath('//ul/li/img[@_v-2f0761bc]')
# img = browser.find_element_by_xpath('/ul/li/img[@_v-2f0761bc]')  # 报错
print(img.get_attribute('src'))
time.sleep(2)
browser.quit()

上例中,报错的原因是在根据XPath找路径的时候,其实位置如果是单个/,表示绝对路径,要从HTML的根节点开始查找,而双//则是相对路径。

除此之外,Chrome浏览器还支持无脑获取标签的XPath,那就是万能的右键copy XPath

妥妥的准!

节点匹配

再介绍一些XPath中节点匹配的基本方法。

路径匹配

路径匹配与文件路径的表示相仿,比较好理解,有以下几个符号:

  • /表示节点路径,如/A/B/C表示节点A的子节点B的子节点C/表示根节点。
  • //表示所有路径以//后指定的子路径结尾的元素,如//D表示所有的D元素;如果是//C/D表示所有父节点为CD元素。
  • *表示路径的通配符,如/A/B/C/*表示A元素下的B元素下的C元素下的所有子元素。

位置匹配

对于每一个元素,它的各个子元素都是有序的:

  • /A/B/C[1]表示A元素下的B元素下的C元素下的第一个子元素。
  • /A/B/C[last()]表示A元素下的B元素下的C元素下最后一个子元素。
  • /A/B/C[position()>2]表示A元素下的B元素下的C元素下的位置号大于2的元素。

属性及属性值

在XPath中可以利用属性及属性值来匹配元素,需要注意的是:元素的属性名前要有@前缀,例如:

  • //B[@id]表示所有具有属性id的B元素。
  • //B[@*]表示所有具有属性的B元素。
  • //B[not(@*)]表示所有不具有属性的B元素。
  • //B[@id="b1"]表示id值为b1的B元素。

see also: XPath 教程

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!