如何让Selenium Web Driver等待元素可访问,而非仅仅存在?

28 投票
4 回答
61591 浏览
提问于 2025-04-17 12:26

我正在为一个网页应用写测试。有些命令会弹出对话框,里面有一些控件,这些控件虽然可见,但在几秒钟内是无法使用的。(它们是灰色的,但webdriver依然认为它们是可见的。)

我该如何让Selenium等到这些元素真正可以使用,而不仅仅是可见呢?

    try:
        print "about to look for element"
        element = WebDriverWait(driver, 10).until(lambda driver : driver.find_element_by_id("createFolderCreateBtn"))
        print "still looking?"
    finally: print 'yowp'

这是我尝试过的代码,但它在按钮还不能用的时候就“看到了”这个按钮,结果直接跳过了应该“等待”的部分。

需要注意的是,我可以在代码里加一个十秒的暂停,这样代码就能正常工作,但这样做既不优雅,又不可靠,也不高效。不过,这确实证明了问题在于“点击”命令跑得太快,超过了控件可用的时间。

4 个回答

1

我觉得下面这样的代码也应该可以用:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions

browser = webdriver.Firefox()
wait = WebDriverWait(browser, 30)
wait.until(expected_conditions.presence_of_element_located((By.XPATH, "//*[@id='createFolderCreateBrn' and not(@disabled)]")))
17

我假设事件的时间线是这样的:

  1. 页面上没有需要的元素。
  2. 需要的元素出现了,但它是禁用的:
    <input type="button" id="createFolderCreateBtn" disabled="disabled" />
  3. 需要的元素变成可用的:
    <input type="button" id="createFolderCreateBtn" />

目前你是通过ID来查找元素的,你在第二步找到了一个,但那是你不需要的。你需要做的是通过xpath来查找它:

//input[@id="createFolderCreateBtn" and not(@disabled)]

这里是它们之间的区别:

from lxml import etree


html = """
<input type="button" id="createFolderCreateBtn" disabled="disabled" />
<input type="button" id="createFolderCreateBtn" />
"""

tree = etree.fromstring(html, parser=etree.HTMLParser())

tree.xpath('//input[@id="createFolderCreateBtn"]')
# returns both elements:
# [<Element input at 102a73680>, <Element input at 102a73578>]


tree.xpath('//input[@id="createFolderCreateBtn" and not(@disabled)]')
# returns single element:
# [<Element input at 102a73578>]

总结一下,这就是你修正后的代码:

try:
    print "about to look for element"
    element_xpath = '//input[@id="createFolderCreateBtn" and not(@disabled)]'
    element = WebDriverWait(driver, 10).until(
            lambda driver : driver.find_element_by_xpath(element_xpath)
    )
    print "still looking?"
finally: 
    print 'yowp'

更新:
重新粘贴一下,使用实际的webdriver。
这是example.html页面的代码:

<input type="button" id="createFolderCreateBtn" disabled="disabled" />
<input type="button" id="createFolderCreateBtn" />

这是ipython会话:

In [1]: from selenium.webdriver import Firefox

In [2]: browser = Firefox()

In [3]: browser.get('file:///tmp/example.html')

In [4]: browser.find_elements_by_xpath('//input[@id="createFolderCreateBtn"]')
Out[4]: 
[<selenium.webdriver.remote.webelement.WebElement at 0x103f75110>,
 <selenium.webdriver.remote.webelement.WebElement at 0x103f75150>]

In [5]: browser.find_elements_by_xpath('//input[@id="createFolderCreateBtn" and not(@disabled)]')
Out[5]: 
[<selenium.webdriver.remote.webelement.WebElement at 0x103f75290>]

更新 2:

这个方法也可以用:

<input type="button" id="createFolderCreateBtn" disabled />
13
    print time.time()
    try:
        print "about to look for element"
        def find(driver):
            e = driver.find_element_by_id("createFolderCreateBtn")
            if (e.get_attribute("disabled")=='true'):
                return False
            return e
        element = WebDriverWait(driver, 10).until(find)
        print "still looking?"
    finally: print 'yowp'
    print "ok, left the loop"
    print time.time()

这是我们最后得到的结果。(感谢lukeis和RossPatterson的帮助。)请注意,我们必须先通过ID找到所有的项目,然后再筛选出“禁用”的项目。我本来希望能有一个简单的搜索方式,但没办法,只能这样做。

撰写回答