web-dev-qa-db-ja.com

Seleniumは文書の準備ができるまで待つ

ページが完全に読み込まれるまで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"));

どんな考えだれでも?

119
Girish

このコードを試してください。

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

上記のコードはページの読み込みを最大10秒間待ちます。ページの読み込みが時間を超えると、TimeoutExceptionがスローされます。あなたは例外を捕まえてあなたの必要をします。例外がスローされた後にページの読み込みが終了するかどうかはわかりません。私はまだこのコードを試していませんでした。ただ試してみたい。

これは暗黙の待機です。一度設定すると、Webドライバインスタンスが破棄されるまで有効範囲があります。

詳細 情報について

78
Manigandan

提案された解決策は、 DOM readyStatecompleteを通知するのを待つだけです。しかし、Seleniumはデフォルトで driver.get()を介してページロード時にそれら(およびもう少し)を待機しようとします。 element.click() メソッドそれらはすでにブロックしています、彼らはページが完全にロードされるのを待ちます、そして、それらはうまく動くはずです。

問題は、明らかに、AJAXリクエストと実行中のスクリプトを介したリダイレクトです - これらはSeleniumに捕まえることができず、それらが完了するのを待ちません。また、あなたはそれらをreadyStateで確実に捕らえることはできません - 少し待って、それは役に立ちますが、すべてのAJAXコンテンツがダウンロードされるずっと前にcompleteを知らせます。

どこにでも誰にとってもうまくいく一般的な解決策はありません。それが難しいので、誰もが少し違うものを使うのです。

一般的なルールは、WebDriverに依存して、暗黙的な待機を使用し、次にページでアサートする要素に対して明示的な待機を使用することです。ただし、実行できる手法は他にもたくさんあります。テスト済みのページで、自分のケースに最も適したもの(またはそれらのいくつかの組み合わせ)を選択する必要があります。

これに関する私の2つの答えを参照してください。

82
Petr Janeček

これはあなたが与えた例の実用的な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");
}
57
Ben Dyer

これが私のPythonでの完全に一般的な解決策の試みです。

まず、一般的な "wait"関数(好きなら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がトップレベルの<html>要素を含むページ上のすべての要素のための(内部の)id番号を記録するという事実に頼ります。ページが更新またはロードされると、新しいIDを持つ新しいhtml要素が取得されます。

そのため、例えば "my link"というテキストのリンクをクリックしたいとします。

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

それは防弾だと思います!どう思いますか?

それについての ブログ記事の詳細情報はこちら

11
hwjp

私は同様の問題を抱えていました。私は自分の文書の準備ができるまで待つだけでなく、すべてのAjax呼び出しが終わるまで待つ必要がありました。 2番目の条件は検出が難しいことがわかりました。結局私はアクティブな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);
}
7
Rubanov
WebDriverWait wait = new WebDriverWait(dr, 30);
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState==\"complete\";"));
6
Ram Bharath

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

これは、条件が満たされるかタイムアウトするまで待機します。

4
S.Akruwala

最初のページロードでは、ブラウザウィンドウを「最大化」すると、実際にはページロードが完了するまで待機します(ソースを含む)。

交換します。

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

これによりページがロードされ、完了するまで待機し、最大化してそのページに集中します。私はなぜそれがこのようなのかわからないが、それはうまくいく。

Nextまたは他のページナビゲーショントリガ "Navigate()"をクリックした後にページの読み込みを待機する場合は、Ben Dyerの回答(このスレッド内)が動作します。

3
Roei Sabag

Nodejsであなたは約束を通してそれを得ることができます...

あなたがこのコードを書くならば、あなたがその時に着くときあなたはページが完全にロードされていると確信することができます...

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)→その後

指定されたURLにナビゲートするためのコマンドをスケジュールします。

ドキュメントの読み込みが完了したときに解決されるプロミスを返します。

Seleniumドキュメント(Nodejs)

1
sidanmor

seleniumがclick、submitあるいはgetメソッドから新しいページを開くとき、通常はページがロードされるまで待ちますが、問題は、ページがxhr呼び出し(ajax)を持っていてxhrがロードされるのを待たないことです。 xhrを監視してそれらを待つための新しい方法を作成するのは良いことです。 (私の下手な英語が残念:D)

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

$ .active == 0の場合、これはアクティブなxhrs呼び出しではありません(これはjQueryでのみ機能します)。 JavaScriptのAjax呼び出しの場合は、プロジェクト内に変数を作成してそれをシミュレートする必要があります。

1
elazzam

tapestry web-frameworkを見てください。 ソースコードのダウンロード ができます。

アイデアは、そのページが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 Webドライバの拡張機能を作成します(タペストリーフレームワークに従って)。

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
Tomas Kubes

私はこのコードを試しました、そしてそれは私のために働きます。別のページに移動するたびにこの関数を呼び出します

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
SOAlgorithm

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
Hazel Troost

このコードはまだ競合状態にあるため、document.readyイベントを待つだけでは問題は解決しません。ブラウザが起動していないため、clickイベントが処理される前にこのコードが呼び出されて直接戻る場合があります。新しいページをまだ読み込んでいます。

いくつかの検索の後、私は に投稿を見つけました。テスト用のヤギ に従ってください。そのソリューションの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
Maarten Kieft

これを処理するためのロジックを書くことができます。私はWebElementを返すメソッドを書きました、そしてこのメ​​ソッドは3回呼ばれるでしょう、あるいはあなたは時間を増やしてWebElementのためにnullチェックを追加することができます

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
Vaibhav Pandey

特定の要素が現れるのを待つ必要がある人々のために。 (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"));

0
Mo D Genesis

次のコードはおそらくうまくいくはずです。

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));
0
vrn
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
akhilesh gulati

ドキュメントの準備ができているかどうかを確認するために、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");
}
0
bertanasco

低速のページ接続またはネットワーク接続を使用している場合は、上記のいずれも機能しない可能性があります。私はそれらすべてを試しました、そして私のために働いた唯一のことはそのページの最後の目に見える要素を待つことです。例えばBingのWebページを見てください。彼らは、完全なページがロードされた後にだけ見えるメインの検索ボタンの隣にカメラアイコン(画像ボタンで検索)を置きました。みんながそれをしたのなら、上の例のように明示的な待機を使用するだけです。

0
user3988148

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

Javaではそれは以下のようになります: -

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