李尚林,陳宮,雷勇
(桂林理工大學 信息科學與工程學院,廣西 桂林 541004)
如今信息化時代學習方式早已不同以前,利用網絡開展學習必不可少。網絡資源不同于傳統學習資源,具有實時性高、數量大、內容豐富、傳播速度快、范圍廣、易共享等特點。而且通過網絡學習不受限于環境因素,這種隨時隨地獲取學習資源的形式特別迎合快節奏人群的學習需求[1]。但隨著互聯網技術的日益發展,網絡中信息量呈爆炸式增長,在搜索學習資源時盡管搜素引擎會幫助用戶抓取一部分網絡信息,但對于信息需求的不同往往還需要花費大量時間進行過濾篩選,顯然這樣的方式既費時又費力。爬蟲技術的誕生有效的解決了這個問題。由于爬蟲的種種優點,其也成為現代人檢索網絡信息的重要工具。
如何使用爬蟲技術讓程序高效、自動化的獲取資源信息,簡化獲取學習資源的過程,成為人們在使用爬蟲時所面臨的主要問題。因此本文探究了靜態網頁爬蟲和動態網頁爬蟲的設計過程,實現了靜態網頁的多線程泛用性爬蟲,并以百度圖片、百度文庫為例設計實現了動態網頁的多線程特殊性爬蟲,完成對所需資源的高效爬取。
網絡爬蟲,又稱為網絡機器人或者網頁蜘蛛,是一種按照自身需求和一定規則自動抓取萬維網信息的腳本或程序。如今使用爬蟲檢索信息和資源已成為人們訪問萬維網的入口和指南。
按照系統結構和實現技術的不同,爬蟲可大致分為四類[2]:
(1)通用網絡爬蟲。其爬取對象可從一個種子延伸到整個Web。這類爬蟲爬取范圍廣、數量大,因此對爬取速度和存儲空間要求較高。典型應用為百度、谷歌搜索等大型搜索引擎。
(2)聚焦網絡爬蟲。聚焦網絡爬蟲又稱為主題爬蟲,它是按照預先定義好的主題進行相關頁面爬取的網絡爬蟲。由于只爬取與主題相關的頁面,聚焦網絡爬蟲可大大節省硬件和網絡資源。
(3)增量式網絡爬蟲。增量式網絡爬蟲指不爬取重復部分,只對已經獲取網頁的新增部分進行爬取。其采取增量式更新的爬取方法,可有效減少爬取次數。
(4)深層網絡爬蟲。Web頁面按照存在方式可分為表層網頁和深層網頁。表層網頁是指搜索引擎可以直接索引,通過超鏈接即可進入到靜態網頁主體;深層網頁是指需要用戶提交關鍵詞或表單數據才能訪問到的網頁。因此深層網絡爬蟲需要分析頁面結構,將預先準備的數據自動填寫到表單中并提交至服務器驗證處理,最終才能獲得相應的資源信息。
實際中的網絡爬蟲應用往往是幾種網絡爬蟲技術相結合實現的。
HTTP是面向事務的應用層協議,它是萬維網上能夠可靠交換文件(包括文本、聲音、圖像等各種多媒體文件)的重要基礎[3]。在Java網絡編程中會經常遇到HTTP協議編程,雖然JDK提供了HttpURLConnection編程接口對HTTP協議進行支持,但由于協議應用本身的復雜性,使得在大量實際項目中單純使用JDK進行HTTP編程仍然比較困難[4]。HttpClient 是Java所支持的 Apache Jakarta Common 下的子項目,用來提供和支持 HTTP 協議的客戶端編程,不僅如此它還支持 HTTP 協議最新版本和規范。HttpClient的出現大大降低了HTTP網絡編程的復雜度,使其也成為了Java網絡爬蟲的核心技術之一。
Jsoup是一款HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM、CSS以及類似于jQuery的操作方法來取出和操作數據,只要解析的內容符合規定就能返回一個簡潔的頁面詳情[5]。由于Jsoup良好的可擴展性API 設計,以及強大的HTML解析功能,其也廣泛的應用于Java網絡爬蟲中。
計算機中正在執行的程序被稱為“進程”,相對于程序來說進程是一個動態的概念,是程序的一次執行過程。線程相比進程來說屬于更小的運行單位,一個線程是指進程中一個單一順序的控制流。一個進程中可以并發多個線程,每個線程并行執行不同的任務。在如今信息高速化時代單線程的效率是非常低的,多線程技術可有效提高程序運行效率[6]。
本文研究的爬蟲系統主要分為兩部分,靜態網頁的泛用性爬取和動態網頁的特殊性爬取。靜態網面的泛用性爬取涉及的功能模塊有:設置參數(User-Agent和cookie)模塊、獲取目標URL的HTML文本模塊、Jsoup解析模塊、深度爬取模塊。動態網頁的特殊性爬取涉及的功能模塊有:百度圖片按關鍵字爬取模塊、百度文庫文檔爬取模塊。下面對這些功能模塊進行詳細描述。
2.2.1 靜態頁面爬蟲設計及實現
靜態網頁爬取功能的核心是通過HttpClient對目標靜態網頁發出請求并取得服務器響應,接著將獲得的響應內容(HTML文本)進行解析,獲取所需資源的路徑,從而將資源爬取下來[7]。靜態網頁爬取流程如圖1所示:

