19

無限スクロールのWebサイトをクロールするためにSeleniumWebDriverを使用しています(たとえば、他のWebサイトもクロールします!)。

問題文:

Selenium Webドライバーを使用してコンテンツの読み込みが停止するまで、無限スクロールページを下にスクロールします。

私のアプローチ: 現在私はこれを行っています-

ステップ1:ページ下部までスクロールします

JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("javascript:window.onload=toBottom();"+
                        "function toBottom(){" +
                        "window.scrollTo(0,Math.max(document.documentElement.scrollHeight," +
                        "document.body.scrollHeight,document.documentElement.clientHeight));" +
                "}");

次に、Ajaxリクエストが次のように完了するまでしばらく待ちます-

ステップ2:Ajaxリクエストが終了するのを明示的に待つ

Thread.sleep(1000);

次に、ページがスクロール可能かどうかを確認するために別のJavaスクリプトを提供します

ステップ3:ページがスクロール可能かどうかを確認する

//Alternative to document.height is to be used which is document.body.clientHeight
//refer to https://developer.mozilla.org/en-US/docs/DOM/document.height

    if((Long)js.executeScript("return " +
                                "(document.body.clientHeight-(window.pageYOffset + window.innerHeight))")>0)

上記の条件が真の場合、ステップ3の条件が偽になるまで、ステップ1〜3を繰り返します。

問題:ステップ2で 指定したくないのですがThread.sleep(1000);、バックグラウンドのAjaxリクエストが終了したかどうかをJavaスクリプトを使用して確認し、ステップ3の条件が真の場合はさらに下にスクロールします。

PS:私はページの開発者ではないので、ページを実行しているコードにアクセスできません。Webページに(ステップ1と3のように)Javaスクリプトを挿入するだけです。そして、無限スクロール中にAjaxリクエストを含むWebサイトの汎用ロジックを作成する必要があります。

ここで時間を割いてくれる人に感謝します!

編集:わかりました。2日間苦労した後、Selenium WebDriverをクロールしているページには、これらのJavaScriptライブラリのいずれかを含めることができ、別のライブラリに従ってプールする必要があることがわかりました。たとえば、jQuery apiを使用するWebアプリケーション、私は待っているかもしれません

(Long)((JavascriptExecutor)driver).executeScript("return jQuery.active")

ゼロを返します。

同様に、WebアプリケーションがPrototype JavaScriptライブラリを使用している場合は、待つ必要があります

(Long)((JavascriptExecutor)driver).executeScript("return Ajax.activeRequestCount")

ゼロを返します。

さて問題は、利用可能なほとんどのJavaScriptライブラリを処理できるジェネリックコードをどのように書くかです。

これを実装する際に直面している問題-

1。(JavaでSelenium WebDriverを使用して)Webアプリケーションで使用されているJavaScriptライブラリを見つけて、対応する待機メソッドを記述するにはどうすればよいですか?現在、これを使用しています

コード

2。このように、個別のJavaScriptライブラリ用に77ものメソッドを作成する必要があるため、このシナリオを処理するためのより良い方法も必要です。

要するに、ブラウザがSelenium Web DriverのJava実装を介して、JavaScriptライブラリの有無にかかわらず呼び出し(Ajaxまたは単純)を行っているかどうかを把握する必要があります

PS:使用されているJavaScriptライブラリを検出するChormeのJavaScriptLib検出器 とFirefoxのJavaScriptLibrary検出器のアドオンがあります。

4

3 に答える 3

11

Web ページを開く前に、無限スクロール中に Ajax Response を使用し、jQuery API (またはその他のアクション) を使用する Web ページの場合。

    //Inject the pooling status variable
    js.executeScript("window.status = 'fail';");

    //Attach the Ajax call back method
    js.executeScript( "$(document).ajaxComplete(function() {" +
    "status = 'success';});");

ステップ1:元の質問と同じままです

ステップ 2次のスクリプトをプールします (これは、必要性をなくし、Thread.Sleep()ロジックをより動的にするスクリプトです) 。

String aStatus = (String)js.executeScript("return status;");

                        if(aStatus!=null && aStatus.equalsIgnoreCase("success")){
                            js.executeScript("status = 'fail';");
                            break poolingLoop;
                        }

ステップ 3:今は必要ありません。

結論:鈍い Thread.sleep(); を与える必要はありません。Selenium WebDriverを使用している間、何度も何度も!!

このアプローチは、Web アプリケーションで jQuery API が使用されている場合にのみ有効です。

