Python Selenium等待加载多个元素

Python Selenium等待加载多个元素,python,testing,selenium,selenium-webdriver,automated-tests,Python,Testing,Selenium,Selenium Webdriver,Automated Tests,我有一个列表,它是由AJAX动态加载的。 首先,在加载时,其代码如下所示: <ul><li class="last"><a class="loading" href="#"><ins>&nbsp;</ins>Загрузка...</a></li></ul> ... 2) 基于查找元素的自定义函数 def wait_for_several_elements(driver, selector,

我有一个列表,它是由AJAX动态加载的。 首先,在加载时,其代码如下所示:

<ul><li class="last"><a class="loading" href="#"><ins>&nbsp;</ins>Загрузка...</a></li></ul>
... 2) 基于查找元素的自定义函数

def wait_for_several_elements(driver, selector, min_amount, limit=60):
    """
    This function provides awaiting of <min_amount> of elements found by <selector> with
    time limit = <limit>
    """
    step = 1   # in seconds; sleep for 500ms
    current_wait = 0
    while current_wait < limit:
        try:
            print "Waiting... " + str(current_wait)
            query = driver.find_elements_by_css_selector(selector)
            if len(query) > min_amount:
                print "Found!"
                return True
            else:
                time.sleep(step)
                current_wait += step
        except:
            time.sleep(step)
            current_wait += step

    return False
请告诉我确保为指定元素加载多个继承元素的正确方法

另外,所有这些检查和搜索都应该与当前元素相关。

(1)您没有提到它带来的错误

(2) 自从你提到

…因为驱动程序(传递给此函数的当前元素)

我假设这实际上是一个WebElement。在本例中,不将对象本身传递给方法,只需传递找到该WebElement的选择器(在本例中为
ul
)。如果“驱动程序在DOM中丢失”,则可能是在当前\u wait循环期间在
中重新创建它可以缓解问题

(3) 是的,
time.sleep()
只会让你走那么远


(4) 由于动态加载的
li
元素包含
class=closed
,而不是
(By.XPATH,'.//ul')
,因此您可以尝试
(By.CSS_SELECTOR,'ul>li.closed')
(有关CSS选择器的更多详细信息)

记住E.先生和Arran先生的评论,我在CSS选择器上完全遍历了我的列表。棘手的部分是关于我自己的列表结构和标记(更改类等),以及关于动态创建所需的选择器并在遍历期间将其保存在内存中

我通过搜索未处于加载状态的任何内容来处理等待几个元素的问题。您可以使用“:nth child”选择器,如下所示:

