前言:
Selenium传统定位基本能解决80%的定位需求,但是还是有一些复杂场景传统定位定不到的场景。在现在框架横行的年代以及快速迭代的开发流程中,开发为了完成任务很多html文档都没有id,class或者其他易于识别的dom元素,虽然使用绝对路径能够定位到,但是你不能保证下次是有效的。因此大部分定位语法基本是使用相对路径定位的,但是相对定位也有个缺点就是有时候定位会有N个匹配,特别是笔者公司做的报表平台,很多控件是复用的,因此你定位到的元素不一定就是你想交互的元素,使用Selenium相对定位能很好解决这个痛点。
相对定位
Selenium 4引入了相对定位器(以前称为友好定位器)。当不容易为所需元素构造定位器,但很容易在空间上描述元素相对于具有易于构造定位器的元素的位置时,这些定位器是有用的
工作原理
Selenium使用JavaScript函数getBoundingClientRect()来确定页面上元素的大小和位置,并可以使用这些信息来定位相邻的元素。找到相关元素。
相对定位器方法可以将先前定位的元素引用或另一个定位器作为原点的参数。在这些示例中,我们将仅使用定位器,但您可以在最后的方法中用元素对象交换定位器,它也会起作用。
让我们考虑下面的例子来理解相对定位器。
可用的相对定位
Above
如果由于某种原因,电子邮件文本字段元素不容易识别,但密码文本字段元素可以识别,我们可以使用它是密码元素“上方”的“输入”元素这一事实来定位文本字段元素。
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import locate_with
email_locator = locate_with(By.TAG_NAME, "input").above({By.ID: "password"})
Below
如果密码文本字段元素由于某种原因不容易识别,但电子邮件文本字段元素是,我们可以使用它是电子邮件元素“下面”的“输入”元素这一事实来定位文本字段元素。
password_locator = locate_with(By.TAG_NAME, "input").below({By.ID: "email"})
Left of
如果由于某种原因,取消按钮不容易识别,但提交按钮元素是,我们可以使用取消按钮元素是提交元素“左侧”的“按钮”元素这一事实来定位取消按钮元素。
cancel_locator = locate_with(By.TAG_NAME, "button").to_left_of({By.ID: "submit"})
Right of
如果提交按钮由于某种原因不容易识别,但取消按钮元素是,我们可以使用提交按钮元素是取消元素右侧的“按钮”元素这一事实来定位提交按钮元素。
submit_locator = locate_with(By.TAG_NAME, "button").to_right_of({By.ID: "cancel"})
Near
如果相对定位不明显,或者根据窗口大小而变化,可以使用near方法来识别距离所提供定位器最多50像素的元素。一个很好的用例是使用一个表单元素,该元素没有一个容易构建的定位器,但它的相关输入标签元素有。
email_locator = locate_with(By.TAG_NAME, "input").near({By.ID: "lbl-email"})
链式相对定位
如果需要,也可以链式定位器。有时,元素最容易被识别为既在一个元素的上方/下方,又在另一元素的右侧/左侧。
submit_locator = locate_with(By.TAG_NAME, "button").below({By.ID: "email"}).to_right_of({By.ID: "cancel"})
其中to_right_of、above、to_left_of、below 传参类型为element_or_locator: Union[WebElement, Dict] = None,可以是webelement如:
news_loc = (By.XPATH, "//*[@id='hotsearch-content-wrapper']//li//a//span[@class='title-content-title']")
new_element=driver.find_element(*news_loc)
或者字典:
to_right_of({By.XPATH: "//*[@id='s-usersetting-top']"})
near方法多了一个int类型的入参表示相对其他定位元素的距离。可以使用near方法来识别距离所提供定位器最多50像素的元素
near(30)
相对于WebElement的相对定位
在Selenium webdriver.remote.webelement源码中发现存在find_element和find_elements2个方法,这在查找元素子节点元素是非常有效的,避免了写不必要的定位语法
def find_element(self, by=By.ID, value=None) -> WebElement:
def find_elements(self, by=By.ID, value=None) -> List[WebElement]:
如以下示例,定位这个百度热搜,可以先定位到上面一级,然后根据上面一级往下定位
element=driver.find_element(By.ID,'hotsearch-content-wrapper')
elements=element.find_elements(By.TAG_NAME,'li')
实例演示
import logging
import time
import pytest
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import locate_with
class TestRelative:
@pytest.mark.skip
def test_relative_loc_locate_with(self):
# 点击百度新闻信息
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
driver.maximize_window()
# driver.implicitly_wait(15)
news_loc = (By.XPATH, "//*[@id='hotsearch-content-wrapper']//li//a//span[@class='title-content-title']")
# 定位后返回RelativeBy对象,需要用find_elements来解析,locate_with比较适合定位多个元素的比如本例的新闻列表
input_baidu = locate_with(*news_loc)
elements = driver.find_elements(input_baidu)
for element in elements:
logging.info(element.get_attribute('textContent'))
# logging.info(element.text)
if element.get_attribute('textContent') == '书写历史 建立友谊 增进互信':
element.click()
time.sleep(1)
assert '书写历史 建立友谊 增进互信' in driver.page_source
if __name__ == '__main__':
pytest.main([])
版权归原作者 知识的宝藏 所有, 如有侵权,请联系我们删除。