1. 程式人生 > 實用技巧 >Selenium開啟瀏覽器載入慢的原因

Selenium開啟瀏覽器載入慢的原因

在自動化元素定位操作中經常使用智慧等待來加強定位的強壯性,主要就是因為WebDriver沒有提供頁面載入場景的方法;在使用JavaScript知識的突然心生靈感,可以使用JavaScript來配合驗證頁面載入,結果發現我真是井底之蛙。

一、domcument.readyState

首先定位從Document物件出發,而Document物件是在html文件載入完成便可操作使用,所以判斷檔案裝載完成即可;Document物件的readyState屬性返回當前文件裝載狀態:

uninitialized 表示未開始載入
loading 載入中,下載了html並且開始載入
interactive 已載入完html內容
complete 已載入完html內容和下載解析了子資源(js、css、image),不包含ajax非同步。
public static void pageDOMLoadComplete(){
       JavaScriptExecutor js=(JavaScriptExecutor) SeleniumMethod.ThreadDriver.get();
       try {
              while(!"complete".equals(js.executeScript("return document.readyState"))){
                     Thread.sleep(2000);
       }
         logger.info("頁面元素載入完成");
       } catch (Exception e) {
              // TODO: handle exception
              e.printStackTrace();
              logger.info("判斷頁面元素載入異常");
       }     
}

  

如上程式碼,如果結果返回的complete表示載入完成,這時候就可以開始元素定位操作。返回不是complete則使用執行緒迴圈等待,直到載入完成才跳出迴圈。

詳細瞭解的建議去腦補一下瀏覽器對頁面載入的過程。通過瀏覽器傳送請求到伺服器響應先得到html形成Document物件,瀏覽器解析html形成dom樹開始載入,當載入dom樹遇到外部資源時去請求外部資源,但載入dom樹並不會等待會繼續載入,當資源如JavaScript、css、圖片等等載入完成這時候document.readyState的值變成complete。

二、Selenium載入策略

因為Selenium從頭到位都沒有任何判斷頁面載入完成的關鍵字;而結合document.readyState的返回來確定頁面載入成功,然後再去執行定位操作,這樣來說是個合理的思想。根據想法編寫下面的程式碼:

WebDriver driver = new ChromeDriver();
driver.get("https://www.sina.com.cn/");
try {
	while(!"complete".equals(((JavaScriptExecutor) driver).executeScript("return document.readyState"))) {
		Thread.sleep(1000);
	}
} catch(Exception e) {
	// TODO: handle exception
	e.printStackTrace();

}
driver.findElement(By.name("SerchKey")).sendKeys("軟體測試");
driver.findElement(By.name("SearchSubButton")).click();
driver.quit();

  

上面程式碼利用JavaScript執行一段指令碼document.readyState的值返回不是complete則一直迴圈併線程等待1S,直到返回的值是complete退出迴圈。

但是在程式碼執行後發現,從來都沒有進入過迴圈。於是猜測,是get()方法在開啟瀏覽器並訪問URL時,是不是就已經判斷過頁面載入完成了?驗證方法很簡單,把迴圈去掉,程式碼直接列印document.readyState返回的值

System.out.print(((JavaScriptExecutor)driver).executeScript("return document.readyState"));

  

得到的結果每次都是complete,驗證確實get()方法後就已經頁面載入成功了。在使用WebDriver時,每次開啟瀏覽器都知道它並不會帶有任何快取資料,而是一個全新初始化的瀏覽器,這點只要用過Selenium的同學,並做過登入功能都應該知道,每次使用都需要重新登入,瀏覽器並不會儲存任何cookie、快取資訊。就是因為這個原因,WebDriver每次調起瀏覽器並訪問URL的時候因為沒有快取所以頁面載入會非常慢(當然不僅僅是快取,WebDriver還會去遍歷一些其他東西,如虛擬網絡卡、代理配置等);這個慢的現象大多數是頁面已經展示內容了,然後瀏覽器還在轉圈載入,然而使用document.readyState來判斷好像不現實了,因為WebDriver的get()方法預設就會等待頁面載入完。所以思路就轉化為,get()方法的探究。

