在我的应用程序中,当我打开页面 XI 时,希望看到元素 A 或元素 B。它们位于 DOM 中的不同位置,可以使用它们的 id 找到,例如driver.findElement(By.id("idA"))
如何让 webdriver 找到 A 或 B?
driver.findElements(By)
当找到至少一个元素时,有一种方法会停止等待,但是这种方法迫使我对 A 和 B 使用相同的定位器。
可靠地找到 A 或 B 的正确方法是什么,这样我就不必等待隐式超时?
在我的应用程序中,当我打开页面 XI 时,希望看到元素 A 或元素 B。它们位于 DOM 中的不同位置,可以使用它们的 id 找到,例如driver.findElement(By.id("idA"))
如何让 webdriver 找到 A 或 B?
driver.findElements(By)
当找到至少一个元素时,有一种方法会停止等待,但是这种方法迫使我对 A 和 B 使用相同的定位器。
可靠地找到 A 或 B 的正确方法是什么,这样我就不必等待隐式超时?
id 为 I1 的元素或 id 为 I2 的元素
路径://E1[@id=I1] | //E2[@id=I2]
CSS:css=E1#I1,E2#I2
driver.findElement(By.xpath(//E1[@id=I1] | //E2[@id=I2]))
driver.findElement(By.cssSelector(E1#I1,E2#I2))
不要忘记fluentWait机制:
public WebElement fluentWait(final By locator){
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring(org.openqa.selenium.NoSuchElementException.class);
WebElement foo = wait.until(
new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
}
);
return foo;
};
您可以在此处获取有关 fluentWait 的更多信息
恕我直言,您的问题的解决方案如下:
fluentWait(By.xpath(//E1[@id=I1] | //E2[@id=I2]));
fluentWait(By.cssSelector(E1#I1,E2#I2))
仅供参考:这是不错的 xpath,cssSelector 手册
希望这可以帮助你。
我为此写了一个 ExpectedCondition 可以随意使用它。
public static ExpectedCondition<By> titleIs(final By[] selectors) {
return new ExpectedCondition<By>() {
public By apply(WebDriver driver) {
WebElement el=null;
for (By selector:selectors) {
try {
el = driver.findElement(selector);
} catch (NoSuchElementException ex) {
// ignore as we are waiting for that to stop
}
if (el!=null) return selector;
}
return null;
}
};
}
这是我的解决方案,它像其他人建议的那样使用流利的等待。您需要用驱动程序对象或您自己的获取它的方法替换对 getDriver() 的任何调用或对驱动程序的引用。
/**
* Waits for any one of a given set of WebElements to become displayed and
* enabled.
*
* @param locators
* An array of locators to be sought.
* @param timeout
* Timeout in seconds.
*/
protected void waitForOneOfManyToBePresent(By[] locators, int timeout) {
try {
(new WebDriverWait(getDriver(), timeout))
.until(somethingIsPresent(locators));
} catch (TimeoutException timeoutEx) {
// Do what you wish here to handle the TimeoutException, or remove
// the try/catch and let the TimeoutException fly. I prefer to
// rethrow a more descriptive Exception
}
}
/**
* Condition for presence of at least one of many elements.
*
* @param locators
* An array of By locators to be sought.
* @return Boolean T if at least one element is present, F otherwise.
*/
protected ExpectedCondition<Boolean> somethingIsPresent(By[] locators) {
final By[] finalLocators = locators;
return new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
boolean found = false;
for (By locator : finalLocators) {
if (isElementPresent(locator)) {
found = true;
break;
}
}
return new Boolean(found);
}
};
}
/**
* Similar to does element exist, but also verifies that only one such
* element exists and that it is displayed and enabled.
*
* @param by
* By statement locating the element.
* @return T if one and only one element matching the locator is found, and
* if it is displayed and enabled, F otherwise.
*/
protected boolean isElementPresent(By by) {
// Temporarily set the implicit timeout to zero
driver.manage().timeouts().implicitlyWait(0, TimeUnit.MILLISECONDS);
// Check to see if there are any elements in the found list
List<WebElement> elements = driver.findElements(by);
boolean isPresent = (elements.size() == 1)
&& elements.get(0).isDisplayed() && elements.get(0).isEnabled();
// Return to the original implicit timeout value
driver.manage().timeouts()
.implicitlyWait(Properties.TIMEOUT_TEST, TimeUnit.SECONDS);
// Properties.TIMEOUT_TEST is from other personal code, replace with your
// own default timeout setting.
return isPresent;
}
我的版本还检查以确保找到的任何元素都是单一的、可见的和启用的,但如果您只想检查是否存在或者您不关心您的定位器是否找到多个匹配元素,则可以轻松删除它。通过抑制默认超时然后调用 findElements() 来检查元素的存在可能看起来很笨拙,但这显然是 Selenium API 中推荐的方法。
我也遇到了这个问题,所以我为它做了一个方法。请注意,该方法位于一个包含 webdriver 作为“self._driver”的类中。代码在 Python 中。
调用方法的一个例子是:
self.MES(3, ('name', 'name_of_element1'), ('id', 'id_of_element2'))
from selenium.common.exceptions import NoSuchElementException
import time
def MES(self, wait_time, element1, element2):
'''
A function to check a website for multiple elements at the same time
MultiElementSearch. Returns the element if found, or False if neither
are found.
It will also throw a ValueError is the element locator type is not
valid.
MES(int, (str, str), (str, str)) -> Element or bool
'''
time1 = time.time()
while time.time() < (time1 + wait_time):
try:
if element1[0] == 'id':
selection1 = self._driver.find_element_by_id(element1[1])
elif element1[0] == 'name':
selection1 = self._driver.find_element_by_name(element1[1])
elif element1[0] == 'xpath':
selection1 = self._driver.find_element_by_xpath(element1[1])
elif element1[0] == 'link_text':
selection1 = self._driver.find_element_by_link_text(element1[1])
elif element1[0] == 'partial_link_text':
selection1 = self._driver.find_element_by_partial_link_text(
element1[1])
elif element1[0] == 'tag_name':
selection1 = self._driver.find_element_by_tag_name(element1[1])
elif element1[0] == 'class_name':
selection1 = self._driver.find_element_by_class_name(
element1[1])
elif element1[0] == 'css_selector':
selection1 = self._driver.find_element_by_css_selector(
element1[1])
else:
raise ValueError(
'The first element locator type is not vaild')
return selection1
except NoSuchElementException:
pass
try:
if element2[0] == 'id':
selection2 = self._driver.find_element_by_id(element2[1])
elif element2[0] == 'name':
selection2 = self._driver.find_element_by_name(element2[1])
elif element2[0] == 'xpath':
selection2 = self._driver.find_element_by_xpath(element2[1])
elif element2[0] == 'link_text':
selection2 = self._driver.find_element_by_link_text(element2[1])
elif element2[0] == 'partial_link_text':
selection2 = self._driver.find_element_by_partial_link_text(
element2[1])
elif element2[0] == 'tag_name':
selection2 = self._driver.find_element_by_tag_name(element2[1])
elif element2[0] == 'class_name':
selection2 = self._driver.find_element_by_class_name(
element2[1])
elif element2[0] == 'css_selector':
selection2 = self._driver.find_element_by_css_selector(
element2[1])
else:
raise ValueError(
'The second element locator type is not vaild')
return selection2
except NoSuchElementException:
pass
return False
@pavel_kazlou,关于你关于 FluentWait 的问题:基本上有两种等待: 显式等待
WebDriverWait.until(condition-that-finds-the-element)
隐式等待
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
区别是
这是 WebDriverWait 使用示例(使用不同的 WebDriverWait 构造函数来指定元素轮询间隔(以毫秒为单位)。):
new WebDriverWait(webDriver(), 10, 50).until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath)));
使用与 WebDriverWait 类似的 FluentWait(实际上扩展了 FluentWait),但为您提供了更多的灵活性:特别是选择要忽略的 WebDriver 异常的能力。用法示例:
new FluentWait(webDriver())
.withTimeout(timeout, TimeUnit.SECONDS)
.pollingEvery(50, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class)
.until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath)));
总结一下我的说明:fluentWait 是一种显式等待类型,您可以显式选择要忽略的 WebDriver 异常类型,因为任何隐式等待都包括等待任何 webElement 的固定时间。从这个角度来看,恕我直言 fluentWait 方法更加稳健。
对于任何希望在 Python 中执行此操作的人
这是一个对我有用的非常简单的方法,首先我使用该implicitly_wait
方法,以便驱动程序自动等待给定的时间,直到它可以找到元素:
driver.implicitly_wait(30) #driver constantly tests for 30 seconds before complaining
现在,如果我想找到两个不同元素之一(或以不同方式找到相同元素),我只需执行以下操作:
#We see if either is present, we only need one for the if statement to be true
if driver.find_element_by_class_name('dir') or driver.find_element_by_class_name('dirSel'):
#Set wait time to 0 so we can try both fast (since we know one was found but not which)
driver.implicitly_wait(0)
try:
ele = driver.find_element_by_class_name('dir')
except:
ele = driver.find_element_by_class_name('dirSel')
driver.implicitly_wait(30) #reset the driver's wait time.
这可以很容易地转换为一个函数并缩放以搜索两个以上的元素,只是想避免这种情况,因为原始帖子在 Java 中寻求帮助。尽管如此,selenium 命令在各种语言中都是非常统一的,所以我帮助这可以帮助那些不使用 python 工作的人:)
这是一个Java 8
解决方案。
包装对象:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;
public class SelectorWebElement
{
private WebElement webElement;
private By by;
private SelectorWebElement(WebElement webElement, By by)
{
this.webElement = webElement;
this.by = by;
}
public By getBy()
{
return by;
}
public WebElement getWebElement()
{
return webElement;
}
private static ExpectedCondition<SelectorWebElement> findFirstElement(By... selectors)
{
return driver ->
{
for (By selector : selectors)
{
try
{
assert driver != null;
WebElement webElement = driver.findElement(selector);
if (webElement.isDisplayed())
{
return new SelectorWebElement(webElement, selector);
}
} catch (Exception ignored)
{
}
}
return null;
};
}
public static SelectorWebElement waitForFirstElement(WebDriver driver,
long timeout,
By... selectors)
{
Wait wait = new WebDriverWait(driver, timeout);
return (SelectorWebElement) wait.until(findFirstElement(selectors));
}
}
示例代码:
By badPasswordSelector = By.cssSelector("...");
By myAccountPage = By.cssSelector("...");
SelectorWebElement selectorWebElement = SelectorWebElement.waitForFirstElement(driver, 5, badPasswordSelector, myAccountPage);
By matchedSelector = selectorWebElement.getBy();
if (matchedSelector.equals(badPasswordSelector))
{
System.out.println("Bad password");
} else if (matchedSelector.equals(myAccountPage))
{
System.out.println("Successfully logged in");
}