【发布时间】:2011-08-17 15:04:07
【问题描述】:
如何让Selenium 2.0 等待页面加载?
【问题讨论】:
-
对我来说只有Paul's 的答案看起来是正确的,大多数投票率很高的答案都是关于等待特定元素的。
标签: java selenium webdriver selenium-webdriver
如何让Selenium 2.0 等待页面加载?
【问题讨论】:
标签: java selenium webdriver selenium-webdriver
您还可以使用以下代码检查页面加载
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"));
【讨论】:
另见here
您可以期望显示一些元素。类似于 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")));
【讨论】:
如果您设置了驱动程序的隐式等待,然后对您希望在加载页面上的元素调用findElement 方法,WebDriver 将轮询该元素,直到找到该元素或达到超时值.
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
【讨论】:
一般来说,在 Selenium 2.0 中,Web 驱动程序只有在确定页面已加载后才应将控制权返回给调用代码。如果没有,你可以调用waitforelemement,循环调用findelement,直到找到或超时(可以设置超时)。
【讨论】:
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.
JavascriptExecutor 添加了一个等待,等待 document.readyState “完成”。由于从 selenium 到浏览器的往返,我猜竞争条件得到了缓解,这“总是”对我有用。在“click()”之后,当我期望页面加载时,我明确地等待(使用 WebDriverWait)准备就绪状态。 ]
Ruby 实现:
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
@driver.execute_script("return document.readyState;") == "complete"
}
【讨论】:
您可以删除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");
}
});
}
【讨论】:
所有这些解决方案都适用于特定情况,但它们至少存在以下几个可能的问题之一:
它们不够通用——它们希望您提前知道您将要访问的页面的某些特定条件(例如某些元素将被显示)
它们对竞争条件开放,您可以使用在旧页面和新页面上实际存在的元素。
这是我对避免此问题的通用解决方案的尝试(在 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 中的更多信息
【讨论】:
这是目前most upvoted answer的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。
【讨论】:
WebDriverWait(drive, long) 已弃用,因此请使用 WebDriverWait(drive, duration) ex:- import java.time.Duration; WebDriverWait(driver, Duration.ofSeconds(5));
您还可以使用类:ExpectedConditions 明确等待一个元素出现在网页上,然后您才能采取任何进一步的行动
您可以使用ExpectedConditions 类来确定元素是否可见:
WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));
请参阅ExpectedConditions class Javadoc 了解您可以检查的所有条件的列表。
【讨论】:
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");
}
});
【讨论】:
这似乎是 WebDriver 的一个严重限制。显然,等待一个元素并不意味着页面正在加载,特别是 DOM 可以完全构建(就绪状态),其中 JS 仍在执行,CSS 和图像仍在加载。
我相信最简单的解决方案是在一切初始化后在 onload 事件上设置一个 JS 变量,然后在 Selenium 中检查并等待这个 JS 变量。
【讨论】:
如果您想等待特定元素加载,可以在RenderedWebElement 上使用isDisplayed() 方法:
// 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;
}
}
【讨论】:
RenderedWebElement。但是,isDisplayed() 方法现在可以直接在 WebElement 上使用。
所有这些答案都需要太多代码。这应该是一件很简单的事情,因为它很常见。
为什么不使用 webdriver 注入一些简单的 Javascript 并检查。 这是我在 webscraper 类中使用的方法。即使你不知道,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
【讨论】:
在此等待中显式等待或条件等待,直到给出此条件。
WebDriverWait wait = new WebDriverWait(wb, 60);
wait.until(ExpectedConditions.elementToBeClickable(By.name("value")));
这将等待每个网络元素 60 秒。
使用隐式等待页面上的每个元素直到给定时间。
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
这将等待每个网络元素 60 秒。
【讨论】:
我很惊讶谓词不是首选,因为您通常知道接下来将在等待加载的页面上与哪些元素进行交互。我的方法一直是构建谓词/函数,如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 driver和WebDriverWait driverWait。
我希望这会有所帮助。
【讨论】:
使用隐式等待页面上的每个元素直到给定时间。
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,一旦页面加载,它就会在页面上以不同的方式显示。
【讨论】:
在为 WebDriver 使用 Java 绑定时等待页面加载的最佳方式是使用 Page Object 设计模式和 PageFactory。这使您可以使用AjaxElementLocatorFactory,简单地说,它充当所有元素的全局等待。它对诸如下拉框或复杂的 javascript 转换等元素有限制,但它会大大减少所需的代码量并加快测试时间。在这篇博文中可以找到一个很好的例子。假定对 Core Java 有基本的了解。
http://startingwithseleniumwebdriver.blogspot.ro/2015/02/wait-in-page-factory.html
【讨论】:
在你的脚本中调用下面的函数,这将等到页面没有使用 javascript 加载
public static boolean isloadComplete(WebDriver driver)
{
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
【讨论】:
NodeJS 解决方案:
在 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 Documentation (Nodejs):
this.get( url ) → Thenable<undefined>安排一个命令导航到给定的 URL。
返回一个将在文档加载完成时解决的承诺。
【讨论】:
您可以使用以下现有方法来设置pageLoadTimeout。在下面的示例中,如果页面加载时间超过 20 秒,则会引发页面重新加载异常:
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
【讨论】:
/**
* 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;
}
});
}
您可以从文档更改为元素,以防仅更改文档的一部分。
这项技术的灵感来自 sincebasic 的答案。
【讨论】:
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
}
【讨论】:
我的简单方法:
long timeOut = 5000;
long end = System.currentTimeMillis() + timeOut;
while (System.currentTimeMillis() < end) {
if (String.valueOf(
((JavascriptExecutor) driver)
.executeScript("return document.readyState"))
.equals("complete")) {
break;
}
}
【讨论】:
我见过的最好的方法是利用stalenessOf ExpectedCondition,等待旧页面变得陈旧。
例子:
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement oldHtml = driver.findElement(By.tagName("html"));
wait.until(ExpectedConditions.stalenessOf(oldHtml));
它会等待 10 秒钟让旧的 HTML 标记过时,如果没有发生则抛出异常。
【讨论】:
我使用 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';
}))
【讨论】:
您可以使用等待。 selenium 中基本上有两种等待方式
- 隐式等待
这很简单,请看下面的语法:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
- 显式等待
在此等待中显式等待或条件等待,直到给定条件发生。
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
您可以使用其他属性,例如visblityOf()、visblityOfElement()
【讨论】:
如果有人使用硒化物:
public static final Long SHORT_WAIT = 5000L; // 5 seconds
$("some_css_selector").waitUntil(Condition.appear, SHORT_WAIT);
更多条件可以在这里找到: http://selenide.org/javadoc/3.0/com/codeborne/selenide/Condition.html
【讨论】:
就我而言,我使用以下内容来了解页面加载状态。在我们的应用程序中存在加载 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();
}
【讨论】:
您可以明确地等待某个元素显示在网页上,然后才能执行任何操作(例如 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"));
}
}
);
这是我在类似场景中使用的,效果很好。
【讨论】:
How to get Selenium to wait for page load after a click 提供了以下有趣的方法:
WebElement 的引用。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;
}
});
【讨论】:
new WebDriverWait(driver, 10).until(ExpectedConditions.stalenessOf(element))。