等待硒页面加载


Answers:


138

您还可以使用以下代码检查页面加载

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

22
您会认为将内置这样的内容。等待页面加载是Web上很常见的事情。
PRMan 2014年

19
这真的一直都有效吗?也许我在您的代码中遗漏了一些东西,但是您正在等待dom处于就绪状态。但是请考虑一下,如果您的代码执行得太快,则前一页可能尚未卸载,即使您仍在旧页面上,它也会返回true。您需要做的是等待当前页面卸载,然后调用上面的代码。检测页面卸载的一种方法是在当前页面上获取一个Web元素,然后等待它变得陈旧。obeythetestinggoat.com/…–
乔治,

6
仅供参考-即使上述内容也不能保证页面完整-只是准备好dom。任何dojo / jquery可能仍在页面上动态构建元素,因此您可能需要先等待动态元素才能与它们进行交互。
乔治,

3
请注意,此方法仅检查DOM。如果使用Ajax或AngularJS,则将无法正常工作,因为document.readyState无法检测到一些异步调用。
Homewrecker 2015年

3
这是C#代码。这不适用于Java,并且会询问有关Java的问题。
金纳米尔

89

使用类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"));

这很棒。这是解决问题的一种非常好的现代方法。
CrazyDart

3
@ EmmanuelAngelo.R TimeSpan是一个.Net数据结构。
rjzii 2014年

12
如果我不知道页面上将显示哪个元素怎么办?
JustGoscha 2014年

3
这将用于等待加载特定元素而不是整个页面。
xyz 2014年

1
这根本不能保证一个元素将被完全加载,也不能回答问题。谁能对此表示支持?
Boris D. Teoharov

36

如果设置了驱动程序的隐式等待,然后findElement在预期位于已加载页面上的元素上调用方法,则WebDriver会对该元素进行轮询,直到找到该元素或达到超时值为止。

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

来源:隐式等待


3
它不会帮助等待页面加载。
xyz 2014年

这意味着它将在引发异常之前的10秒钟内尝试某些操作。因此无法确保需要10秒的延迟。
skysign 2016年

1
@xyz-为什么没有帮助?
MasterJoe2 '16

@ testerjoe2它等待直到找到特定的元素,问题是关于如何等待页面加载。
xyz

35

通常,对于Selenium 2.0,Web驱动程序仅在确定页面已加载后才应将控制权返回给调用代码。如果没有,您可以调用waitforelemement,它会循环调用findelement直到找到它或超时(可以设置超时)。


2
补充保罗的答案。请同时检查stackoverflow.com/questions/5858743/…
2011年

27
不幸的是,Selenium 2并非在所有情况下都等待页面加载。例如,WebElement:click()不会等待,这在所属的Javadoc中已明确说明。但是,他们没有告诉我如何检查要加载的新页面。 If click() causes a new page to be loaded via an event or is done by sending a native event (which is a common case on Firefox, IE on Windows) then the method will not wait for it to be loaded and the caller should verify that a new page has been loaded.
Sebi

doc ..开始,该方法将阻塞,直到加载完成...
xyz 2014年

2
@Karna:是的,从理论上讲应该始终如此,实际上在大多数情况下都应该这样做。当时使用Selenium突出表明,有时它认为页面已完成加载,但还没有完成。我最近没有使用过硒,因此可能会或可能不会一直如此。
保罗·哈德菲尔德

3
我同意:尤其是Internet Explorer驱动程序存在错误,即使页面仍在加载,在某些情况下仍会立即返回控制。就我而言,我使用添加了一个等待JavascriptExecutor,等待document.readyState“完成”。由于从硒到浏览器的往返行程,我猜想缓解了竞争条件,并且这种“始终”对我有效。当我希望加载页面时,在“ click()”之后,我明确地(使用WebDriverWait)等待ready状态。]
dmansfield's

21

Ruby实现:

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

17
Python的等价:WebDriverWait(驱动程序,10)。直到(拉姆达d:d.execute_script( '返回document.readyState')== '完成')
杀出

2
Python使用上面的代码,但请不要忘记这一行。从selenium.webdriver.support.ui导入WebDriverWait
t3dodson 2014年

