2014-03-04 97 views
22

我想刮一個網頁,其形式有很多下拉和表單中的值是相互依賴的。在很多時候,我需要代碼等待頁面刷新完成。例如,從列表中選擇一個選項後,代碼應該等到下一個列表根據此選擇填充。如果有人能夠提供指針會非常有幫助,因爲奇怪的是我的代碼只有在我給了這麼多不必要的日誌語句,而這又造成了一些延遲之後才起作用。任何改進代碼的建議都會非常有用。如何在使用casperjs時等待頁面加載?

var casper = require('casper').create({ 
    verbose: true, 
    logLevel: 'debug', 
    userAgent: 'Mozilla/5.0 poi poi poi (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22', 
    pageSettings: {} 
}); 

casper.start('http://www.abc.com', function() { 
    console.log("casper started"); 
    this.fill('form[action="http://www.abc.com/forum/member.php"]', { 
     quick_username: "qwe", 
     quick_password: "qwe" 
    }, true); 
    this.capture('screen.png'); 
}); 
casper.thenOpen("http://www.abc.com/search/index.php").then(function() { 
    this.click('input[type="checkbox"][name="firstparam"]'); 
    this.click('a#poi'); 

    casper.evaluate(function() { 
     document.getElementsByName("status")[0].value = 1; 
     document.getElementsByName("state")[0].value = 1078; 
     changeState(); //This function is associated with the dropdown ie state 
and the page reloads at this point. Only after complete refresh the code shoud execute! How can this be achieved? 
     return true; 
    }); 
    this.echo('Inside the first thenOpen' + this.evaluate(function() { 
     return document.search.action; 
    })); 
}); 
casper.then(function() { 
    this.capture("poi.png"); 
    console.log('just before injecting jquery'); 
    casper.page.injectJs('./jquery.js'); 
    this.click('input[type="checkbox"][name="or"]'); 
    this.evaluate(function() { 
     $('.boxline .filelist input:checkbox[value=18127]').attr("checked", true); 
    }); 
    this.echo('Just before pressing the add college button' + this.evaluate(function() { 
     return document.search.action; 
    })); 
    this.capture('collegeticked.png'); 
    if (this.exists('input[type="button"][name="niv"]')) { 
     this.echo('button is there'); 
    } else { 
     this.echo('button is not there'); 
    } 
    this.echo("Going to print return value"); 
    this.click('input[type="button"][name="poi"]'); // This click again causes a page refresh. Code should wait at this point for completion. 
    this.echo('Immediately after pressing the add college btn getPresentState()' + this.evaluate(function() { 
     return getPresentState(); 
    })); 
    this.echo('Immediately after pressing add colleg button' + this.evaluate(function() { 
     return document.search.action; 
    })); 
    this.capture('iu.png'); 
}); 

casper.then(function() { 
    console.log('just before form submit'); 
    this.click('form[name="search"] input[type="submit"]'); //Again page refresh. Wait. 
    this.echo('Immediately after search btn getPresentState()' + this.evaluate(function() { 
     return getPresentState(); 
    })); 
    this.echo('Immediately after search button-action' + this.evaluate(function() { 
     return document.search.action; 
    })); 
    this.capture("mnf.png"); 
}); 

casper.then(function() { 
    casper.page.injectJs('./jquery.js'); 
    this.capture("resultspage.png"); 

    this.echo('Page title is: ' + this.evaluate(function() { 
     return document.title; 
    }), 'INFO'); 
    var a = casper.evaluate(function() { 
      return $('tbody tr td.tdbottom:contains("tye") ').siblings().filter($('td>a').parent()); 
    }); 
    console.log("ARBABU before" + a.length); 
}); 

casper.run(); 

回答

9

我使用阿倫這裏提到的waitForSelector「處理方法」過: https://stackoverflow.com/a/22217657/1842033

這是我已經找到了最好的解決方案;這個'缺點'就是你需要知道你期望加載的元素。我要說缺點,我個人不認爲我遇到,我已經沒有一些種反饋說,無論我在等已經發生

this.waitForSelector("{myElement}", 
    function pass() { 
     test.pass("Found {myElement}"); 
    }, 
    function fail() { 
     test.fail("Did not load element {myElement}"); 
    }, 
    20000 // timeout limit in milliseconds 
); 

雖然我猜的情況如果你沒有視覺反饋,你可以使用waitForResource()或類似的東西。

+0

什麼是myElement的例子?如果你想等一個div,並且你在頁面上有很多div,該怎麼辦? – PositiveGuy

+1

@WTF {myElement}可以是任何你想要的選擇器,例如,如果你想要一個特定的div,那麼你可以給它一個id'myDiv',然後等待#myDiv ... –

1

我有和你一樣的經歷做同樣的事情。在用戶視角下以這種方式編寫腳本從未順利過它在不中途中斷並且非常不可靠。我正在從salesforce進行搜索,這也需要登錄。

您需要儘可能降低步數。腳本以cron工作方式。除非你在做UI測試,否則不要做表格填充/按鈕點擊。我會建議你打破過程分爲兩個部分

// this part do search and find out the exact url of your screen capture. 
// save it in a db/csv file 
1 - start by POST to http://www.abc.com/forum/member.php with username password in body. 
2 - POST/GET to http://www.abc.com/search/index.php with your search criteria, you look at what the website require. if they do POST, then POST. 