編集: @jayatiによって与えられたリンクに従って、私はjavascriptを注入しました-

Javascript 1:

//XMLHttpRequest instrumentation/wrapping
var startTracing = function (onnew) {
    var OldXHR = window.XMLHttpRequest;

    // create a wrapper object that has the same interfaces as a regular XMLHttpRequest object
    // see http://www.xulplanet.com/references/objref/XMLHttpRequest.html for reference on XHR object
    var NewXHR = function() {
        var self = this;
        var actualXHR = new OldXHR();

        // private callbacks (for UI):
        // onopen, onsend, onsetrequestheader, onupdate, ...
        this.requestHeaders = "";
        this.requestBody = "";

        // emulate methods from regular XMLHttpRequest object
        this.open = function(a, b, c, d, e) { 
            self.openMethod = a.toUpperCase();
            self.openURL = b;
            ajaxRequestStarted = 'open';

            if (self.onopen != null && typeof(self.onopen) == "function") { 
                self.onopen(a,b,c,d,e); } 
            return actualXHR.open(a,b,c,d,e); 
        }
        this.send = function(a) {
            ajaxRequestStarted = 'send';

            if (self.onsend != null && typeof(this.onsend) == "function") { 
                self.onsend(a); } 
            self.requestBody += a;
            return actualXHR.send(a); 
        }
        this.setRequestHeader = function(a, b) {
            if (self.onsetrequestheader != null && typeof(self.onsetrequestheader) == "function") { self.onsetrequestheader(a, b); } 
            self.requestHeaders += a + ":" + b + "\r\n";
            return actualXHR.setRequestHeader(a, b); 
        }
        this.getRequestHeader = function() {
            return actualXHR.getRequestHeader(); 
        }
        this.getResponseHeader = function(a) { return actualXHR.getResponseHeader(a); }
        this.getAllResponseHeaders = function() { return actualXHR.getAllResponseHeaders(); }
        this.abort = function() { return actualXHR.abort(); }
        this.addEventListener = function(a, b, c) { return actualXHR.addEventListener(a, b, c); }
        this.dispatchEvent = function(e) { return actualXHR.dispatchEvent(e); }
        this.openRequest = function(a, b, c, d, e) { return actualXHR.openRequest(a, b, c, d, e); }
        this.overrideMimeType = function(e) { return actualXHR.overrideMimeType(e); }
        this.removeEventListener = function(a, b, c) { return actualXHR.removeEventListener(a, b, c); }

        // copy the values from actualXHR back onto self
        function copyState() {
            // copy properties back from the actual XHR to the wrapper
            try {
                self.readyState = actualXHR.readyState;
            } catch (e) {}
            try {
                self.status = actualXHR.status;
            } catch (e) {}
            try {
                self.responseText = actualXHR.responseText;
            } catch (e) {}
            try {
                self.statusText = actualXHR.statusText;
            } catch (e) {}
            try {
                self.responseXML = actualXHR.responseXML;
            } catch (e) {}
        }

        // emulate callbacks from regular XMLHttpRequest object
        actualXHR.onreadystatechange = function() {
            copyState();

            try {
                if (self.onupdate != null && typeof(self.onupdate) == "function") { self.onupdate(); } 
            } catch (e) {}

            // onreadystatechange callback            
            if (self.onreadystatechange != null && typeof(self.onreadystatechange) == "function") { return self.onreadystatechange(); } 
        }
        actualXHR.onerror = function(e) {

            ajaxRequestComplete = 'err';
            copyState();

            try {
                if (self.onupdate != null && typeof(self.onupdate) == "function") { self.onupdate(); } 
            } catch (e) {}

            if (self.onerror != null && typeof(self.onerror) == "function") { 
                return self.onerror(e); 
            } else if (self.onreadystatechange != null && typeof(self.onreadystatechange) == "function") { 
                return self.onreadystatechange(); 
            }
        }
        actualXHR.onload = function(e) {

            ajaxRequestComplete = 'loaded';
            copyState();

            try {
                if (self.onupdate != null && typeof(self.onupdate) == "function") { self.onupdate(); } 
            } catch (e) {}

            if (self.onload != null && typeof(self.onload) == "function") { 
                return self.onload(e); 
            } else if (self.onreadystatechange != null && typeof(self.onreadystatechange) == "function") { 
                return self.onreadystatechange(); 
            }
        }
        actualXHR.onprogress = function(e) {
            copyState();

            try {
                if (self.onupdate != null && typeof(self.onupdate) == "function") { self.onupdate(); } 
            } catch (e) {}

            if (self.onprogress != null && typeof(self.onprogress) == "function") { 
                return self.onprogress(e);
            } else if (self.onreadystatechange != null && typeof(self.onreadystatechange) == "function") { 
                return self.onreadystatechange(); 
            }
        }

        if (onnew && typeof(onnew) == "function") { onnew(this); }
    }

    window.XMLHttpRequest = NewXHR;

}
window.ajaxRequestComplete = 'no';//Make as a global javascript variable
window.ajaxRequestStarted = 'no';
startTracing();