通過官方資料查詢,發現WebDriver有pageLoadStrategy載入策略這一說,地址如下:https://www.w3.org/TR/webdriver/#dfn-table-of-page-load-strategies,大家可以自己去瞧瞧。載入策略有如下三種:

none:沒有說明對應關係
eager:對應ducument readiness state為”interactive”
normal:對應ducument readiness state為”complete”

從上面三種載入策略可以看到,官方除了none這種沒有具體說明外,另外兩種對應的正式document.readyState返回的狀態。而預設的載入策略是normal,所以再次驗證為什麼使用document.readyState得到的值是complete。驗證程式碼如下:

DesiredCapabilities capabilities = DesiredCapabilities.chrome();
capabilities.setCapability("pageLoadStrategy", "none");
WebDriver driver = new ChromeDriver(capabilities);
driver.get("https://www.sina.com.cn/");
System.out.println(((JavaScriptExecutor) driver).executeScript("return document.readyState"));
driver.findElement(By.name("SerchKey")).sendKeys("軟體測試");
driver.findElement(By.name("SearchSubButton")).click();
System.out.println(((JavaScriptExecutor) driver).executeScript("return document.readyState"));
driver.quit();

  

執行程式碼,列印document.readyState返回的結果是loading;在使用findElement查詢元素時報錯“Unable to locate element”。當載入策略為none時,對應document.readyState結果是loading的時候,頁面還在載入中,進行元素查詢會報錯,這很好理解,Dom樹還在載入過程中,是無法解析任何元素的查詢的,所以報錯。

接下來分別嘗試了eager載入策略,需要注意一點的是Chrome瀏覽器並不支援這種載入策略,所以改為了Firefox驗證。

DesiredCapabilities capabilities = DesiredCapabilities.firefox();
capabilities.setCapability("pageLoadStrategy", "eager");
WebDriver driver = new FirefoxDriver(capabilities);

  

其他程式碼不變,執行程式碼列印document.readyState返回的結果是interactive,但是不同的是使用findElement查詢元素沒有報錯,並且全部執行成功。當載入策略為eager,頁面載入結果interactive時,整個DOM樹載入完成,這時候就已經可以定位元素並執行操作了;根據document.readyState與WebDriver的載入策略得出以下結論:

(1)loading與none

DOM樹 載入中,也就是說已經下載了html並且開始載入;這時候是無法查詢元素的,get()方法的執行時間非常短,可以馬上執行get()方法後面的程式碼。所以這時候就可以使用下面程式碼再次進行判斷後再執行。

while(!"complete".equals(((JavascriptExecutor)driver).executeScript("return document.readyState"))){}

  

這樣的作法好像與載入策略的預設方法沒什麼不同,但是WebDriver的get()方法可不僅僅是等待頁面為complete而已,它還會去判斷一些瀏覽器其他的配置。

這種情況還有一種不建議使用的方式,就是使用隱式等待,每次查詢元素找不到時,等待一定時間。隱式等待在當前driver的整個生命週期中都生效,是非常適合配合none這種載入策略的。

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

  

新片場https://www.wode007.com/sites/73286.html 傲視網https://www.wode007.com/sites/73285.html

(2)interactive與eager

已載入完html內容,但是沒有解析子資源;這時候DOM樹載入完成,並且去請求子資源了,是可以查詢元素並正確返回了。有個弊端就是如果一些資料是通過JavaScript去賦值的,這時候查詢元素某些屬性資料可能得到空。

(3)complete與normal

已載入完html內容和下載解析了子資源(JavaScript、css、image),不包含ajax非同步。前面應該都沒疑問,重要的是ajax非同步請求的載入。如果頁面上一些資料是通過ajax非同步去請求並載入的,同樣在ajax請求中資料響應沒有及時回來的情況下,拿到的也是空。當然這並不需要擔心,只要不是太大資料請求響應還是非常快速的。