#in for loop with enumerate for i    
selector.append(' > li:nth-child(%i)' % (i + 1))  # identify child <li> by its order pos
#在带有enumerate for i的for循环中
selector.append('>li:n个子(%i)'(i+1))#按其顺序pos标识子
  • 这是我的硬注释代码解决方案,例如:

    def parse_crippled_shifted_list(driver, frame, selector, level=1, parent_id=0, path=None):
        """
        Traversal of html list of special structure (you can't know if element has sub list unless you enter it).
        Supports start from remembered list element.
    
        Nested lists have classes "closed" and "last closed" when closed and "open" and "last open" when opened (on <li>).
        Elements themselves have classes "leaf" and "last leaf" in both cases.
        Nested lists situate in <li> element as <ul> list. Each <ul> appears after clicking <a> in each <li>.
        If you click <a> of leaf, page in another frame will load.
    
        driver - WebDriver; frame - frame of the list; selector - selector to current list (<ul>);
        level - level of depth, just for console output formatting, parent_id - id of parent category (in DB),
        path - remained path in categories (ORM objects) to target category to start with.
        """
    
        # Add current level list elements
        # This method selects all but loading. Just what is needed to exclude.
        selector.append(' > li > a:not([class=loading])')
    
        # Wait for child list to load
        try:
            query = WebDriverWait(driver, WAIT_LONG_TIME).until(
                EC.presence_of_all_elements_located((By.CSS_SELECTOR, ''.join(selector))))
    
        except TimeoutException:
            print "%s timed out" % ''.join(selector)
    
        else:
            # List is loaded
            del selector[-1]  # selector correction: delete last part aimed to get loaded content
            selector.append(' > li')
    
            children = driver.find_elements_by_css_selector(''.join(selector))  # fetch list elements
    
            # Walk the whole list
            for i, child in enumerate(children):
    
                del selector[-1]  # delete non-unique li tag selector
                if selector[-1] != ' > ul' and selector[-1] != 'ul.ltr':
                    del selector[-1]
    
                selector.append(' > li:nth-child(%i)' % (i + 1))  # identify child <li> by its order pos
                selector.append(' > a')  # add 'li > a' reference to click
    
                child_link = driver.find_element_by_css_selector(''.join(selector))
    
                # If we parse freely further (no need to start from remembered position)
                if not path:
                    # Open child
                    try:
                        double_click(driver, child_link)
                    except InvalidElementStateException:
                            print "\n\nERROR\n", InvalidElementStateException.message(), '\n\n'
                    else:
                        # Determine its type
                        del selector[-1]  # delete changed and already useless link reference
                        # If <li> is category, it would have <ul> as child now and class="open"
                        # Check by class is priority, because <li> exists for sure.
                        current_li = driver.find_element_by_css_selector(''.join(selector))
    
                        # Category case - BRANCH
                        if current_li.get_attribute('class') == 'open' or current_li.get_attribute('class') == 'last open':
                            new_parent_id = process_category_case(child_link, parent_id, level)  # add category to DB
                            selector.append(' > ul')  # forward to nested list
                            # Wait for nested list to load
                            try:
                                query = WebDriverWait(driver, WAIT_LONG_TIME).until(
                                    EC.presence_of_all_elements_located((By.CSS_SELECTOR, ''.join(selector))))
    
                            except TimeoutException:
                                print "\t" * level,  "%s timed out (%i secs). Failed to load nested list." %\
                                                     ''.join(selector), WAIT_LONG_TIME
                            # Parse nested list
                            else:
                                parse_crippled_shifted_list(driver, frame, selector, level + 1, new_parent_id)
    
                        # Page case - LEAF
                        elif current_li.get_attribute('class') == 'leaf' or current_li.get_attribute('class') == 'last leaf':
                            process_page_case(driver, child_link, level)
                        else:
                            raise Exception('Damn! Alien class: %s' % current_li.get_attribute('class'))
    
                # If it's required to continue from specified category
                else:
                    # Check if it's required category
                    if child_link.text == path[0].name:
                        # Open required category
                        try:
                            double_click(driver, child_link)
    
                        except InvalidElementStateException:
                                print "\n\nERROR\n", InvalidElementStateException.msg, '\n\n'
    
                        else:
                            # This element of list must be always category (have nested list)
                            del selector[-1]  # delete changed and already useless link reference
                            # If <li> is category, it would have <ul> as child now and class="open"
                            # Check by class is priority, because <li> exists for sure.
                            current_li = driver.find_element_by_css_selector(''.join(selector))
    
                            # Category case - BRANCH
                            if current_li.get_attribute('class') == 'open' or current_li.get_attribute('class') == 'last open':
                                selector.append(' > ul')  # forward to nested list
                                # Wait for nested list to load
                                try:
                                    query = WebDriverWait(driver, WAIT_LONG_TIME).until(
                                        EC.presence_of_all_elements_located((By.CSS_SELECTOR, ''.join(selector))))
    
                                except TimeoutException:
                                    print "\t" * level, "%s timed out (%i secs). Failed to load nested list." %\
                                                         ''.join(selector), WAIT_LONG_TIME
                                # Process this nested list
                                else:
                                    last = path.pop(0)
                                    if len(path) > 0:  # If more to parse
                                        print "\t" * level, "Going deeper to: %s" % ''.join(selector)
                                        parse_crippled_shifted_list(driver, frame, selector, level + 1,
                                                                    parent_id=last.id, path=path)
                                    else:  # Current is required
                                        print "\t" * level,  "Returning target category: ", ''.join(selector)
                                        path = None
                                        parse_crippled_shifted_list(driver, frame, selector, level + 1, last.id, path=None)
    
                            # Page case - LEAF
                            elif current_li.get_attribute('class') == 'leaf':
                                pass
                    else:
                        print "dummy"
    
            del selector[-2:]
    
    def parse_跛行_移位_列表(驱动程序、帧、选择器、级别=1、父级_id=0、路径=None):
    """
    遍历特殊结构的html列表(除非输入子列表,否则无法知道元素是否有子列表)。
    支持从记住的列表元素开始。
    嵌套列表在关闭时具有类“closed”和类“last closed”,在打开时具有类“open”和类“last open”(在
  • 上)。 在这两种情况下,元素本身都有类“leaf”和“last leaf”。 嵌套列表位于
  • 元素中,作为
      列表。每个
        在单击每个
      • 后显示。 若你们点击叶子的顶部,另一个框架中的页面将被加载。 driver-WebDriver;frame-list的frame;selector-selector到当前列表(
          ); level-深度级别,仅用于控制台输出格式,parent_id-parent category的id(以DB为单位), 路径-从类别(ORM对象)到目标类别的剩余路径。 """ #添加当前级别的列表元素 #此方法选择除加载之外的所有选项。只是需要排除什么。 selector.append('>li>a:not([class=load])) #等待加载子列表 尝试: query=WebDriverWait(驱动程序,等待很长时间)。直到( EC.所有元素的存在位置((By.CSS_选择器,''.join(选择器))) 除TimeoutException外: 打印“%s”超时“%''。加入(选择器) 其他: #列表已加载 删除选择器[-1]#选择器更正:删除旨在获取加载内容的最后一部分 selector.append('>li') children=driver.find_elements_by_css_selector(“”.join(selector))#fetch list elements #浏览整个列表 对于i,枚举中的child(children): del选择器[-1]#删除非唯一li标记选择器 如果选择器[-1]!='>ul'和选择器[-1]!='ul.ltr': del选择器[-1] selector.append('>li:n个子(%i)'(i+1))#按其顺序pos标识子
        • selector.append('>a')#添加'li>a'引用以单击 子链接=驱动程序。通过css选择器(“”.join(选择器))查找元素 #如果我们进一步自由解析(无需从记住的位置开始) 如果不是路径: #开放儿童 尝试: 双击(驱动程序、子链接) 除InvalidElementStateException外: 打印“\n\n错误\n”,InvalidElementStateException.message(),”\n\n“ 其他: #确定其类型 删除选择器[-1]#删除已更改且已无用的链接引用 #如果
        • 是category,则它现在将
            作为child并class=“open” #按类检查优先,因为肯定存在
          • 。 当前\u li=驱动程序。通过\u css\u选择器(“”.join(选择器))查找\u元素 #类别个案-分行 如果当前\u li.get\u属性('class')=='open'或当前\u li.get\u属性('class')=='last open': 新建\父级\ id=流程\类别\案例(子级\链接,父级\ id,级别)\将类别添加到数据库 selector.append('>ul')#转发到嵌套列表 #等待加载嵌套列表 尝试: query=WebDriverWait(驱动程序,等待很长时间)。直到( EC.所有元素的存在位置((By.CSS_选择器,''.join(选择器))) 除TimeoutException外: 打印“\t”*级别,“%s”超时(%i秒)。加载嵌套列表失败。“%\ ''加入(选择器),等待很长时间 #解析嵌套列表 其他: 解析受损移位列表(驱动程序、帧、选择器、级别+1、新父id) #页面大小写-页 如果当前获取属性('class')='leaf'或当前获取属性('class')='last leaf':
            def wait_for_several_elements(driver, selector, min_amount, limit=60):
                """
                This function provides awaiting of <min_amount> of elements found by <selector> with
                time limit = <limit>
                """
                step = 1   # in seconds; sleep for 500ms
                current_wait = 0
                while current_wait < limit:
                    try:
                        print "Waiting... " + str(current_wait)
                        query = driver.find_elements_by_css_selector(selector)
                        if len(query) > min_amount:
                            print "Found!"
                            return True
                        else:
                            time.sleep(step)
                            current_wait += step
                    except:
                        time.sleep(step)
                        current_wait += step
            
                return False
            
            try:
                print "Going to nested list..."
                #time.sleep(WAIT_TIME)
                query = WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.XPATH, './/ul')))
                nested_list = child.find_element_by_css_selector('ul')
            
            #in for loop with enumerate for i    
            selector.append(' > li:nth-child(%i)' % (i + 1))  # identify child <li> by its order pos
            
            def parse_crippled_shifted_list(driver, frame, selector, level=1, parent_id=0, path=None):
                """
                Traversal of html list of special structure (you can't know if element has sub list unless you enter it).
                Supports start from remembered list element.
            
                Nested lists have classes "closed" and "last closed" when closed and "open" and "last open" when opened (on <li>).
                Elements themselves have classes "leaf" and "last leaf" in both cases.
                Nested lists situate in <li> element as <ul> list. Each <ul> appears after clicking <a> in each <li>.
                If you click <a> of leaf, page in another frame will load.
            
                driver - WebDriver; frame - frame of the list; selector - selector to current list (<ul>);
                level - level of depth, just for console output formatting, parent_id - id of parent category (in DB),
                path - remained path in categories (ORM objects) to target category to start with.
                """
            
                # Add current level list elements
                # This method selects all but loading. Just what is needed to exclude.
                selector.append(' > li > a:not([class=loading])')
            
                # Wait for child list to load
                try:
                    query = WebDriverWait(driver, WAIT_LONG_TIME).until(
                        EC.presence_of_all_elements_located((By.CSS_SELECTOR, ''.join(selector))))
            
                except TimeoutException:
                    print "%s timed out" % ''.join(selector)
            
                else:
                    # List is loaded
                    del selector[-1]  # selector correction: delete last part aimed to get loaded content
                    selector.append(' > li')
            
                    children = driver.find_elements_by_css_selector(''.join(selector))  # fetch list elements
            
                    # Walk the whole list
                    for i, child in enumerate(children):
            
                        del selector[-1]  # delete non-unique li tag selector
                        if selector[-1] != ' > ul' and selector[-1] != 'ul.ltr':
                            del selector[-1]
            
                        selector.append(' > li:nth-child(%i)' % (i + 1))  # identify child <li> by its order pos
                        selector.append(' > a')  # add 'li > a' reference to click
            
                        child_link = driver.find_element_by_css_selector(''.join(selector))
            
                        # If we parse freely further (no need to start from remembered position)
                        if not path:
                            # Open child
                            try:
                                double_click(driver, child_link)
                            except InvalidElementStateException:
                                    print "\n\nERROR\n", InvalidElementStateException.message(), '\n\n'
                            else:
                                # Determine its type
                                del selector[-1]  # delete changed and already useless link reference
                                # If <li> is category, it would have <ul> as child now and class="open"
                                # Check by class is priority, because <li> exists for sure.
                                current_li = driver.find_element_by_css_selector(''.join(selector))
            
                                # Category case - BRANCH
                                if current_li.get_attribute('class') == 'open' or current_li.get_attribute('class') == 'last open':
                                    new_parent_id = process_category_case(child_link, parent_id, level)  # add category to DB
                                    selector.append(' > ul')  # forward to nested list
                                    # Wait for nested list to load
                                    try:
                                        query = WebDriverWait(driver, WAIT_LONG_TIME).until(
                                            EC.presence_of_all_elements_located((By.CSS_SELECTOR, ''.join(selector))))
            
                                    except TimeoutException:
                                        print "\t" * level,  "%s timed out (%i secs). Failed to load nested list." %\
                                                             ''.join(selector), WAIT_LONG_TIME
                                    # Parse nested list
                                    else:
                                        parse_crippled_shifted_list(driver, frame, selector, level + 1, new_parent_id)
            
                                # Page case - LEAF
                                elif current_li.get_attribute('class') == 'leaf' or current_li.get_attribute('class') == 'last leaf':
                                    process_page_case(driver, child_link, level)
                                else:
                                    raise Exception('Damn! Alien class: %s' % current_li.get_attribute('class'))
            
                        # If it's required to continue from specified category
                        else:
                            # Check if it's required category
                            if child_link.text == path[0].name:
                                # Open required category
                                try:
                                    double_click(driver, child_link)
            
                                except InvalidElementStateException:
                                        print "\n\nERROR\n", InvalidElementStateException.msg, '\n\n'
            
                                else:
                                    # This element of list must be always category (have nested list)
                                    del selector[-1]  # delete changed and already useless link reference
                                    # If <li> is category, it would have <ul> as child now and class="open"
                                    # Check by class is priority, because <li> exists for sure.
                                    current_li = driver.find_element_by_css_selector(''.join(selector))
            
                                    # Category case - BRANCH
                                    if current_li.get_attribute('class') == 'open' or current_li.get_attribute('class') == 'last open':
                                        selector.append(' > ul')  # forward to nested list
                                        # Wait for nested list to load
                                        try:
                                            query = WebDriverWait(driver, WAIT_LONG_TIME).until(
                                                EC.presence_of_all_elements_located((By.CSS_SELECTOR, ''.join(selector))))
            
                                        except TimeoutException:
                                            print "\t" * level, "%s timed out (%i secs). Failed to load nested list." %\
                                                                 ''.join(selector), WAIT_LONG_TIME
                                        # Process this nested list
                                        else:
                                            last = path.pop(0)
                                            if len(path) > 0:  # If more to parse
                                                print "\t" * level, "Going deeper to: %s" % ''.join(selector)
                                                parse_crippled_shifted_list(driver, frame, selector, level + 1,
                                                                            parent_id=last.id, path=path)
                                            else:  # Current is required
                                                print "\t" * level,  "Returning target category: ", ''.join(selector)
                                                path = None
                                                parse_crippled_shifted_list(driver, frame, selector, level + 1, last.id, path=None)
            
                                    # Page case - LEAF
                                    elif current_li.get_attribute('class') == 'leaf':
                                        pass
                            else:
                                print "dummy"
            
                    del selector[-2:]
            
            from selenium import webdriver
            from selenium.webdriver.support.ui import WebDriverWait
            from selenium.webdriver.support import expected_conditions as EC
            from selenium.webdriver.common.by import By
            
            # create a new Chrome session
            driver = webdriver.Chrome()
            
            # navigate to your web app.
            driver.get("http://my.local.web")
            
            # get the search button
            seemore_button = driver.find_element_by_id("seemoreID")
            
            # Count the cant of post
            seemore_button.click()
            
            # Wait for 30 sec, until AJAX search load the content
            WebDriverWait(driver,30).until(EC.visibility_of_all_elements_located(By.CLASS_NAME, "post"))) 
            
            # Get the list of post
            listpost = driver.find_elements_by_class_name("post")
            
            elements = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, "ul.ltr li[id^='t_b_'] > a[id^='t_a_'][href]")))
            
            elements = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.XPATH, "//ul[@class='ltr']//li[starts-with(@id, 't_b_')]/a[starts-with(@id, 't_a_') and starts-with(., 'Category')]")))
            
            from selenium.webdriver.support.ui import WebDriverWait
            from selenium.webdriver.common.by import By
            from selenium.webdriver.support import expected_conditions as EC
            
            myLength = 9
            WebDriverWait(driver, 20).until(lambda driver: len(driver.find_elements_by_xpath("//ul[@class='ltr']//li[starts-with(@id, 't_b_')]/a[starts-with(@id, 't_a_') and starts-with(., 'Category')]")) > int(myLength))
            
            myLength = 10
            WebDriverWait(driver, 20).until(lambda driver: len(driver.find_elements_by_xpath("//ul[@class='ltr']//li[starts-with(@id, 't_b_')]/a[starts-with(@id, 't_a_') and starts-with(., 'Category')]")) == int(myLength))
            
            from typing import Callable
            from selenium.webdriver.support import expected_conditions as EC
            from selenium.webdriver.common.by import By
            from selenium.common.exceptions import StaleElementReferenceException
            
            class AllEc(object):
                def __init__(self, *args: Callable, description: str = None):
                    self.ecs = args
                    self.description = description
            
                def __call__(self, driver):
                    try:
                        for fn in self.ecs:
                            if not fn(driver):
                                return False
                        return True
                    except StaleElementReferenceException:
                        return False
            
            # usage example:
            wait = WebDriverWait(driver, timeout)
            ec1 = EC.invisibility_of_element_located(locator1)
            ec2 = EC.invisibility_of_element_located(locator2)
            ec3 = EC.invisibility_of_element_located(locator3)
            
            all_ec = AllEc(ec1, ec2, ec3, description="Required elements to show page has loaded.") 
            found_elements = wait.until(all_ec, "Could not find all expected elements")
            
            class AnyEc(object):
                """
                Use with WebDriverWait to combine expected_conditions in an OR.
            
                Example usage:
            
                    >>> wait = WebDriverWait(driver, 30)
                    >>> either = AnyEc(expectedcondition1, expectedcondition2, expectedcondition3, etc...)
                    >>> found = wait.until(either, "Cannot find any of the expected conditions")
                """
            
                def __init__(self, *args: Callable, description: str = None):
                    self.ecs = args
                    self.description = description
            
                def __iter__(self):
                    return self.ecs.__iter__()
            
                def __call__(self, driver):
                    for fn in self.ecs:
                        try:
                            rt = fn(driver)
                            if rt:
                                return rt
                        except TypeError as exc:
                            raise exc
                        except Exception as exc:
                            # print(exc)
                            pass
            
                def __repr__(self):
                    return " ".join(f"{e!r}," for e in self.ecs)
            
                def __str__(self):
                    return f"{self.description!s}"
            
            either = AnyEc(ec1, ec2, ec3)
            found_element = wait.until(either, "Could not find any of the expected elements")
            
            def is_ajax_complete(driver)
                rt = driver.execute_script("return jQuery.active", *args)
                return rt == 0
            
            wait.until(lambda driver: is_ajax_complete(driver), "Ajax did not finish")