硒等到文件准备好


133

任何人都可以让我如何让硒等到页面完全加载时?我想要一些通用的东西,我知道我可以配置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"));

有任何想法吗?


为什么您不想使用等待?
艾美

7
您的意思是明确等待?这很耗时,我正在测试约1万页。
Girish

2
我的意思是,如果我正在测试大量链接,那么添加修复等待可能不是一个好主意,对吗?
Girish

10
等待固定的秒数是没有用的。那是猜测。
安德斯·林登(AndersLindén)2015年

鉴于页面的javascript可以运行任何通用代码,因此编写程序来等待其完成是不可能的。这是停止问题的一种形式(en.wikipedia.org/wiki/Halting_problem)。此处的任何解决方案都需要做出妥协或基于基础网页的假设。
Speedplane

Answers:


83

试试这个代码:

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

上面的代码最多等待10秒钟才能加载页面。如果页面加载时间超出限制,它将抛出TimeoutException。您捕获异常并满足您的需求。我不确定抛出异常后是否退出页面加载。我还没有尝试此代码。想要尝试一下。

这是一个隐式等待。如果设置一次,它将具有作用域,直到Web Driver实例销毁为止。

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


3
谢谢,因此,如果页面在10秒钟之前加载,在这里会发生什么,加载后仍要等待10秒钟执行下一行吗?
Girish

如果您的页面在10秒钟之前加载,则不会,这将终止等待条件并执行上一行。
Manigandan

3
当预期页面加载会花费很长时间超时并引发异常时,它不会立即等待页面加载或设置更好的加载策略。它默认为无限时间,因此您的页面加载不会抛出异常,Selenium总是尝试等待它们完全加载。
PetrJaneček13年

19
此方法的问题在于,即使隐式等待先前成功返回了WebElement对象,也可能无法完全访问DOM。然后,如果您尝试单击该元素,则会得到一个过时的元素异常。因此,这个答案并不完全安全。
djangofan 2013年

3
此超时与等待文档加载有何关系?
安德斯·林登(AndersLindén)2015年

90

您建议的解决方案仅等待DOMreadyState发出信号complete。但是Selenium默认情况下会尝试通过driver.get()element.click()方法等待页面加载中的那些(还有更多)。他们已经在阻止,他们在等待页面完全加载,并且应该可以正常工作。

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

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

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

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


20
这是不准确的,Selenium不会等待或阻止element.click()通话。
hwjp

3
@hwjp想详细说明吗?JavaDocs否则表示“如果这导致要加载新页面,则此方法将尝试阻止直到页面加载完毕。”
PetrJaneček2014年

5
参照我在邮件列表上进行的一些对话,这似乎是不正确的。selenium可能会阻止您明确请求URL的.get调用,但是对于单击调用并没有什么特殊之处,因为它无法告诉您是单击了“真正的”超链接还是被javascript拦截的超链接。 ..
hwjp 2014年

4
我在邮件列表讨论的开头链接到一个错误。甚至文档都模棱两可: “如果click()[...]是通过发送本地事件完成的,则该方法将*不*等待”
hwjp 2014年

4
因此,所有这些都打开了浏览器是否使用“本地事件”的信息。默认情况下,大多数似乎会这样: code.google.com/p/selenium/wiki/… (所以我想说这些文档充其量是误导性的。将对邮件列表执行ping操作)。
hwjp

61

这是您提供的示例的有效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");
}

1
杜德,我正在复制/粘贴。这个片段非常精彩。为什么我没有想到这一点?事实是,不止一对眼球总是可以带来一个好的解决方案。谢谢。
luis.espinal

2
您可以将其放入兼容的Java 1.7版本中,因为不支持lambda表达式
vkrams '16

3
Java 1.7版本:wait.until(new Predicate <WebDriver>(){public boolean apply(WebDriver driver){return((JavascriptExecutor)driver).executeScript(“ return document.readyState”)。equals(“ complete”);} });
luQ

1
如果有人想使用@IuQ的解决方案,则谓词已导入import com.google.common.base.Predicate
Knu8

