Python/Selenium:如何正确结合any_of与element_to_be_clickable使用?
我在Selenium项目中需要等待三个元素中的任意一个变为可点击状态,当前使用的代码如下:
LOC1 = (By.ID, "ID1") LOC2 = (By.ID, "ID2") LOC2 = (By.ID, "ID3") # 注:此处存在笔误,应为LOC3 def get_correct_el(self): def wait_for_el(): return self.robot.wait.until( EC.any_of( EC.element_to_be_clickable(LOC1), EC.element_to_be_clickable(LOC2), EC.element_to_be_clickable(LOC3), ) ) el = wait_for_el() while el != self.driver.find_element(*LOC1): el.click() el = wait_for_el()
代码可正常运行,但Pylance报出以下类型错误:
Argument of type "(WebDriverOrWebElement) -> (WebElement | Literal[False])" cannot be assigned to parameter "expected_conditions" of type "(D@any_of) -> T@any_of" in function "any_of"
Type "(WebDriverOrWebElement) -> (WebElement | Literal[False])" cannot be assigned to type "(Unknown) -> WebElement"
Function return type "WebElement | Literal[False]" is incompatible with type "WebElement"
Type "WebElement | Literal[False]" cannot be assigned to type "WebElement"
"Literal[False]" is incompatible with "WebElement"PylancereportArgumentType
(function) def element_to_be_clickable(mark: WebElement | Tuple[str, str]) -> ((WebDriverOrWebElement) -> (WebElement | Literal[False]))
An Expectation for checking an element is visible and enabled such that you can click it.element is either a locator (text) or an WebElement
我已确认是any_of的类型校验问题:any_of期望参数类型为*expected_conditions: Callable[[D], T],但element_to_be_clickable的返回类型为Callable[[WebDriverOrWebElement], Union[Literal[False], WebElement]],二者签名不匹配。想请教该错误是否可忽略,以及如何正确使用any_of与element_to_be_clickable。
1. 错误是否可忽略?
可以忽略。该报错仅为Pylance的静态类型检查提示,Selenium的any_of内部逻辑完全能处理element_to_be_clickable返回的False或WebElement类型——当条件不满足时返回False,满足时返回对应元素,any_of会正确识别第一个满足的条件并返回结果,不影响代码实际运行。
若不想看到该报错,可在EC.any_of代码行添加类型忽略注解:
EC.any_of( EC.element_to_be_clickable(LOC1), EC.element_to_be_clickable(LOC2), EC.element_to_be_clickable(LOC3), ) # type: ignore[arg-type]
2. 正确使用方式
方式一:修正类型兼容问题(适合类型洁癖)
自己封装一个类型兼容的element_to_be_clickable版本,确保返回的Callable仅返回WebElement(因为wait.until会自动等待到条件返回真值,实际不会返回False):
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.remote.webelement import WebElement from typing import Callable, Tuple, Any def my_element_to_be_clickable(locator: Tuple[str, str]) -> Callable[[Any], WebElement]: def condition(driver: Any) -> WebElement: el = EC.element_to_be_clickable(locator)(driver) assert el is not False, "目标元素不可点击" return el return condition # 使用时替换原方法 def get_correct_el(self): def wait_for_el(): return self.robot.wait.until( EC.any_of( my_element_to_be_clickable(LOC1), my_element_to_be_clickable(LOC2), my_element_to_be_clickable(LOC3), ) ) el = wait_for_el() while el != self.driver.find_element(*LOC1): el.click() el = wait_for_el()
方式二:简化逻辑(更实用)
先修正代码中的笔误:将LOC2 = (By.ID, "ID3")改为LOC3 = (By.ID, "ID3"),避免后续逻辑出错。之后直接保留原逻辑并添加类型忽略即可,因为实际运行无问题。
内容的提问来源于stack exchange,提问作者kiesel

