1. 程式人生 > >利用htmlunit和jsoup來實現爬取js的動態網頁實踐(執行js)

利用htmlunit和jsoup來實現爬取js的動態網頁實踐(執行js)

更新,這就尷尬了,這篇文章部落格閱讀文章最多,但是被踩得也最多。

爬取思路:

所謂動態,就是通過請求後臺,可以動態的改變相應的html頁面,頁面並不是一開始就全部展現出來的。

大部分操作都是通過請求完成的,一次請求,一次返回。而在大多數網頁中請求往往都被開發者隱藏在了js程式碼中。

所以爬取動態網頁的思路就轉化為找出相應的js程式碼,並且執行相應的js程式碼,從而能夠通過java程式碼動態的改變頁面。

而當頁面能夠正確顯示出來,我們也就可以類似於爬取靜態網頁般去爬取資料啦!

首先,可以利用htmlunit來模擬滑鼠點選事件,這個容易實現:

	/**
	 * 通過htmlunit來獲得一些搜狗的網址。
	 * 通過模擬滑鼠點選事件來實現
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public String getNextUrl(String key){
		String page = new String();
		try {
			WebClient webClient = new WebClient();
			webClient.getOptions().setCssEnabled(false);
			webClient.getOptions().setJavaScriptEnabled(false);
			//去拿網頁
			HtmlPage htmlPage = webClient.getPage("http://pic.sogou.com/");
			//得到表單
			HtmlForm form = htmlPage.getFormByName("searchForm");
			//得到提交按鈕
			HtmlSubmitInput button = form.getInputByValue("搜狗搜尋");
			//得到輸入框
			HtmlTextInput textField = form.getInputByName("query");
			//輸入內容
			textField.setValueAttribute(key);
			//點一下按鈕
			HtmlPage nextPage = button.click();
			String str = nextPage.toString();
			page = cutString(str);
			webClient.close();
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return page;
	}

如上圖所示,我就是通過java程式碼,向搜尋礦中填入關鍵字,然後在通過getInputByValue方法獲得button控制元件,最後直接button.click(),

即可以模擬點選,並且把點選後的返回的http請求解析到htmlpage中。


這個功能其實很強大,比如你可以通過這個功能去模擬搶票,或者利用點選事件,加上搜索相關知識,將某一整個系統離線下來,並且以html的形式儲存。

接下來就是利用強大的htmlunit來執行js程式碼的過程了。

首先寫一個簡單的jsp頁面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>

</head>

<body id="body">

	<label id="test">原數字</label>
</body>
<script type="text/javascript">
	function change(value) {
		document.getElementById("test").innerHTML = value;
		return "hello htmlUnit";
	}
</script>
</html>
由上可知,jsp頁面很簡單,就一個函式change,用於給htmlUnit呼叫。

再下來就是一個使用htmlunit的類。該類通過支援JavaScript直譯器,

然後在頁面中嵌入自己寫的JavaScript程式碼從而執行,並且獲得執行後的返回結果以及返回頁面。

package com.blog.anla;

import com.gargoylesoftware.htmlunit.ScriptResult;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

public class TestMyOwnPage {
	private void action() {
		WebClient webClient = new WebClient();
		try {

			webClient.getOptions().setCssEnabled(false);
			webClient.getOptions().setJavaScriptEnabled(true); // 設定支援JavaScript。
			// 去拿網頁
			HtmlPage htmlPage = webClient
					.getPage("http://localhost:8989/testHtmlScrop/index.jsp");

			String s = "更改後的數字";
			ScriptResult t = htmlPage.executeJavaScript("change(\"" + s
					+ "\");", "injected script", 500);
			// 這裡是在500行插入這一小行JavaScript程式碼段,因為如果在預設的1行,那不會有結果
			// 因為js是順序執行的,當你執行第一行js程式碼時,可能還沒有渲染body裡面的標籤。
			HtmlPage myPage = (HtmlPage) t.getNewPage();
			String nextPage = myPage.asXml().toString();
			String nextPage2 = myPage.asText().toString();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			webClient.close();
		}

	}

	public static void main(String[] args) {
		TestMyOwnPage tmop = new TestMyOwnPage();
		tmop.action();
	}
}
在t.getNewPage()中有兩個屬性,一個是
javaScriptResult:執行該段程式碼後返回的結果,如果有(如上我寫的,就返回hello htmlunit),如果沒有(返回Undefined)。

newPage_:執行該段程式碼後返回的整個頁面。

結果如圖:

最終這段程式碼執行的結果如下:

asXml():將整個頁面的html程式碼返回給我們:


而asText()則僅僅返回頁面上能顯示的值,即head和label標籤:

這樣的執行思路也就能夠去動態的執行相應的js程式碼,從而爬取到需要的資料了。

----------------------------------------------------------------------------------------2017年7月更新--------------------------------------------------------------------------------------------------------

這兩天在做一個有關於網路爬蟲的系統

但是呢,一開始爬的時候就發現問題,js的動態頁面的爬不下來

網上找了好多方法,google也問了,主要還是提到htmlunit,以下是核心程式碼,

使用htmlunit主要就是為了模擬瀏覽器操作,因為有些連結點選無法直接通過src來獲得url,而通常使用JavaScript

進行簡單拼接後的網址,所以這樣一來,使用htmlunit直接來模擬瀏覽器點選,相比來說就更加的簡單了。

        WebClient webClient = new WebClient();  
        webClient.getOptions().setJavaScriptEnabled(true); //啟用JS直譯器,預設為true  
        webClient.getOptions().setCssEnabled(false); //禁用css支援      
        webClient.getOptions().setThrowExceptionOnScriptError(false); //js執行錯誤時,是否丟擲異常   
        webClient.getOptions().setTimeout(20000);      
        HtmlPage page = wc.getPage("http://www.hao123.com");  
	//我認為這個最重要
        String pageXml = page.asXml(); //以xml的形式獲取響應文字  
  
        /**jsoup解析文件*/  
        Document doc = Jsoup.parse(pageXml, "http://cq.qq.com");   

這個時候,就可以得到jsoup中的document物件了,接下來就好寫了,就像爬普通靜態網頁一樣了。
不過,webclient解析是還是會出現一些問題,js的問題,
主要是由於目標url的js寫的有些問題,但在實際的瀏覽器中卻會忽略,eclipse中會報異常。

今天一看,好多人踩啊哈,可能當時並沒有認真的寫部落格吧,如果大家想找一個java爬蟲的專案,可以去我的專欄

圖片搜尋  包括使用jsoup來爬圖,以及lire來建立索引以及搜尋圖片。

贈人玫瑰手留餘香,有問題的大家可以多多討論呀!