またはJavascript 2:

var startTracing = function (onnew) {
    window.ajaxRequestComplete = 'no';//Make as a global javascript variable
    window.ajaxRequestStarted = 'no';

    XMLHttpRequest.prototype.uniqueID = function() {
        if (!this.uniqueIDMemo) {
            this.uniqueIDMemo = Math.floor(Math.random() * 1000);
        }
        return this.uniqueIDMemo;
    }

    XMLHttpRequest.prototype.oldOpen = XMLHttpRequest.prototype.open;

    var newOpen = function(method, url, async, user, password) {

        ajaxRequestStarted = 'open';
        /*alert(ajaxRequestStarted);*/
        this.oldOpen(method, url, async, user, password);
    }

    XMLHttpRequest.prototype.open = newOpen;

    XMLHttpRequest.prototype.oldSend = XMLHttpRequest.prototype.send;

    var newSend = function(a) {
        var xhr = this;

        var onload = function() {
            ajaxRequestComplete = 'loaded';
            /*alert(ajaxRequestComplete);*/
        };

        var onerror = function( ) {
            ajaxRequestComplete = 'Err';
            /*alert(ajaxRequestComplete);*/
        };

        xhr.addEventListener("load", onload, false);
        xhr.addEventListener("error", onerror, false);

        xhr.oldSend(a);
    }

    XMLHttpRequest.prototype.send = newSend;
}
startTracing();

また、Java コードのステータス変数のステータスを確認するとajaxRequestStarted, ajaxRequestComplete、ajax が開始されたか完了したかを判断できます。

これで、Ajax が完了するまで待機する方法ができました。また、何らかのアクションで Ajax がトリガーされたかどうかを確認することもできます。

于 2012-08-01T10:42:20.583 に答える
7

アプローチ 1 :

あなたのアプローチは良いです。いくつかの変更を加えるだけでうまくいきます。

ステップ 1: を使用して定期的に toBottom 関数を呼び出すように、このステップを改善しwindow.setIntervalます。(c >= totalcount)通話時window.clearInterval

設定 2: ページがまだスクロール可能かどうかを確認する代わりに、(c >= totalcount). (c >= totalcount)そして、この条件は true を返すまで 200ms ごとに実行されます。

参考: ステップ 1 がすべてのブラウザーで機能しない場合は、おそらく、Tata-Nano-Reviews-925076578.js の 5210 行を参照して、c変数チェックでこれを呼び出すことができます。

アプローチ 2 :

jQuery APIに移動し、「ajax」と入力します。ajax リクエストに使用できるいくつかのコールバック ハンドラーを見つけることができます。

おそらく、リクエストが送信される前とリクエストが適切に受信された後に変数を設定します。

その間に、スクロールできなくなった場合を除き、元の方法で一定の間隔で一番下までスクロールします。この時点で、間隔変数をクリアします。

次に、その間隔変数が null かどうかを定期的に確認します。Null は、最下位に到達したことを意味します。

于 2012-08-01T09:16:45.210 に答える
0

同じ問題を解決する必要があり、長い Javascript 関数を使用して管理しました。チェックを追加して、どのライブラリが未定義でないかを確認するだけです。

PS 進行中のプロトタイプ リクエストを確認する方法について簡単に回答していただきありがとうございます。

例えば。JQuery と XHR/プロトタイプを処理する

var jsExecutor = /*Get your WebDriverInstance*/ as IJavaScriptExecutor;
while(/*your required timeout here*/)
{
    var ajaxComplete = 
      jsExecutor.ExecuteScript("return ((typeof Ajax === 'undefined') ||   
      Ajax.activeRequestCount == 0) && ((typeof jQuery === 'undefined') || $.active == 0)");
    if (ajaxIsComplete)
      return
}
于 2016-02-04T01:36:16.017 に答える