143

谁能让我如何让硒等到页面完全加载?我想要一些通用的东西,我知道我可以配置 WebDriverWait 并调用类似“find”的东西来让它等待,但我没有走那么远。我只需要测试页面是否成功加载并转到下一页进行测试。

我在 .net 中找到了一些东西,但无法使其在 java 中工作......

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"));

有什么想法吗?

4

27 回答 27

98

您建议的解决方案只等待DOMreadyState发出信号completedriver.get()但是默认情况下,Selenium 会尝试通过andelement.click()方法在页面加载时等待那些(以及更多) 。他们已经被阻塞了,他们等待页面完全加载并且应该可以正常工作。

显然,问题是通过 AJAX 请求和运行脚本进行的重定向 - Selenium 无法捕获这些,它不会等待它们完成。此外,您不能通过可靠地捕获它们readyState- 它会等待一段时间,这可能很有用,但它会complete在下载所有 AJAX 内容之前很久就发出信号。

没有通用的解决方案可以在任何地方对每个人都有效,这就是为什么它很难并且每个人都使用一些不同的东西。

一般规则是依靠 WebDriver 来完成他的工作,然后使用隐式等待,然后对要在页面上声明的元素使用显式等待,但是还有更多技术可以完成。您应该在测试页面上选择最适合您的情况的一个(或其中几个的组合)。

有关更多信息,请参阅我的两个答案:

于 2013-02-28T13:08:52.827 回答
84

试试这个代码:

  driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);

上面的代码将等待页面加载长达 10 秒。如果页面加载时间超过了会抛出TimeoutException. 您捕获异常并满足您的需求。我不确定它是否在抛出异常后退出页面加载。我还没有尝试过这段代码。只想试一试。

这是一个隐含的等待。如果您设置一次,它将具有范围,直到 Web 驱动程序实例销毁。

有关更多信息,请参阅文档。WebDriver.Timeouts

于 2013-02-28T04:14:06.767 回答
69

这是您提供的示例的有效 Java 版本:

void waitForLoad(WebDriver driver) {
    new WebDriverWait(driver, 30).until((ExpectedCondition<Boolean>) wd ->
            ((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
}

c# 示例:

public static void WaitForLoad(IWebDriver driver, int timeoutSec = 15)
{
    IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
    WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, timeoutSec));
    wait.Until(wd => js.ExecuteScript("return document.readyState").ToString() == "complete");
}

PHP 示例:

final public function waitUntilDomReadyState(RemoteWebDriver $webDriver): void
{
    $webDriver->wait()->until(function () {
        return $webDriver->executeScript('return document.readyState') === 'complete';
    });
}
于 2013-02-27T23:25:39.403 回答
12

这是我在 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-number 的事实<html>。当页面刷新或加载时,它会获得一个具有新 ID 的新 html 元素。

因此,假设您要单击带有文本“我的链接”的链接,例如:

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)

要获得更多 Pythonic、可重用、通用的帮助器,您可以创建一个上下文管理器:

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()

我认为这是防弹的!你怎么看?

更多信息请参见此处的博客文章

于 2014-09-03T18:28:20.700 回答
7

我有一个类似的问题。我需要等到我的文档准备好,还要等到所有 Ajax 调用都完成。事实证明,第二种情况很难检测到。最后,我检查了活动的 Ajax 调用,它起作用了。

Javascript:

return (document.readyState == 'complete' && jQuery.active == 0)

完整的 C# 方法:

private void WaitUntilDocumentIsReady(TimeSpan timeout)
{
    var javaScriptExecutor = WebDriver as IJavaScriptExecutor;
    var wait = new WebDriverWait(WebDriver, timeout);            

    // Check if document is ready
    Func<IWebDriver, bool> readyCondition = webDriver => javaScriptExecutor
        .ExecuteScript("return (document.readyState == 'complete' && jQuery.active == 0)");
    wait.Until(readyCondition);
}
于 2015-11-09T14:53:11.410 回答
7
WebDriverWait wait = new WebDriverWait(dr, 30);
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState==\"complete\";"));
于 2016-06-09T06:19:18.417 回答
4

对于 C# NUnit,你需要将 WebDriver 转换为 JSExecuter,然后执行脚本来检查 document.ready 状态是否完整。检查以下代码以供参考:

 public static void WaitForLoad(IWebDriver driver)
    {
        IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
        int timeoutSec = 15;
        WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, timeoutSec));
        wait.Until(wd => js.ExecuteScript("return document.readyState").ToString() == "complete");
    }

这将等到条件满足或超时。

