• Selenium之元素定位几种方式


    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 教程

  • 相关阅读:
    PHP js使用ajax异步处理方式请求PHP,解决数组中文乱码
    PHP Apache服务配置
    opencv高斯背景建模
    Opencv,腐蚀,膨胀,轮廓检测,轮廓外接多边形
    opencv删除二值图中较小的噪点色块
    opencv图像操作
    opencv统计二值图黑白像素个数
    JAVA常用工具类
    Netty4 学习笔记之四: Netty HTTP服务的实现
    分享一些JAVA相关资源
  • 原文地址:https://www.cnblogs.com/sundawei7/p/11958873.html
Copyright © 2020-2023  润新知