页面未完全加载时,单击元素时出现问题。在Python中,我尝试了time.sleep(30)。有效。它将始终等待最多30秒。然后,我尝试了以下代码,它现在更加高效,更快。WebDriverWait(driver,10).until(lambda d:driver.find_element_by_xpath(“ // div [。='Administration']”)。click())
Riaz Ladhani 2015年

20

您可以删除该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中。cf. javabox
boly38

17

所有这些解决方案都可以在特定情况下使用,但是它们至少会遇到以下两种可能的问题之一:

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

接下来,解决方案依赖于硒记录页面上所有元素(包括顶层元素)的(内部)ID号这一事实<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)

然后,您几乎可以在任何硒相互作用中使用它:

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

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

有关此内容博客文章中的更多信息


在重新搜索用于实现您的解决方案的Java代码之前,我先阅读了您的网页。到目前为止,还没有……
安德鲁·洛里安

11

您还可以使用class:ExpectedConditions明确地等待某个元素显示在网页上,然后再执行任何操作

您可以使用ExpectedConditions该类来确定元素是否可见:

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

请参阅ExpectedConditions class Javadoc以获取您可以检查的所有条件的列表。


10

这似乎是WebDriver的严重限制。显然,等待元素并不意味着正在加载页面,特别是可以完全构建DOM(就绪状态),从而JS仍在执行,CSS和图像仍在加载。

我相信最简单的解决方案是在初始化所有内容后在onload事件上设置JS变量,然后在Selenium中检查并等待此JS变量。


如果您要修改站点,这很简单!
reinierpost 2012年

是的,只需使用JavascriptExecutor执行jQuery.js,然后您就可以访问jQuery加载事件。但是,在极少数情况下有必要这样做。标准的Webdriver具有足够的功能来执行98%的适当等待。
djangofan 2014年

@djangofan真棒,看到一个例子...我是前端人员,所以不确定在哪里或如何使用JavascriptExecutor。
BradGreens 2014年

@BradGreens-好的,在这里查看我的项目:github.com/djangofan/jquery-growl-selenium-example 。如果您有足够的带宽来完成该示例,尽管jQuery可以正常工作,但我无法让jGrowl在该测试项目中正常工作。
djangofan 2014年

1
@dradgos除了djangofan的评论外,请在此处查看我的答案:stackoverflow.com/a/24638792/730326
jmathew 2014年

10

对于Java 7,Imran的答案重新出现:

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

9

如果要等待特定元素加载,可以在isDisplayed()上使用方法RenderedWebElement

// Sleep until the div we want is visible or 5 seconds is over
long end = System.currentTimeMillis() + 5000;
while (System.currentTimeMillis() < end) {
    // Browsers which render content (such as Firefox and IE) return "RenderedWebElements"
    RenderedWebElement resultsDiv = (RenderedWebElement) driver.findElement(By.className("gac_m"));

    // If results have been returned, the results are displayed in a drop down.
    if (resultsDiv.isDisplayed()) {
      break;
    }
}

“ 5分钟入门指南”中的示例)


4
一年后(当前的Selenium版本2.23.1),RenderedWebElementAPI中没有任何内容。但是,isDisplayed()方法现在可以直接在上使用WebElement
PetrJaneček2012年

1
当可以避免时,轮询是很糟糕的。
reinierpost 2012年

8

在此等待中显式等待或条件等待,直到给出此条件。

WebDriverWait wait = new WebDriverWait(wb, 60);
wait.until(ExpectedConditions.elementToBeClickable(By.name("value")));

这将等待每个Web元素60秒钟。

使用隐式等待页面上每个元素的等待直到给定的时间。

driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);

这将等待每个Web元素60秒钟。


5

使用隐式等待页面上每个元素的等待直到给定的时间。

driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

这将等待页面上的每个元素30秒。

另一个等待是显式等待或条件等待,在此等待中直到给定条件。

WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));

在id中,提供静态元素ID,该ID在页面加载后立即显示在页面上。


4

令您惊讶的是谓词并不是首选,因为您通常会知道接下来要与之交互的元素将在等待加载的页面上进行。我的做法一直是打造出来的谓词/像功能waitForElementByID(String id)waitForElemetVisibleByClass(String className),等等,然后在需要的地方使用和重用这些,无论是等待页面加载还是页面内容更改。

例如,

在我的测试课中:

driverWait.until(textIsPresent("expectedText");

在我的测试班级家长中:

protected Predicate<WebDriver> textIsPresent(String text){
    final String t = text;
    return new Predicate<WebDriver>(){
        public boolean apply(WebDriver driver){
            return isTextPresent(t);
        }
    };
}

protected boolean isTextPresent(String text){
    return driver.getPageSource().contains(text);
}

尽管这看起来很多,但它会为您重复检查,并且可以设置检查频率的间隔以及超时前的最终等待时间。另外,您将重用此类方法。

在此示例中,父类定义并启动WebDriver driverWebDriverWait driverWait

我希望这有帮助。


这对我有帮助!感谢您提供的优秀样本。我将其添加到我的SeleniumHelper中。cf. javabox
boly38

4

使用WebDriver的Java绑定时,等待页面加载的最佳方法是将Page Object设计模式与PageFactory一起使用。这样,您就可以利用AjaxElementLocatorFactory将其简单地用作全局等待所有元素的方式。它对诸如保管箱或复杂的javascript转换之类的元素有限制,但是它将大大减少所需的代码量并加快测试时间。可以在此博客文章中找到一个很好的例子。假定您对Core Java有基本的了解。

http://startingwithseleniumwebdriver.blogspot.ro/2015/02/wait-in-page-factory.html


4

NodeJS解决方案:

Nodejs中,您可以通过promises获得它。

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

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

如果您编写此代码,则将进行导航,硒将等待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...

从硒文档:

this.get(URL)→可以

调度命令以导航到给定的URL。

返回将在文档完成加载后解决的Promise

硒文档(Nodejs)


4

这是当前最受欢迎的答案的Java 8版本:

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

myDriver一个WebDriver对象(早些时候宣布)。

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


4

在脚本中的Function下面调用,它将等到未使用javascript加载页面时

public static boolean isloadComplete(WebDriver driver)
{
    return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
            || ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}

我将布尔值更改为void,但无法在Chrome浏览器中工作
鼻吸武器

3
/**
 * Call this method before an event that will change the page.
 */
private void beforePageLoad() {
    JavascriptExecutor js = (JavascriptExecutor) driver;
    js.executeScript("document.mpPageReloaded='notYet';");
}

/**
 * Call this method after an event that will change the page.
 * 
 * @see #beforePageLoad
 * 
 *      Waits for the previous page to disappear.
 */
private void afterPageLoad() throws Exception {
    (new WebDriverWait(driver, 10)).until(new Predicate<WebDriver>() {

        @Override
        public boolean apply(WebDriver driver) {
            JavascriptExecutor js = (JavascriptExecutor) driver;
            Object obj = js.executeScript("return document.mpPageReloaded;");
            if (obj == null) {
                return true;
            }
            String str = (String) obj;
            if (!str.equals("notYet")) {
                return true;
            }
            return false;
        }
    });
}

如果只更改文档的一部分,则可以从文档更改为元素。

这项技术的灵感来自从创始以来的答案。


3

SeleniumWaiter

import com.google.common.base.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;

public class SeleniumWaiter {

      private WebDriver driver;

      public SeleniumWaiter(WebDriver driver) {
           this.driver = driver;
      }

      public WebElement waitForMe(By locatorname, int timeout){
           WebDriverWait wait = new WebDriverWait(driver, timeout);
           return wait.until(SeleniumWaiter.presenceOfElementLocated(locatorname));
      }

      public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {
            // TODO Auto-generated method stub
            return new Function<WebDriver, WebElement>() {
                 @Override
                 public WebElement apply(WebDriver driver) {
                      return driver.findElement(locator);
                 }
            };
      }
 }

和你一起使用

_waiter = new SeleniumWaiter(_driver);

try {
   _waiter.waitForMe(By.xpath("//..."), 10);
} 
catch (Exception e) {
   // Error
}

3

您可以使用下面的现有方法在以下示例中设置pageeLoadTimeout的时间,如果页面加载时间超过20秒,则它将抛出页面重新加载异常

 WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS)

2

您可以明确地等待某个元素显示在网页上,然后再执行任何操作(例如element.click())

driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
  .until(new ExpectedCondition<WebElement>(){
        @Override
        public WebElement apply(WebDriver d) {
        return d.findElement(By.id("myDynamicElement"));
}});

这是我在类似情况下使用的,并且工作正常。


我认为driver.get等待onload函数完成,然后再将控制返回给代码,除非页面上有大量的ajax
goh 2012年

2

我的简单方法:

long timeOut = 5000;
    long end = System.currentTimeMillis() + timeOut;

        while (System.currentTimeMillis() < end) {

            if (String.valueOf(
                    ((JavascriptExecutor) driver)
                            .executeScript("return document.readyState"))
                    .equals("complete")) {
                break;
            }
        }

2

我见过的最好的方法是利用stalenessOfExpectedCondition,等待旧页面变旧。

例:

WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);

WebElement oldHtml = driver.findElement(By.tagName("html"));
wait.until(ExpectedConditions.stalenessOf(oldHtml));

它将等待十秒钟,以使旧的HTML标签过时,然后如果未发生,则引发异常。


1
WebElement过时并不意味着新页面已完成加载。
科里·戈德堡

可以,但是这意味着旧页面已完成卸载。以我的经验,知道何时卸载旧页面或是否由于某种原因而暂停同样重要。
forresthopkinsa

当然,我通常将stalenessOf与其他测试结合使用以获取完整的卸载/加载过程。
forresthopkinsa

2

我使用node + selenium-webdriver(现在的版本是3.5.0)。我为此做的是:

var webdriver = require('selenium-webdriver'),
    driver = new webdriver.Builder().forBrowser('chrome').build();
;
driver.wait(driver.executeScript("return document.readyState").then(state => {
  return state === 'complete';
}))

2

您可以使用等待。硒中基本上有两种等待类型

  • 隐式等待
  • 明确等待

-隐式等待

这很简单,请参见下面的语法:

driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);

-明确等待

在此等待中显式等待或条件等待,直到发生给定条件。

WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));

您可以使用其他属性,如visblityOf()visblityOfElement()



2

就我而言,我使用以下信息来了解页面加载状态。在我们的应用程序中,存在正在加载的gif,我按照以下方式进行监听,以消除脚本中不必要的等待时间。

public static void processing(){ 
    WebDriverWait wait = new WebDriverWait(driver, 30);
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[@id='Msgpanel']/div/div/img")));
    wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[@id='Msgpanel']/div/div/img")));
}

xpath在HTML DOM中找到gif的位置。此后,您还可以实现您的操作方法Click。

public static void click(WebElement elementToBeClicked){
    WebDriverWait wait = new WebDriverWait(driver, 45);
    wait.until(ExpectedConditions.visibilityOf(element));
    wait.until(ExpectedConditions.elementToBeClickable(element)); 
    wait.ignoring(NoSuchElementException.class).ignoring(StaleElementReferenceException.class); elementToBeClicked.click(); 
 }

2

男人所有这些答案都需要太多的代码。这应该很简单,因为它很常见。

为什么不只是使用Webdriver注入一些简单的javascript并进行检查。这是我在webscraper类中使用的方法。即使您不懂js,JavaScript还是很基本的。

    def js_get_page_state(self):        
    """
    Javascript for getting document.readyState
    :return: Pages state.

    More Info: https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
    """
    ready_state = self.driver.execute_script('return document.readyState')
    if ready_state == 'loading':
        self.logger.info("Loading Page...")
    elif ready_state == 'interactive':
        self.logger.info("Page is interactive")
    elif ready_state == 'complete':
        self.logger.info("The page is fully loaded!")
    return ready_state

1

单击后如何使Selenium等待页面加载提供了以下有趣的方法:

  1. 存储WebElement对旧页面中a的引用。
  2. 点击链接。
  3. 继续进行WebElement直到调用的操作StaleElementReferenceException

样例代码:

WebElement link = ...;
link.click();
new WebDriverWait(webDriver, timeout).until((org.openqa.selenium.WebDriver input) ->
{
    try
    {
        link.isDisplayed();
        return false;
    }
    catch (StaleElementReferenceException unused)
    {
        return true;
    }
});

好主意!我从来没有想过通过检查成功的页面加载,成功的页面卸载(因为缺少更好的术语)的对立来解决这种情况。尽管这是否是最佳选择是基于StaleElementReferenceException是否比等待成功加载花费了更少的时间等。尽管如此,另一种好的方法。
约瑟夫·奥兰多

4
这并不是要告诉您当前页面是否已加载,而是要告诉您是否最后一页(特别是一个元素)都已卸载。您仍然需要等待当前页面加载。顺便说一句,您可以将上面的代码替换为new WebDriverWait(driver, 10).until(ExpectedConditions.stalenessOf(element))
JeffC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.