于 2017-02-17T06:19:59.190 回答
3

对于初始页面加载,我注意到“最大化”浏览器窗口实际上会等到页面加载完成(包括源代码)

代替:

AppDriver.Navigate().GoToUrl(url);

和:

public void OpenURL(IWebDriver AppDriver, string Url)
            {
                try
                {
                    AppDriver.Navigate().GoToUrl(Url);
                    AppDriver.Manage().Window.Maximize();
                    AppDriver.SwitchTo().ActiveElement();
                }
                catch (Exception e)
                {
                    Console.WriteLine("ERR: {0}; {1}", e.TargetSite, e.Message);
                    throw;
                }
            }

比使用:

OpenURL(myDriver, myUrl);

这将加载页面,等到完成,最大化并专注于它。我不知道为什么会这样,但它有效。

如果您想在单击下一步或“Navigate()”以外的任何其他页面导航触发器后等待页面加载,Ben Dyer 的回答(在此线程中)将完成工作。

于 2015-06-04T06:56:15.200 回答
2

在 Nodejs 中,您可以通过 Promise 获得它...

如果您编写此代码,则可以确定当您到达 then 时页面已完全加载...

driver.get('www.sidanmor.com').then(()=> {
    // here the page is fully loaded!!!
    // do your stuff...
}).catch(console.log.bind(console));

如果您编写此代码,您将导航,selenium 将等待 3 秒...

driver.get('www.sidanmor.com');
driver.sleep(3000);
// you can't be sure that the page is fully loaded!!!
// do your stuff... hope it will be OK...

来自 Selenium 文档:

this.get( url ) → Thenable

安排一个命令导航到给定的 URL。

返回一个将在文档完成加载时解决的承诺。

Selenium 文档 (Nodejs)

于 2016-12-23T15:50:57.137 回答
1

看看挂毯网络框架。你可以在那里下载源代码

这个想法是通过 body 的 html 属性表示页面已准备好。您可以使用这个想法忽略复杂的诉讼案件。

<html>
<head>
</head>
<body data-page-initialized="false">
    <p>Write you page here</p>

    <script>
    $(document).ready(function () {
        $(document.body).attr('data-page-initialized', 'true');
    });
    </script>  
</body>
</html>

然后创建 Selenium webdriver 的扩展(根据 Tapestry 框架)

public static void WaitForPageToLoad(this IWebDriver driver, int timeout = 15000)
{
    //wait a bit for the page to start loading
    Thread.Sleep(100);

    //// In a limited number of cases, a "page" is an container error page or raw HTML content
    // that does not include the body element and data-page-initialized element. In those cases,
    // there will never be page initialization in the Tapestry sense and we return immediately.
    if (!driver.ElementIsDisplayed("/html/body[@data-page-initialized]"))
    {                
        return;
    }

    Stopwatch stopwatch = Stopwatch.StartNew();

    int sleepTime = 20;

    while(true)
    {
        if (driver.ElementIsDisplayed("/html/body[@data-page-initialized='true']"))
        {
            return;
        }

        if (stopwatch.ElapsedMilliseconds > 30000)
        {
            throw new Exception("Page did not finish initializing after 30 seconds.");
        }

        Thread.Sleep(sleepTime);
        sleepTime *= 2; // geometric row of sleep time
    }          
}

使用由 Alister Scott 编写的扩展 ElementIsDisplayed 。

public static bool ElementIsDisplayed(this IWebDriver driver, string xpath)
{
    try
    {
        return driver.FindElement(By.XPath(xpath)).Displayed;
    }
    catch(NoSuchElementException)
    {
        return false;
    }
}

最后创建测试:

driver.Url = this.GetAbsoluteUrl("/Account/Login");            
driver.WaitForPageToLoad();
于 2016-06-06T08:13:59.050 回答
1

Ben Dryer 的答案没有在我的机器上编译("The method until(Predicate<WebDriver>) is ambiguous for the type WebDriverWait")。

工作 Java 8 版本:

Predicate<WebDriver> pageLoaded = wd -> ((JavascriptExecutor) wd).executeScript(
        "return document.readyState").equals("complete");
new FluentWait<WebDriver>(driver).until(pageLoaded);

Java 7 版本:

Predicate<WebDriver> pageLoaded = new Predicate<WebDriver>() {

        @Override
        public boolean apply(WebDriver input) {
            return ((JavascriptExecutor) input).executeScript("return document.readyState").equals("complete");
        }

};
new FluentWait<WebDriver>(driver).until(pageLoaded);
于 2016-06-21T20:59:49.043 回答
1

