Selenium 等到文档准备好
Posted
技术标签:
【中文标题】Selenium 等到文档准备好【英文标题】:Selenium wait until document is ready 【发布时间】:2013-02-13 21:30:14 【问题描述】:谁能让我如何让 selenium 等到页面完全加载?我想要一些通用的东西,我知道我可以配置 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"));
有什么想法吗?
【问题讨论】:
为什么你不想使用等待? 你的意思是明确等待?这很耗时,我正在测试大约 10k 页面。 我的意思是,如果我要测试大量链接,添加修复等待可能不是一个好主意,对吧? 等待固定的秒数是没有用的。那是猜测。 鉴于页面的 javascript 可以运行任何通用代码,编写一个等待其完成的程序是不可能的。这是停机问题 (en.wikipedia.org/wiki/Halting_problem) 的一种形式。这里的任何解决方案都需要做出妥协或基于底层网页的假设。 【参考方案1】:这是您提供的示例的有效 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';
);
【讨论】:
你能把它放在java 1.7 版本中兼容吗,因为lambda 表达式不支持 Java 1.7 版本:wait.until(new Predicateimport com.google.common.base.Predicate
我在一个 VB 测试应用程序中尝试了这个想法。大部分时间都在工作。我有时会收到此错误: System.InvalidOperationException : JavaScript error (UnexpectedJavaScriptError) at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse) 测试应用程序单击级别 2 菜单链接,然后调用 WaitObj.Until(Function(D) DirectCast( D、InternetExplorerDriver).ExecuteScript("return document.readyState") = "complete")。我认为错误是因为浏览器卸载当前页面并且不是文档对象引起的。也许在执行“return document.readystate? Ideas?之前等待1/2秒
WebDriverWait 等待 = new WebDriverWait(_driver, TimeSpan.FromSeconds(30)); wait.Until(wd => try return (wd as IJavaScriptExecutor).ExecuteScript("return (document.readyState == 'complete' && jQuery.active == 0)"); catch return false; ) ;【参考方案2】:
试试这个代码:
driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
以上代码将等待页面加载最多 10 秒。如果页面加载超过时间,它将抛出TimeoutException
。您捕获异常并满足您的需求。我不确定它是否在抛出异常后退出页面加载。我还没有尝试过这段代码。想试试看。
这是一个隐含的等待。如果您设置一次,它将具有范围,直到 Web 驱动程序实例销毁。
请参阅documentation for WebDriver.Timeouts
了解更多信息。
【讨论】:
谢谢,如果页面在 10 秒之前加载,会发生什么情况,加载后还会等待 10 秒执行下一行吗? 否,如果您的页面在 10 秒之前加载意味着它将终止等待条件并执行前一行。 这用于当您预计页面加载需要很长时间才能超时并引发异常时,它不会立即等待页面加载或设置更好的加载策略。它默认为无限时间,因此您的页面加载永远不会引发异常,Selenium 总是尝试等待它们完全加载。 这种方法的问题在于,即使隐式等待之前成功返回了 WebElement 对象,也可能无法完全访问 DOM。然后,如果您尝试单击该元素,您将得到一个过时的元素异常。所以,这个答案并不完全安全。 这个超时与等待文件加载有什么关系?【参考方案3】:您建议的解决方案只等待DOM readyState
向complete
发送信号。但默认情况下,Selenium 会尝试通过 driver.get()
和 element.click()
方法在页面加载时等待这些(以及更多)。他们已经被阻塞了,他们等待页面完全加载并且应该可以正常工作。
问题显然是通过 AJAX 请求和运行脚本进行的重定向 - Selenium 无法捕获这些,它不会等待它们完成。此外,您无法通过 readyState
可靠地捕获它们 - 它会等待一段时间,这可能很有用,但它会在所有 AJAX 内容下载之前很久就发出 complete
信号。
没有通用的解决方案可以在任何地方和每个人都适用,这就是为什么它很难,而且每个人使用的东西都有点不同。
一般规则是依靠 WebDriver 来完成他的工作,然后使用隐式等待,然后对要在页面上声明的元素使用显式等待,但还有更多技术可以完成。您应该在测试页面上选择最适合您的情况的一个(或其中几个的组合)。
有关更多信息,请参阅我的两个答案:
How I can check whether page is loaded completely or not in web driver Selenium Webdriver : Wait for complex page with javascript to load【讨论】:
那不准确,Selenium 不会等待或阻止element.click()
调用。
@hwjp 想详细说明? The JavaDocs say otherwise:“如果这会导致加载新页面,此方法将尝试阻止,直到页面加载完毕。”
cf some conversations I've had on the mailing list 这似乎不准确。 selenium 可能会阻止您明确请求 URL 的 .get 调用,但它对单击调用没有什么特别的作用,因为它无法判断您是否单击了“真实”超链接或将被 javascript 拦截的超链接。 ..
我在邮件列表讨论的开头链接到一个错误。甚至文档也模棱两可:“如果 click() [...] 是通过发送本机事件完成的,那么该方法将*不*等待”
所以这一切都取决于浏览器是否正在使用“本机事件”。他们中的大多数似乎默认情况下会:code.google.com/p/selenium/wiki/…(所以我认为这些文档充其量是误导性的。将 ping 邮件列表)。【参考方案4】:
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
【讨论】:
【参考方案5】:您可以编写一些逻辑来处理这个问题。我写了一个返回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;
【讨论】:
【参考方案6】:这是我在 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 元素。
因此,假设您要单击带有文本“我的链接”的链接,例如:
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()
我认为这是防弹的!你怎么看?
blog post about it here 中的更多信息。
【讨论】:
非常有趣的方法。一个挑战是这是否可以在首次启动浏览器后在初始页面加载时起作用。无法保证浏览器的初始状态已加载任何页面。此外,在 Java 中,我没有看到我们在对象上有一个“id”——我假设你并不是说 selenium 插入了一个 html id 属性。一旦我更多地探索了这个选项,我会在这个回复中添加更多内容。感谢您的帖子! @hwjp 我多次使用这个解决方案,效果很好,但看起来它在一种情况下不起作用。问题的完整解释***.com/q/31985739/4249707【参考方案7】:这是类似的东西,在 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,我可以访问它,然后页面重新加载,我无法再访问该框架。我已经阅读了很多关于等待、睡眠、时间、切换到框架然后切换到父级的答案,但是我无法达到我的目标。 听起来您的问题主要是关于如何切换到 iFrame,而不是关于等待文档加载。如果您在有关使用 iFrame 的问题中找不到解决方案,最好开始一个新问题。【参考方案8】:对于初始页面加载,我注意到“最大化”浏览器窗口实际上要等到页面加载完成(包括源)
替换:
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 的回答(在此线程中)将完成工作。
【讨论】:
【参考方案9】:我遇到了类似的问题。我需要等到我的文档准备好,还要等到所有 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);
【讨论】:
React 有类似document.readyState == 'complete' && jQuery.active == 0
的东西吗?【参考方案10】:
看看tapestry 网络框架。你可以在那里download source code。
这个想法是通过 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
使用扩展 ElementIsDisplayed written by Alister Scott。
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();
【讨论】:
【参考方案11】:您可以让线程休眠,直到页面重新加载。这不是最佳解决方案,因为您需要估计页面加载所需的时间。
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) 通常是唯一可以使测试正常工作的方法,尽管所有建议都反对使用它。等待元素的任何组合都不适合我,尤其是在实际找到许多元素之前无法识别它们。【参考方案12】:WebDriverWait wait = new WebDriverWait(dr, 30);
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState==\"complete\";"));
【讨论】:
【参考方案13】: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);
【讨论】:
【参考方案14】:我执行了一段 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");
【讨论】:
【参考方案15】:我试过这段代码,它对我有用。我每次移动到另一个页面时都会调用这个函数
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;
【讨论】:
【参考方案16】:在 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 Documentation (Nodejs)
【讨论】:
【参考方案17】:你在使用 Angular 吗?如果您是,webdriver 可能无法识别异步调用已完成。
我建议查看Paul Hammants ngWebDriver。 waitForAngularRequestsToFinish() 方法可以派上用场。
【讨论】:
他很少使用 Angular。此答案被否决,因为您似乎没有仔细阅读原始问题。 但不要让投票让你变形。这里的人是善意的。批评实际上是一种爱(因为如果有人花时间纠正任何人,那是因为他们在乎)。结帐How to ask questions the smart way - 这些想法也适用于以聪明的方式回答问题。【参考方案18】: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;
【讨论】:
【参考方案19】:对于 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");
这会一直等到条件满足或超时。
【讨论】:
【参考方案20】:等待 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 之后直接触发这个方法,以便它尽快获得页面的引用。玩得开心!
【讨论】:
【参考方案21】:就像 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.");
【讨论】:
【参考方案22】:在 Java 中它会像下面这样:-
private static boolean isloadComplete(WebDriver driver)
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
【讨论】:
最好将JS命令合并为一个,这样您就不必两次点击页面,例如return document.readyState == 'loaded' || return document.readyState == 'complete'
【参考方案23】:
通常当 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 调用,您必须在项目中创建一个变量并对其进行模拟。
【讨论】:
【参考方案24】:以下代码应该可以工作:
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));
【讨论】:
【参考方案25】:我检查页面加载完成,在 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);
【讨论】:
【参考方案26】:适用于需要等待特定元素出现的人。 (使用 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"));
【讨论】:
【参考方案27】:如果您的页面或网络连接速度较慢,则上述方法可能都不起作用。我已经尝试了所有这些,唯一对我有用的是等待该页面上的最后一个可见元素。以必应网页为例。他们在主搜索按钮旁边放置了一个相机图标(按图像搜索按钮),该按钮仅在加载完整页面后可见。如果每个人都这样做,那么我们所要做的就是使用上面示例中的显式等待。
【讨论】:
以上是关于Selenium 等到文档准备好的主要内容,如果未能解决你的问题,请参考以下文章
Selenium WebElement.Click() 是不是等到加载下一页?