【问题标题】:Selenium, Presence of one of many elements located?硒,是否存在许多元素之一?
【发布时间】:2019-01-09 12:18:12
【问题描述】:

基于How to wait until the page is loaded with Selenium for Python? 的答案,我正在尝试创建一种方法,该方法允许使用预期条件轮询多个元素是否存在。

我在包含以下内容的行收到错误'bool' object is not callable:wait.until(any(expectations))。

考虑的过程是允许多个 Xpaths 作为预期条件传递,然后使用any(),以类似于此基于 java 的答案 Trying to wait for one of two elements in the page using selenium xpath 的方式,检查是否存在任何条件。

在这种情况下使用 any() 的正确方法是什么?或者更好的是,需要做什么才能使这种方法起作用?

假设 Selenium .get('url') 在调用此方法之前已经立即执行。

def wait_with_xpath_expectation(self, search_elements, timeout=6, max_attempts=3):
    """
    Selenium wait for an element(s) designated by Xpath to become available in the DOM. Useful for javascript AJAXy
    loading pages where content may be be rendered dynamically on the client side after page load appears complete.
    search_elements may be one Xpath as a string or many as a list. This allows for any of multiple elements on a
    page or pages to be determined to have loaded based on expectations.
    :param search_elements: string or list (strings converted to lists), Xpath(s)
    :param timeout: int, seconds
    :param max_attempts: int, time to wait before giving up on page loading
    :return: Boolean, True if page has loaded, False if all attempts have failed
    """

    # Page is not loaded yet
    loaded = False

    # Initialize attempt count
    attempt = 0

    # If only one element has been passed, ensure it is encapsulated by a list
    if type(search_elements) is str:
        search_elements = [search_elements]

    # Begin the polling loop
    while attempt < max_attempts:

        try:

            while loaded is False:
                # Create a list of expected elements using list comprehension
                expectations = [EC.presence_of_element_located((By.XPATH, element)) for element in search_elements]

                # Define wait
                wait = WebDriverWait(self.browser, timeout)

                # Execute
                wait.until(any(expectations))

                # Acknowledge load
                loaded = True

                print("Success: Page loaded based on expectations")

                # Exit returning True for load
                return loaded

        except TimeoutException as e:

            # Increment attempts
            attempt += 1

            # Check again if max attempts has not been exceeded
            while attempt < max_attempts:

                # Increase timeout by 20%
                timeout *= .2

                # Try again 
                continue

            # Print an error if max attempts is exceeded
            print("Error: Max load with expectations attempts exceeded,", e)

            # Exit returning False for load
            return loaded

【问题讨论】:

标签: python selenium web-scraping expected-condition


【解决方案1】:

一旦我意识到 Xpath 具有联合的能力,下面的方法就起作用了。我会留下这个答案,以防其他人有更好的答案。

def wait_with_xpath_expectation(self, search_elements, timeout=6, max_attempts=3):
    """
    Selenium wait for an element designated by Xpath to become available in the DOM. Useful for javascript AJAXy
    loading pages where content may be be rendered dynamically on the client side after page load appears complete.
    search_elements may be one Xpath as a string or many as a list. This allows for any of multiple elements on a
    page or pages to be determined to have loaded based on expectations.
    :param search_elements: string or list (strings converted to lists), Xpath(s)
    :param timeout: int, seconds
    :param max_attempts: int, time to wait before giving up on page loading
    :return: Boolean, True if page has loaded, False if all attempts have failed
    """

    # Page is not loaded yet
    loaded = False

    # Initialize attempt count
    attempt = 0

    # If only one element has been passed, ensure it is encapsulated by a list
    if type(search_elements) is str:
        search_elements = [search_elements]

    # Begin the polling loop
    while attempt < max_attempts:

        try:

            while loaded is False:
                # Decompose the list of Xpaths to a union of nodes
                node_union = " | ".join(search_elements)

                expectation = EC.presence_of_element_located((By.XPATH, node_union))

                # Define wait
                wait = WebDriverWait(self.browser, timeout)

                # Execute
                wait.until(expectation)

                # Acknowledge load
                loaded = True

                print("Success: Page loaded based on expectations")

                # Exit returning True for load
                return loaded

        except TimeoutException as e:

            # Increment attempts
            attempt += 1

            # Check again if max attempts has not been exceeded
            while attempt < max_attempts:

                # Increase timeout by 20%
                timeout *= .2

                # Try again
                continue

            # Print an error if max attempts is exceeded
            print("Error: Max load with expectations attempts exceeded,", e)

            # Exit returning False for load
            return loaded

【讨论】:

    【解决方案2】:

    您可以有一个预期条件类来等待预期条件的组合。这是一个例子。

    class wait_for_all(object):
        def __init__(self, methods):
            self.methods = methods
    
        def __call__(self, driver):
            try:
                for method in self.methods:
                    if not method(driver):
                        return False
                return True
            except StaleElementReferenceException:
                return False
    

    然后将通过构建一个预期条件数组并在同一等待中检查所有条件来使用它。 (为清楚起见,示例行分开。)

    methods = []
    methods.append(EC.visibility_of_element_located(BY.ID, "idElem1"))
    methods.append(EC.visibility_of_element_located(BY.ID, "idElem2"))
    method = wait_for_all(methods)
    WebDriverWait(driver, 5).until(method)
    

    这将在检查两个不同元素的可见性时执行五秒钟的等待。

    我已在博客文章 here 中进一步记录了这一点。

    【讨论】:

    • 嗨蒂姆。我比我的更喜欢这个实现,谢谢!可以显式等待多个 DOM 元素而不是联合。最重要的是,它看起来非常易于使用。我要更改的一项是将 BY.ID 更改为 BY.XPATH,因为我发现某些站点没有正确使用类和 id 标记,但由于 DOM,XPATH 始终存在。接受答案,谢谢。
    • 谢谢。我只是使用 By.ID 作为示例元素定位器类型。由于每个预期条件都是单独创建的,因此您可以根据该元素组合不同的定位器类型。一个元素可能位于 By.ID,另一个元素可能使用 By.XPATH、By.NAME、By.CLASS_NAME、By.TAG_NAME、By.CSS_SELECTOR 等。
    • 接受回答
    • 这是完美的答案,但现在 selenium 预期条件函数 visibility_of_element_located 将参数作为 1 个元组。
    猜你喜欢
    • 2020-08-13
    • 2012-02-16
    • 2014-10-23
    • 1970-01-01
    • 2017-10-30
    • 2015-01-12
    • 2018-11-09
    • 1970-01-01
    • 2016-06-19
    相关资源
    最近更新 更多