我在VB测试应用中尝试了这个想法。大部分时间都有效。我有时会收到此错误:System.InvalidOperationException:OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse)处的JavaScript错误(UnexpectedJavaScriptError)测试应用程序单击了2级菜单链接,然后调用WaitObj.Until(Function(D)DirectCast( D,InternetExplorerDriver).ExecuteScript(“ return document.readyState”)=“ complete”)。我认为是由于浏览器卸载当前页面而不是文档对象引起的错误。也许等待1/2秒,然后再执行“返回document.readystate?想法?”
CoolBreeze

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

接下来,解决方案依赖于硒记录页面上所有元素(包括顶层元素)的(内部)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中,我没有看到对象上有一个'id'-我假设您并不是说硒会插入html id属性。一旦进一步研究了此选项,我将在此响应中添加更多内容。感谢您的帖子!
2015年

@hwjp我多次使用此解决方案,均获得了出色的效果,但看起来在某些情况下不起作用。问题的完整说明stackoverflow.com/q/31985739/4249707
El Ruso 2015年

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

是否有类似document.readyState == 'complete' && jQuery.active == 0React的东西?
Rain9333 '18 / 12/12

7
WebDriverWait wait = new WebDriverWait(dr, 30);
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState==\"complete\";"));

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

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


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的答案(在此线程中)将起作用。


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

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

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

1

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

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

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)


1

等待document.ready事件并不是解决此问题的完整方法,因为此代码仍处于争用状态:有时在处理click事件之前会触发此代码,因此由于浏览器尚未启动,因此该代码直接返回正在加载新页面。

经过一番搜索后,我在Obay上测试了山羊的帖子中找到了解决此问题的方法。该解决方案的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之后触发此方法,以便它尽快获取页面的引用。玩得开心!


1

通常,当硒通过单击或提交或获取方法打开新页面时,它将等待告诉加载页面,但问题是当页面进行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调用,您必须在项目中创建一个变量并对其进行仿真。


0

您可以编写一些逻辑来处理此问题。我已经编写了一个将返回的方法,WebElement该方法将被调用3次,或者您可以增加时间并为添加空检查,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;
        }
    }

0

我执行了JavaScript代码来检查文档是否准备就绪。节省了我很多时间来调试具有客户端渲染功能的网站的硒测试。

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

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

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

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

1
最好将JS命令组合为一个,这样您就不必两次访问该页面,例如return document.readyState == 'loaded' || return document.readyState == 'complete'
JeffC

0

以下代码可能应该起作用:

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));

0

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


-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

-1

这在Ruby中类似:

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

您的代码对我不起作用。如何切换iframe并等待onload =“ javascript:pageload(); functionalPageLoad();” 在Ruby中重新加载页面?我发现自己在页面内打开iframe,可以访问它,然后页面重新加载,并且我无法再访问该框架。我已经阅读了许多有关等待,睡眠,时间,切换到相框然后再交给父母的答案,但是我无法达到目标。
阿曼达·卡瓦拉罗

1
听起来您的问题主要是关于如何切换到iFrame的问题,而不是有关等待文档加载的问题。如果在有关使用iFrame的问题中找不到解决方案,那么最好提出一个新问题。
2015年

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

我发现Thread.sleep(5000)通常是唯一可以进行测试的方法,尽管有反对使用它的所有建议。对我而言,没有任何组合的等待元素是有效的,尤其是因为许多元素只有在实际找到之后才能被识别。
史蒂夫·斯台

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

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


-3

您正在使用Angular吗?如果您愿意,则网络驱动程序可能无法识别异步调用已完成。

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


他非常不使用Angular。该答案被否决,因为您似乎没有仔细阅读原始问题。
zelusp

但是,不要让选票让您变形。这里的人很好。批评实际上是爱的一种形式(因为有人花时间纠正任何人是因为他们在乎)。Checkout 如何以聪明的方式提出问题 -这些想法也适用于以聪明的方式回答问题。
zelusp
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.