相对定位历史
- 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
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>relative</title></head><body>
DATE:<inputid="date"type="text">
USER:<inputid="username"type="text"><br>
CODE:<inputid="code"type="text">
PASS:<inputid="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的源码中有这么一段
deffind_element(self, by=By.ID, value=None)-> WebElement:ifisinstance(by, RelativeBy):
elements = self.find_elements(by=by, value=value)ifnot elements:raise NoSuchElementException(f"Cannot locate relative element with: {by.root}")return elements[0]
也就是说你传入的by不仅仅可以是下面这8个,还可以是RelativeBy对象
classBy:"""
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)
deffind_elements(self, by=By.ID, value=None)-> List[WebElement]:ifisinstance(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
classRelativeBy:def__init__(self, root: Dict[By,str]=None, filters: List =None):
self.root = root
self.filters = filters or[]defabove(self, element_or_locator: Union[WebElement, Dict]=None)->"RelativeBy":ifnot 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在同一个文件中
代码如下
deflocate_with(by: By, using:str)->"RelativeBy":assert by isnotNone,"Please pass in a by argument"assert using isnotNone,"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中吧,有空找下,或者哪个大佬知道的可以指点下
版权归原作者 wuxianfeng023 所有, 如有侵权,请联系我们删除。