我尝试了这段代码,它对我有用。每次我移动到另一个页面时我都会调用这个函数

public static void waitForPageToBeReady() 
{
    JavascriptExecutor js = (JavascriptExecutor)driver;

    //This loop will rotate for 100 times to check If page Is ready after every 1 second.
    //You can replace your if you wants to Increase or decrease wait time.
    for (int i=0; i<400; i++)
    { 
        try 
        {
            Thread.sleep(1000);
        }catch (InterruptedException e) {} 
        //To check page ready state.

        if (js.executeScript("return document.readyState").toString().equals("complete"))
        { 
            break; 
        }   
      }
 }
于 2016-08-12T18:47:39.570 回答
1

等待 document.ready 事件并不是解决这个问题的全部,因为这段代码仍然处于竞争状态:有时这段代码在点击事件处理之前被触发,所以直接返回,因为浏览器还没有启动正在加载新页面。

经过一番搜索,我在Obay the testing goat上找到了一个帖子,其中有解决此问题的方法。该解决方案的 c# 代码如下所示:

 IWebElement page = null;
 ...
 public void WaitForPageLoad()
 {
    if (page != null)
    {
       var waitForCurrentPageToStale = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
       waitForCurrentPageToStale.Until(ExpectedConditions.StalenessOf(page));
    }

    var waitForDocumentReady = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    waitForDocumentReady.Until((wdriver) => (driver as IJavaScriptExecutor).ExecuteScript("return document.readyState").Equals("complete"));

    page = driver.FindElement(By.TagName("html"));

}

