Selenium中等待页面加载

301

8
在我看来,只有Paul的回答是正确的,多数被投票高的回答都是关于等待特定元素的。 - Ajinkya
48个回答

154

你也可以使用以下代码检查页面是否已加载

IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));

 wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));

41
你可能会认为这样的功能已经内置了。在网络上等待页面加载是一件非常普遍的事情。 - PRMan
24
这真的总是有效吗?也许我从你的代码中漏掉了什么,但你正在等待DOM处于就绪状态。但请注意,如果您的代码执行得太快,先前的页面可能尚未卸载,即使您仍在旧页面上,它也会返回true。您需要做的是等待当前页面卸载,然后调用您上面的代码。检测页面卸载的一种方法是获取当前页面上的WebElement,并等待直到其变得过时。http://www.obeythetestinggoat.com/how-to-get-selenium-to-wait-for-page-load-after-a-click.html - George
8
FYI - 即使如此,以上内容并不能保证页面完全加载完成 - 只是DOM准备就绪。任何Dojo / jQuery可能仍在动态地构建页面元素,因此您可能需要等待动态元素加载完成后才能与其交互。 - George
3
请注意,此方法仅检查文档对象模型(DOM)。如果您使用Ajax或AngularJS,则该方法无效,因为一些异步调用可能无法被文档.readyState检测到。 - Homewrecker
13
这是C#代码。它在Java中无法运行,而问题是关于Java的。 - Kingamere
显示剩余6条评论

101

使用 WebDriverWait 类。

此外,这里 也可以参考。

您可以期望展示某些元素。例如在C#中:

WebDriver _driver = new WebDriver();
WebDriverWait _wait = new WebDriverWait(_driver, new TimeSpan(0, 1, 0));

_wait.Until(d => d.FindElement(By.Id("Id_Your_UIElement")));

3
TimeSpan是.Net数据结构。 - rjzii
27
如果我不知道页面上将会有哪些元素,该怎么办? - JustGoscha
4
这将等待特定元素的加载,而不是整个页面。 - Ajinkya
@xyz ... 你错了。如果你改进你的代码并使用ExpectedConditions类,你可以这样做:JavaWebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOfAllElements(list));其中list是元素的ArrayList。 - Mario Galea
2
这根本不能保证元素完全加载,也无法回答问题。怎么会有人点赞这个呢? - Boris D. Teoharov
显示剩余2条评论

43
如果你设置了驱动程序的隐式等待时间,并在希望出现在已加载页面上的元素上调用findElement方法,WebDriver将对该元素进行轮询,直到找到该元素或达到超时值。
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

来源:隐式等待


7
等待页面加载不会有帮助。 - Ajinkya
这意味着它会在10秒内尝试某些操作,然后才会引发异常。因此,无法确定它是否会产生10秒的延迟。 - skysign
1
@xyz - 为什么它不能帮助? - MasterJoe
@testerjoe2 它会等待特定元素被找到,问题是如何等待页面加载。 - Ajinkya

36

通常情况下,使用Selenium 2.0时,Web Driver应该在确定页面已加载后才将控制权返回给调用代码。如果没有加载完成,您可以调用waitforelement,它会循环调用findelement直到找到元素或超时(超时时间可以设置)。


2
补充Paul的回答。请也查看这个链接https://dev59.com/bG025IYBdhLWcg3wr4F6#5865445。 - 9ikhan
32
很遗憾,Selenium 2 在某些情况下不会等待页面加载完成。例如,WebElement:click()方法不会等待,这在相关的Javadoc中已经明确说明了。然而,它们并没有告诉我如何检查是否已加载了新页面。如果click()通过事件导致加载了一个新页面,或者是通过发送本机事件完成的(这在Windows上的Firefox、IE上很常见),那么该方法将不会等待页面加载完成,调用者应该验证页面是否已加载完成。 - Sebi
doc中可以看到,该方法会一直阻塞直到页面加载完成。 - Ajinkya
2
@Karna:理论上来说,它应该总是这样,实践中大部分时间也确实如此。但使用Selenium时,有时会出现它认为页面已经加载完成,但实际上并没有。最近我很少使用Selenium,所以这可能仍然存在或者已经解决了。 - Paul Hadfield
仍然是相同的。添加注释以支持您的答案。请查看我在问题上的评论。 - Ajinkya
3
我同意:特别是Internet Explorer驱动程序存在缺陷,并且在某些情况下即使页面仍在加载中也会立即返回控件。在我的情况下,我使用JavascriptExecutor添加了一个等待,等待文档的readyState变为"complete"。因为从Selenium到浏览器的往返,竞争条件得以缓解,所以这对我来说“总是”有效。在“click()”之后,当我期望页面加载时,我明确等待(使用WebDriverWait)readyState。 - dmansfield

25

Ruby 实现:

wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
    @driver.execute_script("return document.readyState;") == "complete" 
}

20
Python等效代码:WebDriverWait(driver, 10).until(lambda d: d.execute_script('return document.readyState') == 'complete')该代码等待浏览器页面加载完成。在Python中,使用WebDriverWait函数和execute_script方法来执行JavaScript脚本并等待页面状态为“complete”。这个lambda函数将驱动程序作为参数输入,并返回True或False以指示页面是否已完全加载。如果在10秒内页面未能加载,则会抛出一个超时异常。 - blaze
5
Python使用上述代码,但不要忘记这一行...|从selenium.webdriver.support.ui导入WebDriverWait。 - t3dodson
当页面没有完全加载时,我点击一个元素时遇到了问题。在Python中,我尝试使用time.sleep(30)。它有效了。但是它总是等待最多30秒。然后我尝试了以下代码,现在更加高效和快速。WebDriverWait(driver, 10).until(lambda d: driver.find_element_by_xpath("//div[. = 'Administration']").click())。 - Riaz Ladhani

21

所有这些解决方案对于特定情况都可以,但它们都会有至少一个可能的问题:

  1. 它们不够通用--它们要求您提前知道某些特定条件将成立(例如将显示某个元素)

  2. 它们容易出现竞争条件,您可能会在旧页面和新页面上同时使用同一个元素。

以下是我尝试提供的一个通用解决方案(使用Python实现):

首先,是一个通用的“等待”函数(如果你喜欢,可以使用WebDriverWait,但我觉得它们很丑):

def wait_for(condition_function):
    start_time = time.time()
    while time.time() < start_time + 3:
        if condition_function():
            return True
        else:
            time.sleep(0.1)
    raise Exception('Timeout waiting for {}'.format(condition_function.__name__))

接下来,该解决方案依赖于selenium记录页面上所有元素的(内部)id编号,包括顶级<html>元素。当页面刷新或加载时,它会得到一个具有新ID的html元素。

因此,假设您想单击文本为"my link"的链接:

old_page = browser.find_element_by_tag_name('html')

browser.find_element_by_link_text('my link').click()

def page_has_loaded():
    new_page = browser.find_element_by_tag_name('html')
    return new_page.id != old_page.id

wait_for(page_has_loaded)

如果想让Python代码更加Pythonic、可重复使用和通用化,可以使用上下文管理器(context manager):

from contextlib import contextmanager

@contextmanager
def wait_for_page_load(browser):
    old_page = browser.find_element_by_tag_name('html')

    yield

    def page_has_loaded():
        new_page = browser.find_element_by_tag_name('html')
        return new_page.id != old_page.id

    wait_for(page_has_loaded)

随后您可以将其用于几乎任何Selenium交互:

with wait_for_page_load(browser):
    browser.find_element_by_link_text('my link').click()

我认为那是防弹的!你觉得呢?

这篇博客文章里有更多相关信息。


我在搜索您的解决方案之前阅读了您的网页,然后更具体地搜索了实现您解决方案的Java代码。到目前为止还没有找到... - andrew lorien

21

您可以删除System.out行。它是为了调试目的而添加的。

WebDriver driver_;

public void waitForPageLoad() {

    Wait<WebDriver> wait = new WebDriverWait(driver_, 30);
    wait.until(new Function<WebDriver, Boolean>() {
        public Boolean apply(WebDriver driver) {
            System.out.println("Current Window State       : "
                + String.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState")));
            return String
                .valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState"))
                .equals("complete");
        }
    });
}

感谢这些技巧。我已将其添加到我的SeleniumHelper中;请参见javabox - boly38
超时行为是什么时间? - undefined

17

这里是一份Java 8版本的目前最赞回答:

WebDriverWait wait = new WebDriverWait(myDriver, Duration.ofSeconds(15));
wait.until(webDriver -> "complete".equals(((JavascriptExecutor) webDriver)
    .executeScript("return document.readyState")));
    

其中 myDriver是一个WebDriver对象(之前声明过)。

注意:请注意,此方法(document.readyState)仅检查DOM。


2
WebDriverWait(drive, long) is deprecated so use WebDriverWait(drive, duration) ex:- import java.time.Duration; WebDriverWait(driver, Duration.ofSeconds(5)); - Isuru Dilshan

12

Imran的答案被转化为Java 7版:

    WebDriverWait wait = new WebDriverWait(driver, 30);

    wait.until(new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver wdriver) {
            return ((JavascriptExecutor) driver).executeScript(
                "return document.readyState"
            ).equals("complete");
        }
    });

12

你还可以使用类:ExpectedConditions来显式等待网页上的元素出现,然后再执行进一步的操作。

你可以使用ExpectedConditions类来确定一个元素是否可见:

WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));

请查看ExpectedConditions类的Javadoc以获取您可以检查的所有条件列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接