// second part read your input 
1 - login same as first part. 
2 - casper forEach your input save your capture. (save the capture result in db/csv) 

我的劇本現在純phantomjs,卡斯帕腳本只是保持崩潰沒有理由。甚至幻影也是不可靠的。每次成功搜索/下載時,我都會保存結果/狀態,每當出現錯誤時,如果不是結果的其餘部分不可預知,則退出腳本(在幻燈片中鍍鉻效果不佳)。

+0

哼。我無法將其縮小到單個POST客戶端到服務器。會做更多的研究。似乎js爬行是一個糟糕的決定,應該已經與Python一起。 – qwerty123

+0

你在Python中使用什麼,它是無頭瀏覽器?它解決鏈接/執行javascripts /遵循重定向/捕獲錯誤?有效識別差異錯誤會很大。 phantomjs做了一個'出色'的工作。 – wayne

+0

我還沒有真正與python合作過,但似乎有像scrapy這樣相當經過測試的解決方案。你如何記錄casperjs。我嘗試從casper.evaluate使用_utils_.echo,但沒有日誌正在打印到控制檯? – qwerty123

0

由於Casperjs是爲開發人員編寫的,因此期望知道加載的頁面應該處於什麼狀態,以及應該使用哪些元素來定義頁面加載狀態。

一個選項是檢查是否存在,例如,在頁面末尾加載的JavaScript資源。

運行任何類型的測試時,結果必須每次都可重複,因此冪等性是必不可少的。爲了做到這一點,測試人員必須能夠控制足夠的環境才能做到這一點。

+3

是的,但有時你不是您嘗試自動執行的頁面的開發人員,並且不知道可能會發生哪些意外事件。以自動化StackOverflow爲例。要使用它,您必須登錄,但有時您會自動登錄並且腳本中斷,但一切正常。那些東西可能不在你的控制之下。發現意外事件很難。 –

+0

這不是一個公平的比較。運行任何測試時,必須將狀態和環境控制在一定程度,以產生一致的結果。 –

+0

編寫測試時應該*完成,但有時會發生失敗。舉例來說,一個網站將在6秒內加載,但您的測試只能容忍5秒,所以測試失敗僅僅是因爲有更多的負載。這告訴你,你需要優化,但如果沒有什麼可以優化呢? –

7

我所採取的做來解決這個問題,在沒有具體的目標,並在重新加載頁面等待什麼,是使用以下命令:

var classname = 'reload-' + (new Date().getTime()), 
    callback = function(){}, 
    timeout = function(){}; 

/// It happens when they change something... 
casper.evaluate(function(classname){ 
    document.body.className += ' ' + classname; 
}, classname); 

casper.thenClick('#submit'); /// <-- will trigger a reload of the page 
casper.waitWhileSelector('body.' + classname, callback, timeout); 

這樣我不必依賴下一頁中特定的期望元素,我基本上已經做了相反的處理。我創建了一個特定的選擇器來注意,一旦選擇器無法匹配,執行就會繼續。

對於我的意圖和目的,知道頁面已經開始重新加載足夠了,我不需要等到下一頁完全重新加載。這樣我就可以觸發對重載之前和之後可能存在的元素的某些waitForSelector調用。等到臨時課程被移除後,我才知道之前存在的任何東西已經被銷燬,所以在重新加載之前不用擔心選擇元素。

+0

不錯的解決方法!是'callback'實際上重裝的頁面上執行,或者是隻要'body'消失執行,但新的頁面沒有完全加載? –

+0

@ArtjomB。謝謝,我沒有真正測試過,但我的假設不會。它可能會在上一頁被丟棄後的某個時候觸發。所以應該更像'onunload'事件。我只提到它,因爲如果你還有一點,一個'waitForSelector'或'waitForResource',你可千萬不要讓從以前/卸載的頁面虛假命中。 – Pebbl

1

我在找到click()或fill()動作重新加載子iframe中的完全相同數據的問題的解決方案時發現此問題。這裏是我對Pebbl的改進回答:

casper.clickAndUnload = function (click_selector, unload_selector, callback, timeout) { 
    var classname = 'reload-' + (new Date().getTime()); 
    this.evaluate(function (unload_selector, classname) { 
     $(unload_selector).addClass(classname); 
    }, unload_selector, classname); 

    this.thenClick(click_selector); 
    this.waitWhileSelector(unload_selector + '.' + classname, callback, timeout); 
}; 

casper.fillAndUnload = function (form_selector, data, unload_selector, callback, timeout) { 
    var classname = 'reload-' + (new Date().getTime()); 
    this.evaluate(function (unload_selector, classname) { 
     $(unload_selector).addClass(classname); 
    }, unload_selector, classname); 
    this.fill(form_selector, data, true); 
    this.waitWhileSelector(unload_selector + '.' + classname, callback, timeout); 
}; 

此解決方案假設頁面使用jQuery。不應該很難修改它沒有的頁面。 unload_selector是預計在點擊或表單提交後重新加載的元素。

0

只需評估document.readyStatecompleteinteractive。然後它被加載。

這是一個while的實現,但也許可以用時間間隔完成...

this.then(function() { 
while(this.evaluate(function() { return document.readyState != 'complete' && document.readyState != 'interactive'; })) {} 
});