` 我在 driver.navigate.gotourl 之后直接触发这个方法,以便它尽快获得页面的引用。玩得开心!

于 2017-03-05T13:50:34.127 回答
1

通常,当 selenium 通过单击或提交或获取方法打开新页面时,它会等待页面已加载,但问题是当页面有 xhr 调用(ajax)时,他永远不会等待 xhr 被加载,所以创建一个新的方法来监控 xhr 并等待他们,这将是一件好事。

public boolean waitForJSandJQueryToLoad() {
    WebDriverWait wait = new WebDriverWait(webDriver, 30);
    // wait for jQuery to load
    ExpectedCondition<Boolean> jQueryLoad = new ExpectedCondition<Boolean>() {
      @Override
      public Boolean apply(WebDriver driver) {
        try {
            Long r = (Long)((JavascriptExecutor)driver).executeScript("return $.active");
            return r == 0;
        } catch (Exception e) {
            LOG.info("no jquery present");
            return true;
        }
      }
    };

    // wait for Javascript to load
    ExpectedCondition<Boolean> jsLoad = new ExpectedCondition<Boolean>() {
      @Override
      public Boolean apply(WebDriver driver) {
        return ((JavascriptExecutor)driver).executeScript("return document.readyState")
        .toString().equals("complete");
      }
    };

  return wait.until(jQueryLoad) && wait.until(jsLoad);
}

if $.active == 0所以没有活动的 xhrs 调用(仅适用于 jQuery)。对于 javascript ajax 调用,您必须在项目中创建一个变量并对其进行模拟。

于 2017-09-01T14:56:44.780 回答
0

您可以编写一些逻辑来处理这个问题。我写了一个返回的方法,WebElement这个方法将被调用三次,或者你可以增加时间并添加一个空检查WebElement这是一个例子

public static void main(String[] args) {
        WebDriver driver = new FirefoxDriver();
        driver.get("https://www.crowdanalytix.com/#home");
        WebElement webElement = getWebElement(driver, "homekkkkkkkkkkkk");
        int i = 1;
        while (webElement == null && i < 4) {
            webElement = getWebElement(driver, "homessssssssssss");
            System.out.println("calling");
            i++;
        }
        System.out.println(webElement.getTagName());
        System.out.println("End");
        driver.close();
    }

    public static WebElement getWebElement(WebDriver driver, String id) {
        WebElement myDynamicElement = null;
        try {
            myDynamicElement = (new WebDriverWait(driver, 10))
                    .until(ExpectedConditions.presenceOfElementLocated(By
                            .id(id)));
            return myDynamicElement;
        } catch (TimeoutException ex) {
            return null;
        }
    }
于 2014-03-13T11:29:59.260 回答
0

我执行了一个 javascript 代码来检查文档是否准备好。为具有客户端渲染的站点调试 selenium 测试节省了我很多时间。

public static boolean waitUntilDOMIsReady(WebDriver driver) {
def maxSeconds = DEFAULT_WAIT_SECONDS * 10
for (count in 1..maxSeconds) {
    Thread.sleep(100)
    def ready = isDOMReady(driver);
    if (ready) {
        break;
    }
}

}

public static boolean isDOMReady(WebDriver driver){
    return driver.executeScript("return document.readyState");
}
于 2016-08-11T00:26:55.763 回答
0
public boolean waitForElement(String zoneName, String element, int index, int timeout) {
        WebDriverWait wait = new WebDriverWait(appiumDriver, timeout/1000);
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(element)));
        return true;
    }
于 2017-02-14T11:19:13.453 回答
0

就像 Rubanov 为 C# 编写的一样,我为 Java 编写它,它是:

    public void waitForPageLoaded() {
    ExpectedCondition<Boolean> expectation = new
            ExpectedCondition<Boolean>() {
                public Boolean apply(WebDriver driver) {
                    return (((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete")&&((Boolean)((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")));
                }
            };
    try {
        Thread.sleep(100);
        WebDriverWait waitForLoad = new WebDriverWait(driver, 30);
        waitForLoad.until(expectation);
    } catch (Throwable error) {
        Assert.fail("Timeout waiting for Page Load Request to complete.");
    }
}
于 2017-07-31T08:26:03.083 回答
0

在 Java 中,它会像下面这样:-

  private static boolean isloadComplete(WebDriver driver)
    {
        return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
                || ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
    }
于 2017-08-11T09:34:45.073 回答
0

以下代码应该可以工作:

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));
于 2017-12-01T07:35:56.943 回答
0

如果您的页面或网络连接速度较慢,则上述方法可能都不起作用。我已经尝试了所有这些,唯一对我有用的是等待该页面上的最后一个可见元素。以必应网页为例。他们在主搜索按钮旁边放置了一个相机图标(按图像搜索按钮),该按钮仅在整个页面加载后才可见。如果每个人都这样做,那么我们所要做的就是使用上面示例中的显式等待。

于 2019-02-13T23:28:27.513 回答
-1
public void waitForPageToLoad()
  {
(new WebDriverWait(driver, DEFAULT_WAIT_TIME)).until(new ExpectedCondition<Boolean>() {
      public Boolean apply(WebDriver d) {
        return (((org.openqa.selenium.JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete"));
      }
    });//Here DEFAULT_WAIT_TIME is a integer correspond to wait time in seconds
于 2013-10-07T08:49:46.837 回答
-1

这是类似的东西,在 Ruby 中:

wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until { @driver.execute_script('return document.readyState').eql?('complete') }
于 2015-04-24T21:18:44.373 回答
-1

您可以让线程休眠,直到重新加载页面。这不是最佳解决方案,因为您需要估计页面加载所需的时间。

driver.get(homeUrl); 
Thread.sleep(5000);
driver.findElement(By.xpath("Your_Xpath_here")).sendKeys(userName);
driver.findElement(By.xpath("Your_Xpath_here")).sendKeys(passWord);
driver.findElement(By.xpath("Your_Xpath_here")).click();
于 2016-06-08T15:13:14.070 回答
-1

我检查了页面加载完成,在 Selenium 3.14.0 中工作

    public static void UntilPageLoadComplete(IWebDriver driver, long timeoutInSeconds)
    {
        Until(driver, (d) =>
        {
            Boolean isPageLoaded = (Boolean)((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete");
            if (!isPageLoaded) Console.WriteLine("Document is loading");
            return isPageLoaded;
        }, timeoutInSeconds);
    }

    public static void Until(IWebDriver driver, Func<IWebDriver, Boolean> waitCondition, long timeoutInSeconds)
    {
        WebDriverWait webDriverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
        webDriverWait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
        try
        {
            webDriverWait.Until(waitCondition);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
于 2018-09-26T06:41:22.910 回答
-1

对于需要等待特定元素出现的人。(使用 C#)

public static void WaitForElement(IWebDriver driver, By element)
{
    WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
    wait.Until(ExpectedConditions.ElementIsVisible(element));
}

然后,如果您想等待,例如 DOM 中是否存在 class="error-message",您只需执行以下操作:

WaitForElement(driver, By.ClassName("error-message"));

对于 id,它将是

WaitForElement(driver, By.Id("yourid"));

于 2018-10-19T08:14:27.603 回答
-3

你在使用 Angular 吗?如果您是 webdriver 可能无法识别异步调用已完成。

我建议看一下Paul Hammants ngWebDriver。waitForAngularRequestsToFinish() 方法可以派上用场。

于 2017-01-11T13:57:11.170 回答