圖1 靜態網頁爬取流程圖
(1)設置參數(User-Agent和cookie)模塊的設計與實現。在HTTP協議中,報文的請求頭部Header可用于服務器識別數據包來源(報文請求頭部結構如圖2所示),其中頭部參數 User-Agent用于服務器識別用戶身份,cookie中存儲用戶密碼等信息[8]。如今網站大都有反爬蟲措施,因此需要使用上述兩個參數模擬真實瀏覽器向服務器發送請求,防止被服務器攔截[9]。user-Agent和cookie設置界面如圖3所示。該部分核心代碼如下:

圖2 http協議請求報文結構

圖3 user-Agent、cookie設置界面
//設置請求頭
httpGet.setHeader(“User-Agent”, 真實瀏覽器的User-Agent);
httpGet.setHeader(“cookie”, COOKIE);
(2)獲取目標URL的HTML文本模塊的設計與實現。靜態網頁爬取的起點即為獲取目標URL的HTML文本內容。設置請求數據包頭部信息后使用HttpClient技術模擬真實瀏覽器向服務器發送請求,最終獲得服務器響應得到所需的HTML文本信息,為下一步的內容解析做出準備。除此之外,還可以輸入多個URL進行多線程爬取。該部分核心代碼如下:
//獲取客戶端工具
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
//創建get請求方法實例,并指定請求url
HttpGet httpGet = new HttpGet(url);
//發送請求,獲取響應
response = httpClient.execute(httpGet);
(3)Jsoup解析模塊的設計與實現。獲取的HTML文本中往往只有某些特定元素是所需要的。因此利用Jsoup對HTML文本進行解析過濾,將所需資源從HTML文本中提取出來。通過觀察所獲取的HTML文本發現本文所需要的資源在a標簽中,同時后續還需要進行深度爬取,因此利用Jsoup過濾出所有有效a標簽中的鏈接。該部分核心代碼如下:
//獲取所有的href值
Elements aElements = this.doc.getElementsByTag(“a”);
//用來存放該頁面中的連接
List<String> linkList = new ArrayList<String>();
for (Element aElement : aElements) {
//判斷該href中是否有值,是否為”/”或者”//”,這些都是不需要的
if(aElement.attr(“href”).trim().length()>0 &&!aElement.attr(“href”).trim().equals(“/”) &&! aElement.attr(“href”).trim().equals(“//”)){
if (!aElement.attr(“href”).trim().equals(url)){
//查看是否鏈接中包含了http或者https,沒有給鏈接加上
if(!(aElement.attr(“href”).toString().startsWith(“http:”)||
aElement.attr(“href”).toString().startsWith(“https:”))){
linkList.add(“http:”+aElement.attr(“href”));
}else{
linkList.add(aElement.attr(“href”));
}
(4)深度爬取模塊的設計與實現。前面所設計的模塊完成了靜態網頁簡單爬取和解析,所爬取的內容局限于單個頁面,因此需要進行深度爬取才能獲取更多資源。深度爬取是通過解析起點網頁中所有a標簽的URL鏈接,將這些URL鏈接再次加入爬取隊列并解析,如此重復循環,實現擴散性爬取。擴散性爬蟲示意如圖4所示。系統使用界面如圖5所示。

圖4 擴散性爬蟲示意圖

圖5 系統使用結果界面
本文在使用多線程執行高強度爬取時會產生大量重復或無用的連接,因此需要對重復的連接去重以及使用cache存儲篩選出有用的連接,從而大大減少無用的工作和程序負載,提高程序運行的效率。緩存結構流程如圖6所示。

圖6 緩存結構流程圖
2.2.2 動態頁面爬蟲設計及實現
動態網頁爬取往往是實現某個特定功能的爬取,不同的主題有不同的設計策略,因此沒有一勞永逸的動態網頁爬蟲。動態網頁不同于靜態網頁的資源可以直接完整爬取,動態網頁通常需要用戶進行特定操作后才會進一步加載展示,這種結構往往是用JSP、ASP、PHP等語言編寫的服務器端代碼。動態網頁技術極大的增強了網頁的多元性與新穎性,可以幫助網站吸引更多用戶的注意[10]。對于動態網頁的爬取主要是通過分析其搜索特性和報文交換規律,掌握服務器與用戶之間是如何進行數據傳遞,最終利用爬蟲技術將所需資源爬取下來。下面將以百度圖片、百度文庫為例設計實現動態網頁爬蟲。
(1)百度圖片按關鍵字爬取模塊的設計與實現。百度圖片是百度針對用戶需求,從8億中文網頁中提取各類圖片而建立的中文圖片庫。設計爬蟲程序,首先通過觀察百度圖片的數據加載過程可以得知,當用戶輸入關鍵字發起請求后,服務器通過json數據包返回所需圖片資源信息,其中每項json數據中的middelURL項即為圖片資源真正的url。在得知服務器如何向用戶傳送數據后還需要知道用戶如何將請求準確的發送到服務器。因此分析百度圖片request請求的URL參數規律。通過對比分析得知幾個關鍵參數的含義,其中queryWord和word參數為用戶所輸入關鍵字的中文編碼格式。百度圖片數據加載過程如圖7所示。

圖7 百度圖片數據加載過程
通過上述分析后,設計爬蟲程序爬取百度圖片。首先對URL的queryWord和word以及pn、rn、gsm三個參數賦予相應的值并向服務器發出請求,獲取服務器響應內容后,從響應內容中解析JSON數據獲取其中的middelURL地址,再對該middelURL地址發送請求即可將圖片資源批量爬取下來(系統使用結果如圖8所示)。詳細步驟如下:

圖8 系統使用結果界面
①對請求目標URL設置對應的queryWord和word以及pn、rn、gsm參數,并向服務器發送請求。該部分核心代碼如下:
//設置參數值
String url = String.format(ROOTURL, keyWord,word, pn);
HttpGet httpGet = new HttpGet(url);
②對響應回來的json數據進行解析取出data數據,再對data數據中middelURL進行篩選。該部分核心代碼如下:
//獲得響應
res = EntityUtils.toString(response.getEntity());
//通過jsonobject來將CloseableHttpResponse轉換成json對象
JSONObject jsonobject = JSONObject.fromObject(res);
//獲取其中的data數據
String data = jsonobject.get(“data”).toString();
//使用正則表達式將middleURL給取出來
Pattern pattern = Pattern.compile(“middleURL”:”.*?””);
③獲得middelURL后發起資源請求,完成對目標圖片資源的爬取。該部分核心代碼如下:
HttpGet request = new HttpGet(mURL);
//獲取服務器響應,得到圖片資源;
CloseableHttpResponse response = httpClient.execute(request);
④如此循環,開啟多線程批量爬取圖片資源。
(2)百度文庫文檔爬取模塊的設計與實現。百度文庫是百度發布供用戶在線文檔分享的平臺,文檔資源由百度用戶上傳,同時用戶也可以在線閱讀和下載這些文檔。百度文庫包括教學資料、考試題庫、專業資料、公文寫作、法律文件等多個領域。同樣百度文庫也是動態網頁,分析其資源搜索特性和交換規律,設計爬蟲程序(爬取流程如圖9所示)。詳細步驟如下:

圖9 百度文庫爬取流程圖
①對服務器發起第一次資源請求。其中第一次資源請求中包含url、type、t、sign四個參數,url參數即為目標資源的url地址,type為文檔類型即為doc。獲得服務器響應后得到第一次資源請求所返回的json數據(如圖10所示)。其中的s參數為所需文檔資源的真實URL地址。

圖10 第一次url返回的json數據
②得到文檔資源的url等參數后,將文檔資源的url、type、t、sign、f和h等參數重新拼接成新的url地址,向服務器發起第二次請求。
③獲得第二次請求的服務器響應,得到所需資源的json數據列表后(如圖11所示)通過使用Java的第三方jar包doc4j將其整合成所需的doc文檔并寫出成為文件,便完成對該文章的爬取(系統使用結果界面如圖12所示)[11]。

圖11 返回的json數據列表

圖12 系統使用結果界面
本文介紹了靜態網頁爬蟲和動態網頁爬蟲的設計與實現,對爬蟲原理進行了簡要分析,并著重對爬蟲的實現過程進行了講解。如今網絡資源數量巨大,掌握爬蟲技術能夠很好的幫助人們快速檢索信息資源,簡化獲取資源的過程。本文從實際出發對靜態和動態網頁爬蟲進行分析設計,完成了對靜態網頁的泛用性爬取,并結合特定實例(百度圖片、百度文庫)實現了動態網頁的特殊性爬取,從而快速高效的獲取資源信息,達到了預期效果。