splinter:更快的查找元素的方法?

2 投票
1 回答
3332 浏览
提问于 2025-04-18 09:18

我正在使用python splinter库来测试一个网页应用,但我遇到了一个问题,就是在检查某个元素是否存在时,我需要手动找到每一个元素来进行操作。

问题是,当输入列表的项目超过4个时,如果遇到某个元素不存在的情况,整个过程会花费超过12秒才能完成。

我也尝试过设置等待时间为1秒,但如果输入列表超过10个,如果页面上根本没有这个元素,整个过程会花费10倍的时间。

for i in inputs:
    if browser.element_exists():
        elm = browser.find_element():
        elm.text()

我需要找到一种方法来加快这个检查元素的速度,让它们可以同时进行,而不是一个一个地检查。我唯一能想到的就是执行JavaScript,但我不太喜欢这样(我希望能全部用Python来完成)。

def get_columns(current_depth,step,element):
    columns = []
    for xpath in xpaths:
        what = parse_xpath(row[2])
        if browser.is_element_present_by_xpath(xpath,wait_time=1):
            element = browser.find_by_xpath(xpath)
            columns.append(element.text)
        else:
            columns.append('none')
    return columns

1 个回答

2

is_element_present_by_xpath 这段代码是

def is_element_present_by_xpath(self, xpath, wait_time=None):
    return self.is_element_present(self.find_by_xpath, xpath, wait_time)

使用

def is_element_present(self, finder, selector, wait_time=None):
     wait_time = wait_time or self.wait_time
     end_time = time.time() + wait_time
     while time.time() < end_time:
         if finder(selector):
             return True
     return False

def find_by_xpath(self, xpath, original_find=None, original_query=None):
    original_find = original_find or "xpath"
    original_query = original_query or xpath
    return self.find_by(self.driver.find_elements_by_xpath, 
                        xpath, 
                        original_find=original_find, 
                        original_query=original_query)

你的代码基本上是用同一个函数调用了两次。第一次你在列表中查找元素是否存在,使用了 find_by_xpath,然后第二次再查找这个元素。

find_by_xpath 会返回一个 元素列表

这个元素列表有一个方法 is_empty(),如果列表是空的,也就是没有找到匹配的元素,它会返回 True。

那么关于这个(我没有测试过,但这是我随便想的)

def get_columns(current_depth,step,element):
    columns = []
    for xpath in xpaths:
        what = parse_xpath(row[2])
        element_list = browser.find_by_xpath(xpath)
        # You might want to check that your element_list has only 1 element.
        if element_list.is_empty():
            columns.append('none')
        else:
            columns.append(element_list[0].text)
    return columns

这样可以避免重复遍历列表两次。

更新 2014-06-20:

深入了解一下。Splinter 是在调用 selenium 的 webdriver… self.find_by(self.driver.find_elements_by_xpath, … 这又是一个更深层次的调用。我不太明白他们为什么不直接调用 selenium。

为了测试,你可以尝试直接使用 selenium 来处理这部分代码,看看性能上有没有明显的差别。这样可以判断性能问题是出在 splinter 还是 selenium。

在 selenium/webdriver/remote/webdriver.py 中

```def find_elements_by_xpath(self, xpath): """ 通过 xpath 查找多个元素。

:Args:
 - xpath - The xpath locator of the elements to be found.

:Usage:
    driver.find_elements_by_xpath("//div[contains(@class, 'foo')]")
"""
return self.find_elements(by=By.XPATH, value=xpath)

```

这个方法使用了:

``` def find_elements(self, by=By.ID, value=None): """ 被 find_elements_by_* 方法使用的“私有”方法。

    :Usage:
        Use the corresponding find_elements_by_* instead of this.

    :rtype: list of WebElement
    """
    if not By.is_valid(by) or not isinstance(value, str):
        raise InvalidSelectorException("Invalid locator values passed in")

    return self.execute(Command.FIND_ELEMENTS,
                         {'using': by, 'value': value})['value']

```

最后这个方法是通过 JsonWireProtocol 直接调用 API。这里的性能很大程度上取决于你系统上 Selenium 的实现和/或你使用的浏览器。

更新 2 2014-06-20:

另外要注意的是,特别是对于 XPath,性能真的取决于使用的驱动程序。Selenium 的 关于这种特定搜索的文档

从高层次来看,WebDriver 尽可能使用浏览器的原生 XPath 功能。在那些没有原生 XPath 支持的浏览器上,我们提供了自己的实现。这可能会导致一些意想不到的行为,除非你了解不同 XPath 引擎之间的差异。

撰写回答