• 谈谈selenium4.0中的相对定位


    相对定位历史

    • 2021-10-13 发布的 selenium 4.0 开始引入,selenium 3.X是没有的
    implement relative locator for find_element (#9902)
    
    • 4.10维护了下
    Improve near relative locator behavior (#11290)
    

    其他都是文档、异常信息方面的处理

    实例演示

    D:\selenium\demo\relative.html

    html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>relativetitle>
    head>
    <body>
        DATE:<input id="date" type="text">
        USER:<input id="username" type="text"><br>
        CODE:<input id="code" type="text">
        PASS:<input id="password" type="text">
    body>
    html>
    

    如下界面

    实例代码

    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.relative_locator import locate_with
    
    
    from selenium import webdriver
    from time import sleep
    driver = webdriver.Chrome()
    driver.get(r'D:\selenium\demo\relative.html')
    ele_date = driver.find_element('id','date')
    ele_code = driver.find_element('id','code')
    ele_user = driver.find_element('id','username')
    ele_password = driver.find_element('id','password')
    
    driver.find_element(locate_with(By.CSS_SELECTOR, "input").above(ele_code)).send_keys('code aboe')
    driver.find_element(locate_with(By.CSS_SELECTOR, "input").below(ele_user)).send_keys('user below')
    driver.find_element(locate_with(By.CSS_SELECTOR, "input").to_left_of(ele_password)).send_keys('pass left')
    driver.find_element(locate_with(By.CSS_SELECTOR, "input").to_right_of(ele_date)).send_keys('date right')
    driver.find_element(locate_with(By.CSS_SELECTOR, "input").near(ele_code)).send_keys('code near')
    

    执行效果

    相关源码说明

    find_element

    在find_element的源码中有这么一段

        def find_element(self, by=By.ID, value=None) -> WebElement:
            if isinstance(by, RelativeBy):
                elements = self.find_elements(by=by, value=value)
                if not elements:
                    raise NoSuchElementException(f"Cannot locate relative element with: {by.root}")
                return elements[0]
    

    也就是说你传入的by不仅仅可以是下面这8个,还可以是RelativeBy对象

    class By:
        """
        Set of supported locator strategies.
        """
    
        ID = "id"
        XPATH = "xpath"
        LINK_TEXT = "link text"
        PARTIAL_LINK_TEXT = "partial link text"
        NAME = "name"
        TAG_NAME = "tag name"
        CLASS_NAME = "class name"
        CSS_SELECTOR = "css selector"
    

    那如果是RelativeBy对象的话,会去调用find_elements,self.find_elements(by=by, value=value)

        def find_elements(self, by=By.ID, value=None) -> List[WebElement]:
        	if isinstance(by, RelativeBy):
                _pkg = '.'.join(__name__.split('.')[:-1])
                raw_function = pkgutil.get_data(_pkg, 'findElements.js').decode('utf8')
                find_element_js = f"return ({raw_function}).apply(null, arguments);"
                return self.execute_script(find_element_js, by.to_dict())
    

    if语句下的2行代码就是在加载findElements.js

    最后两句就是构造一个js然后去执行它,细节就不追究了

    RelativeBy

    这个class位于selenium\webdriver\support\relative_locator.py

    class RelativeBy:
        def __init__(self, root: Dict[By, str] = None, filters: List = None):
            self.root = root
            self.filters = filters or []
            
        def above(self, element_or_locator: Union[WebElement, Dict] = None) -> "RelativeBy":
            if not element_or_locator:
                raise WebDriverException("Element or locator must be given when calling above method")
    
            self.filters.append({"kind": "above", "args": [element_or_locator]})
            return self
    

    这个类提供了5个用于定位的实例方法:above below to_left_of to_right_of near

    可以看到RelativeBy对象的实例化需要2个参数,一个是root:dict类型,一个是filters : 列表类型

    可以看到above这样的方法其实没做啥,关键是对self.filters的一个处理,增加一个对应kind(与方法同名)和args,这个args操作你要去参考的元素的定位器或WebElement

    locate_with

    在实例代码中,我们用到了locate_with这个函数,这个函数跟RelativeBy在同一个文件中

    代码如下

    def locate_with(by: By, using: str) -> "RelativeBy":
        assert by is not None, "Please pass in a by argument"
        assert using is not None, "Please pass in a using argument"
        return RelativeBy({by: using})
    

    可以看到它确实是返回了一个RelativeBy的实例对象

    而它的用法跟我们的find_element就一致了,唯一的不同就是参数名,这边是using,find_element是value

    为何用它的另一方面原因是在RelativeBy的doc中这样的一段描述

            Example:
                lowest = driver.find_element(By.ID, "below")
    
                elements = driver.find_elements(locate_with(By.CSS_SELECTOR, "p").above(lowest))
    
    

    说在最后

    这东西我在工作中没有用过,因为它出生后我就进入了...

    使用过一些常见去测试它的效果,并不理想,不过是在早期的版本中做的,现在不清楚是否好用一些

    溯源的话应该可以追溯到js中吧,有空找下,或者哪个大佬知道的可以指点下

  • 相关阅读:
    requests 在 Python 3.2 中使用 OAuth 导入失败的问题与解决方案
    leetcode做题笔记129. 求根节点到叶节点数字之和
    低资源方面级情感分析研究综述
    一文搞懂浅拷贝与深拷贝到底有什么区别
    lil-gui
    如何使用html、css制作一个期末作业网站【羽毛球体育运动主题html网页设计】
    设计模式 - 适配器模式
    《使用EasyExcel在Excel中增加序号列的方法》
    javascript-基础知识
    web自动化测试 —— cypress测试框架
  • 原文地址:https://www.cnblogs.com/wuxianfeng023/p/17776496.html