硒预期条件 - 可以使用'或'?

art*_*bot 19 python selenium

我正在使用Selenium 2/WebDriver和Python API,如下所示:

from selenium.webdriver.support import expected_conditions as EC

# code that causes an ajax query to be run

WebDriverWait(driver, 10).until( EC.presence_of_element_located( \
    (By.CSS_SELECTOR, "div.some_result")));
Run Code Online (Sandbox Code Playgroud)

我想等要么返回一个结果(div.some_result) "未找到"字符串.那可能吗?的种类:

WebDriverWait(driver, 10).until( \
    EC.presence_of_element_located( \
         (By.CSS_SELECTOR, "div.some_result")) \
    or 
    EC.presence_of_element_located( \
         (By.CSS_SELECTOR, "div.no_result")) \
);
Run Code Online (Sandbox Code Playgroud)

我意识到我可以用CSS选择器(div.no_result, div.some_result)做到这一点,但有没有办法使用Selenium期望条件方法呢?

art*_*bot 16

我是这样做的:

class AnyEc:
    """ Use with WebDriverWait to combine expected_conditions
        in an OR.
    """
    def __init__(self, *args):
        self.ecs = args
    def __call__(self, driver):
        for fn in self.ecs:
            try:
                if fn(driver): return True
            except:
                pass
Run Code Online (Sandbox Code Playgroud)

然后把它称为......

from selenium.webdriver.support import expected_conditions as EC
# ...
WebDriverWait(driver, 10).until( AnyEc(
    EC.presence_of_element_located(
         (By.CSS_SELECTOR, "div.some_result")),
    EC.presence_of_element_located(
         (By.CSS_SELECTOR, "div.no_result")) ))
Run Code Online (Sandbox Code Playgroud)

显然,同样实施一个AllEc班级也是微不足道的.

铌.的try:块为奇数.我很困惑,因为有些EC返回true/false而其他EC会抛出False的异常.WebDriverWait捕获异常,因此我的AnyEc产生奇怪的结果,因为第一个抛出异常意味着AnyEc没有进行下一个测试.

  • 很棒的解决方案@artfulrobot!但是,这会降低找到的元素fn()的返回值。将其更改为“ res = fn(驱动程序);如果res:return res”就可以了。 (2认同)

Fac*_*ion 5

古老的问题

WedDriverWait在不依赖硒的示例中,考虑工作原理:

def is_even(n):
    return n % 2 == 0

x = 10

WebDriverWait(x, 5).until(is_even)
Run Code Online (Sandbox Code Playgroud)

这将等待最多5秒is_even(x)才能返回True

现在,WebDriverWait(7, 5).until(is_even)将花费5秒,并且它们引发TimeoutException

事实证明,您可以返回任何非Falsy值并捕获它:

def return_if_even(n):
    if n % 2 == 0:
        return n
    else:
        return False

x = 10
y = WebDriverWait(x, 5).until(return_if_even)
print(y) # >> 10
Run Code Online (Sandbox Code Playgroud)

现在考虑一下EC工作方法如何:

print(By.CSS_SELECTOR) # first note this is only a string
>> 'css selector'

cond = EC.presence_of_element_located( ('css selector', 'div.some_result') )
# this is only a function(*ish), and you can call it right away:

cond(driver)
# if element is in page, returns the element, raise an exception otherwise
Run Code Online (Sandbox Code Playgroud)

您可能想尝试类似的方法:

def presence_of_any_element_located(parent, *selectors):
    ecs = []
    for selector in selectors:
        ecs.append(
            EC.presence_of_element_located( ('css selector', selector) )
        )

     # Execute the 'EC' functions agains 'parent'
     ecs = [ec(parent) for ec in ecs]

     return any(ecs)
Run Code Online (Sandbox Code Playgroud)

如果在中找不到时EC.presence_of_element_located返回Falseselector将可以正常工作parent,但会引发异常,一种易于理解的解决方法是:

def element_in_parent(parent, selector):
    matches = parent.find_elements_by_css_selector(selector)
    if len(matches) == 0:
        return False
    else:
        return matches

def any_element_in_parent(parent, *selectors):
    for selector in selectors:
        matches = element_in_parent(parent, selector)
        # if there is a match, return right away
        if matches:
            return matches
    # If list was exhausted
    return False

# let's try 
any_element_in_parent(driver, 'div.some_result', 'div.no_result')
# if found in driver, will return matches, else, return False

# For convenience, let's make a version wich takes a tuple containing the arguments (either one works):
cond = lambda args: any_element_in_parent(*args)
cond( (driver, 'div.some_result', 'div.no_result') )
# exactly same result as above

# At last, wait up until 5 seconds for it 
WebDriverWait((driver, 'div.some_result', 'div.no_result'), 5).until(cond)
Run Code Online (Sandbox Code Playgroud)

我的目的是解释一下,artfulrobot已经给出了实际EC方法的一般使用的摘要,请注意

class A(object):
    def __init__(...): pass
    def __call__(...): pass
Run Code Online (Sandbox Code Playgroud)

只是定义函数的一种更灵活的方法(实际上是“类似函数”,但这与上下文无关)