李尚林,陳宮,雷勇
(桂林理工大學(xué) 信息科學(xué)與工程學(xué)院,廣西 桂林 541004)
如今信息化時(shí)代學(xué)習(xí)方式早已不同以前,利用網(wǎng)絡(luò)開展學(xué)習(xí)必不可少。網(wǎng)絡(luò)資源不同于傳統(tǒng)學(xué)習(xí)資源,具有實(shí)時(shí)性高、數(shù)量大、內(nèi)容豐富、傳播速度快、范圍廣、易共享等特點(diǎn)。而且通過(guò)網(wǎng)絡(luò)學(xué)習(xí)不受限于環(huán)境因素,這種隨時(shí)隨地獲取學(xué)習(xí)資源的形式特別迎合快節(jié)奏人群的學(xué)習(xí)需求[1]。但隨著互聯(lián)網(wǎng)技術(shù)的日益發(fā)展,網(wǎng)絡(luò)中信息量呈爆炸式增長(zhǎng),在搜索學(xué)習(xí)資源時(shí)盡管搜素引擎會(huì)幫助用戶抓取一部分網(wǎng)絡(luò)信息,但對(duì)于信息需求的不同往往還需要花費(fèi)大量時(shí)間進(jìn)行過(guò)濾篩選,顯然這樣的方式既費(fèi)時(shí)又費(fèi)力。爬蟲技術(shù)的誕生有效的解決了這個(gè)問(wèn)題。由于爬蟲的種種優(yōu)點(diǎn),其也成為現(xiàn)代人檢索網(wǎng)絡(luò)信息的重要工具。
如何使用爬蟲技術(shù)讓程序高效、自動(dòng)化的獲取資源信息,簡(jiǎn)化獲取學(xué)習(xí)資源的過(guò)程,成為人們?cè)谑褂门老x時(shí)所面臨的主要問(wèn)題。因此本文探究了靜態(tài)網(wǎng)頁(yè)爬蟲和動(dòng)態(tài)網(wǎng)頁(yè)爬蟲的設(shè)計(jì)過(guò)程,實(shí)現(xiàn)了靜態(tài)網(wǎng)頁(yè)的多線程泛用性爬蟲,并以百度圖片、百度文庫(kù)為例設(shè)計(jì)實(shí)現(xiàn)了動(dòng)態(tài)網(wǎng)頁(yè)的多線程特殊性爬蟲,完成對(duì)所需資源的高效爬取。
網(wǎng)絡(luò)爬蟲,又稱為網(wǎng)絡(luò)機(jī)器人或者網(wǎng)頁(yè)蜘蛛,是一種按照自身需求和一定規(guī)則自動(dòng)抓取萬(wàn)維網(wǎng)信息的腳本或程序。如今使用爬蟲檢索信息和資源已成為人們?cè)L問(wèn)萬(wàn)維網(wǎng)的入口和指南。
按照系統(tǒng)結(jié)構(gòu)和實(shí)現(xiàn)技術(shù)的不同,爬蟲可大致分為四類[2]:
(1)通用網(wǎng)絡(luò)爬蟲。其爬取對(duì)象可從一個(gè)種子延伸到整個(gè)Web。這類爬蟲爬取范圍廣、數(shù)量大,因此對(duì)爬取速度和存儲(chǔ)空間要求較高。典型應(yīng)用為百度、谷歌搜索等大型搜索引擎。
(2)聚焦網(wǎng)絡(luò)爬蟲。聚焦網(wǎng)絡(luò)爬蟲又稱為主題爬蟲,它是按照預(yù)先定義好的主題進(jìn)行相關(guān)頁(yè)面爬取的網(wǎng)絡(luò)爬蟲。由于只爬取與主題相關(guān)的頁(yè)面,聚焦網(wǎng)絡(luò)爬蟲可大大節(jié)省硬件和網(wǎng)絡(luò)資源。
(3)增量式網(wǎng)絡(luò)爬蟲。增量式網(wǎng)絡(luò)爬蟲指不爬取重復(fù)部分,只對(duì)已經(jīng)獲取網(wǎng)頁(yè)的新增部分進(jìn)行爬取。其采取增量式更新的爬取方法,可有效減少爬取次數(shù)。
(4)深層網(wǎng)絡(luò)爬蟲。Web頁(yè)面按照存在方式可分為表層網(wǎng)頁(yè)和深層網(wǎng)頁(yè)。表層網(wǎng)頁(yè)是指搜索引擎可以直接索引,通過(guò)超鏈接即可進(jìn)入到靜態(tài)網(wǎng)頁(yè)主體;深層網(wǎng)頁(yè)是指需要用戶提交關(guān)鍵詞或表單數(shù)據(jù)才能訪問(wèn)到的網(wǎng)頁(yè)。因此深層網(wǎng)絡(luò)爬蟲需要分析頁(yè)面結(jié)構(gòu),將預(yù)先準(zhǔn)備的數(shù)據(jù)自動(dòng)填寫到表單中并提交至服務(wù)器驗(yàn)證處理,最終才能獲得相應(yīng)的資源信息。
實(shí)際中的網(wǎng)絡(luò)爬蟲應(yīng)用往往是幾種網(wǎng)絡(luò)爬蟲技術(shù)相結(jié)合實(shí)現(xiàn)的。
HTTP是面向事務(wù)的應(yīng)用層協(xié)議,它是萬(wàn)維網(wǎng)上能夠可靠交換文件(包括文本、聲音、圖像等各種多媒體文件)的重要基礎(chǔ)[3]。在Java網(wǎng)絡(luò)編程中會(huì)經(jīng)常遇到HTTP協(xié)議編程,雖然JDK提供了HttpURLConnection編程接口對(duì)HTTP協(xié)議進(jìn)行支持,但由于協(xié)議應(yīng)用本身的復(fù)雜性,使得在大量實(shí)際項(xiàng)目中單純使用JDK進(jìn)行HTTP編程仍然比較困難[4]。HttpClient 是Java所支持的 Apache Jakarta Common 下的子項(xiàng)目,用來(lái)提供和支持 HTTP 協(xié)議的客戶端編程,不僅如此它還支持 HTTP 協(xié)議最新版本和規(guī)范。HttpClient的出現(xiàn)大大降低了HTTP網(wǎng)絡(luò)編程的復(fù)雜度,使其也成為了Java網(wǎng)絡(luò)爬蟲的核心技術(shù)之一。
Jsoup是一款HTML解析器,可直接解析某個(gè)URL地址、HTML文本內(nèi)容。它提供了一套非常省力的API,可通過(guò)DOM、CSS以及類似于jQuery的操作方法來(lái)取出和操作數(shù)據(jù),只要解析的內(nèi)容符合規(guī)定就能返回一個(gè)簡(jiǎn)潔的頁(yè)面詳情[5]。由于Jsoup良好的可擴(kuò)展性API 設(shè)計(jì),以及強(qiáng)大的HTML解析功能,其也廣泛的應(yīng)用于Java網(wǎng)絡(luò)爬蟲中。
計(jì)算機(jī)中正在執(zhí)行的程序被稱為“進(jìn)程”,相對(duì)于程序來(lái)說(shuō)進(jìn)程是一個(gè)動(dòng)態(tài)的概念,是程序的一次執(zhí)行過(guò)程。線程相比進(jìn)程來(lái)說(shuō)屬于更小的運(yùn)行單位,一個(gè)線程是指進(jìn)程中一個(gè)單一順序的控制流。一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每個(gè)線程并行執(zhí)行不同的任務(wù)。在如今信息高速化時(shí)代單線程的效率是非常低的,多線程技術(shù)可有效提高程序運(yùn)行效率[6]。
本文研究的爬蟲系統(tǒng)主要分為兩部分,靜態(tài)網(wǎng)頁(yè)的泛用性爬取和動(dòng)態(tài)網(wǎng)頁(yè)的特殊性爬取。靜態(tài)網(wǎng)面的泛用性爬取涉及的功能模塊有:設(shè)置參數(shù)(User-Agent和cookie)模塊、獲取目標(biāo)URL的HTML文本模塊、Jsoup解析模塊、深度爬取模塊。動(dòng)態(tài)網(wǎng)頁(yè)的特殊性爬取涉及的功能模塊有:百度圖片按關(guān)鍵字爬取模塊、百度文庫(kù)文檔爬取模塊。下面對(duì)這些功能模塊進(jìn)行詳細(xì)描述。
2.2.1 靜態(tài)頁(yè)面爬蟲設(shè)計(jì)及實(shí)現(xiàn)
靜態(tài)網(wǎng)頁(yè)爬取功能的核心是通過(guò)HttpClient對(duì)目標(biāo)靜態(tài)網(wǎng)頁(yè)發(fā)出請(qǐng)求并取得服務(wù)器響應(yīng),接著將獲得的響應(yīng)內(nèi)容(HTML文本)進(jìn)行解析,獲取所需資源的路徑,從而將資源爬取下來(lái)[7]。靜態(tài)網(wǎng)頁(yè)爬取流程如圖1所示:
圖1 靜態(tài)網(wǎng)頁(yè)爬取流程圖
(1)設(shè)置參數(shù)(User-Agent和cookie)模塊的設(shè)計(jì)與實(shí)現(xiàn)。在HTTP協(xié)議中,報(bào)文的請(qǐng)求頭部Header可用于服務(wù)器識(shí)別數(shù)據(jù)包來(lái)源(報(bào)文請(qǐng)求頭部結(jié)構(gòu)如圖2所示),其中頭部參數(shù) User-Agent用于服務(wù)器識(shí)別用戶身份,cookie中存儲(chǔ)用戶密碼等信息[8]。如今網(wǎng)站大都有反爬蟲措施,因此需要使用上述兩個(gè)參數(shù)模擬真實(shí)瀏覽器向服務(wù)器發(fā)送請(qǐng)求,防止被服務(wù)器攔截[9]。user-Agent和cookie設(shè)置界面如圖3所示。該部分核心代碼如下:
圖2 http協(xié)議請(qǐng)求報(bào)文結(jié)構(gòu)
圖3 user-Agent、cookie設(shè)置界面
//設(shè)置請(qǐng)求頭
httpGet.setHeader(“User-Agent”, 真實(shí)瀏覽器的User-Agent);
httpGet.setHeader(“cookie”, COOKIE);
(2)獲取目標(biāo)URL的HTML文本模塊的設(shè)計(jì)與實(shí)現(xiàn)。靜態(tài)網(wǎng)頁(yè)爬取的起點(diǎn)即為獲取目標(biāo)URL的HTML文本內(nèi)容。設(shè)置請(qǐng)求數(shù)據(jù)包頭部信息后使用HttpClient技術(shù)模擬真實(shí)瀏覽器向服務(wù)器發(fā)送請(qǐng)求,最終獲得服務(wù)器響應(yīng)得到所需的HTML文本信息,為下一步的內(nèi)容解析做出準(zhǔn)備。除此之外,還可以輸入多個(gè)URL進(jìn)行多線程爬取。該部分核心代碼如下:
//獲取客戶端工具
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
//創(chuàng)建get請(qǐng)求方法實(shí)例,并指定請(qǐng)求url
HttpGet httpGet = new HttpGet(url);
//發(fā)送請(qǐng)求,獲取響應(yīng)
response = httpClient.execute(httpGet);
(3)Jsoup解析模塊的設(shè)計(jì)與實(shí)現(xiàn)。獲取的HTML文本中往往只有某些特定元素是所需要的。因此利用Jsoup對(duì)HTML文本進(jìn)行解析過(guò)濾,將所需資源從HTML文本中提取出來(lái)。通過(guò)觀察所獲取的HTML文本發(fā)現(xiàn)本文所需要的資源在a標(biāo)簽中,同時(shí)后續(xù)還需要進(jìn)行深度爬取,因此利用Jsoup過(guò)濾出所有有效a標(biāo)簽中的鏈接。該部分核心代碼如下:
//獲取所有的href值
Elements aElements = this.doc.getElementsByTag(“a”);
//用來(lái)存放該頁(yè)面中的連接
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或者h(yuǎn)ttps,沒(méi)有給鏈接加上
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)深度爬取模塊的設(shè)計(jì)與實(shí)現(xiàn)。前面所設(shè)計(jì)的模塊完成了靜態(tài)網(wǎng)頁(yè)簡(jiǎn)單爬取和解析,所爬取的內(nèi)容局限于單個(gè)頁(yè)面,因此需要進(jìn)行深度爬取才能獲取更多資源。深度爬取是通過(guò)解析起點(diǎn)網(wǎng)頁(yè)中所有a標(biāo)簽的URL鏈接,將這些URL鏈接再次加入爬取隊(duì)列并解析,如此重復(fù)循環(huán),實(shí)現(xiàn)擴(kuò)散性爬取。擴(kuò)散性爬蟲示意如圖4所示。系統(tǒng)使用界面如圖5所示。
圖4 擴(kuò)散性爬蟲示意圖
圖5 系統(tǒng)使用結(jié)果界面
本文在使用多線程執(zhí)行高強(qiáng)度爬取時(shí)會(huì)產(chǎn)生大量重復(fù)或無(wú)用的連接,因此需要對(duì)重復(fù)的連接去重以及使用cache存儲(chǔ)篩選出有用的連接,從而大大減少無(wú)用的工作和程序負(fù)載,提高程序運(yùn)行的效率。緩存結(jié)構(gòu)流程如圖6所示。
圖6 緩存結(jié)構(gòu)流程圖
2.2.2 動(dòng)態(tài)頁(yè)面爬蟲設(shè)計(jì)及實(shí)現(xiàn)
動(dòng)態(tài)網(wǎng)頁(yè)爬取往往是實(shí)現(xiàn)某個(gè)特定功能的爬取,不同的主題有不同的設(shè)計(jì)策略,因此沒(méi)有一勞永逸的動(dòng)態(tài)網(wǎng)頁(yè)爬蟲。動(dòng)態(tài)網(wǎng)頁(yè)不同于靜態(tài)網(wǎng)頁(yè)的資源可以直接完整爬取,動(dòng)態(tài)網(wǎng)頁(yè)通常需要用戶進(jìn)行特定操作后才會(huì)進(jìn)一步加載展示,這種結(jié)構(gòu)往往是用JSP、ASP、PHP等語(yǔ)言編寫的服務(wù)器端代碼。動(dòng)態(tài)網(wǎng)頁(yè)技術(shù)極大的增強(qiáng)了網(wǎng)頁(yè)的多元性與新穎性,可以幫助網(wǎng)站吸引更多用戶的注意[10]。對(duì)于動(dòng)態(tài)網(wǎng)頁(yè)的爬取主要是通過(guò)分析其搜索特性和報(bào)文交換規(guī)律,掌握服務(wù)器與用戶之間是如何進(jìn)行數(shù)據(jù)傳遞,最終利用爬蟲技術(shù)將所需資源爬取下來(lái)。下面將以百度圖片、百度文庫(kù)為例設(shè)計(jì)實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)頁(yè)爬蟲。
(1)百度圖片按關(guān)鍵字爬取模塊的設(shè)計(jì)與實(shí)現(xiàn)。百度圖片是百度針對(duì)用戶需求,從8億中文網(wǎng)頁(yè)中提取各類圖片而建立的中文圖片庫(kù)。設(shè)計(jì)爬蟲程序,首先通過(guò)觀察百度圖片的數(shù)據(jù)加載過(guò)程可以得知,當(dāng)用戶輸入關(guān)鍵字發(fā)起請(qǐng)求后,服務(wù)器通過(guò)json數(shù)據(jù)包返回所需圖片資源信息,其中每項(xiàng)json數(shù)據(jù)中的middelURL項(xiàng)即為圖片資源真正的url。在得知服務(wù)器如何向用戶傳送數(shù)據(jù)后還需要知道用戶如何將請(qǐng)求準(zhǔn)確的發(fā)送到服務(wù)器。因此分析百度圖片request請(qǐng)求的URL參數(shù)規(guī)律。通過(guò)對(duì)比分析得知幾個(gè)關(guān)鍵參數(shù)的含義,其中queryWord和word參數(shù)為用戶所輸入關(guān)鍵字的中文編碼格式。百度圖片數(shù)據(jù)加載過(guò)程如圖7所示。
圖7 百度圖片數(shù)據(jù)加載過(guò)程
通過(guò)上述分析后,設(shè)計(jì)爬蟲程序爬取百度圖片。首先對(duì)URL的queryWord和word以及pn、rn、gsm三個(gè)參數(shù)賦予相應(yīng)的值并向服務(wù)器發(fā)出請(qǐng)求,獲取服務(wù)器響應(yīng)內(nèi)容后,從響應(yīng)內(nèi)容中解析JSON數(shù)據(jù)獲取其中的middelURL地址,再對(duì)該middelURL地址發(fā)送請(qǐng)求即可將圖片資源批量爬取下來(lái)(系統(tǒng)使用結(jié)果如圖8所示)。詳細(xì)步驟如下:
圖8 系統(tǒng)使用結(jié)果界面
①對(duì)請(qǐng)求目標(biāo)URL設(shè)置對(duì)應(yīng)的queryWord和word以及pn、rn、gsm參數(shù),并向服務(wù)器發(fā)送請(qǐng)求。該部分核心代碼如下:
//設(shè)置參數(shù)值
String url = String.format(ROOTURL, keyWord,word, pn);
HttpGet httpGet = new HttpGet(url);
②對(duì)響應(yīng)回來(lái)的json數(shù)據(jù)進(jìn)行解析取出data數(shù)據(jù),再對(duì)data數(shù)據(jù)中middelURL進(jìn)行篩選。該部分核心代碼如下:
//獲得響應(yīng)
res = EntityUtils.toString(response.getEntity());
//通過(guò)jsonobject來(lái)將CloseableHttpResponse轉(zhuǎn)換成json對(duì)象
JSONObject jsonobject = JSONObject.fromObject(res);
//獲取其中的data數(shù)據(jù)
String data = jsonobject.get(“data”).toString();
//使用正則表達(dá)式將middleURL給取出來(lái)
Pattern pattern = Pattern.compile(“middleURL”:”.*?””);
③獲得middelURL后發(fā)起資源請(qǐng)求,完成對(duì)目標(biāo)圖片資源的爬取。該部分核心代碼如下:
HttpGet request = new HttpGet(mURL);
//獲取服務(wù)器響應(yīng),得到圖片資源;
CloseableHttpResponse response = httpClient.execute(request);
④如此循環(huán),開啟多線程批量爬取圖片資源。
(2)百度文庫(kù)文檔爬取模塊的設(shè)計(jì)與實(shí)現(xiàn)。百度文庫(kù)是百度發(fā)布供用戶在線文檔分享的平臺(tái),文檔資源由百度用戶上傳,同時(shí)用戶也可以在線閱讀和下載這些文檔。百度文庫(kù)包括教學(xué)資料、考試題庫(kù)、專業(yè)資料、公文寫作、法律文件等多個(gè)領(lǐng)域。同樣百度文庫(kù)也是動(dòng)態(tài)網(wǎng)頁(yè),分析其資源搜索特性和交換規(guī)律,設(shè)計(jì)爬蟲程序(爬取流程如圖9所示)。詳細(xì)步驟如下:
圖9 百度文庫(kù)爬取流程圖
①對(duì)服務(wù)器發(fā)起第一次資源請(qǐng)求。其中第一次資源請(qǐng)求中包含url、type、t、sign四個(gè)參數(shù),url參數(shù)即為目標(biāo)資源的url地址,type為文檔類型即為doc。獲得服務(wù)器響應(yīng)后得到第一次資源請(qǐng)求所返回的json數(shù)據(jù)(如圖10所示)。其中的s參數(shù)為所需文檔資源的真實(shí)URL地址。
圖10 第一次url返回的json數(shù)據(jù)
②得到文檔資源的url等參數(shù)后,將文檔資源的url、type、t、sign、f和h等參數(shù)重新拼接成新的url地址,向服務(wù)器發(fā)起第二次請(qǐng)求。
③獲得第二次請(qǐng)求的服務(wù)器響應(yīng),得到所需資源的json數(shù)據(jù)列表后(如圖11所示)通過(guò)使用Java的第三方j(luò)ar包doc4j將其整合成所需的doc文檔并寫出成為文件,便完成對(duì)該文章的爬?。ㄏ到y(tǒng)使用結(jié)果界面如圖12所示)[11]。
圖11 返回的json數(shù)據(jù)列表
圖12 系統(tǒng)使用結(jié)果界面
本文介紹了靜態(tài)網(wǎng)頁(yè)爬蟲和動(dòng)態(tài)網(wǎng)頁(yè)爬蟲的設(shè)計(jì)與實(shí)現(xiàn),對(duì)爬蟲原理進(jìn)行了簡(jiǎn)要分析,并著重對(duì)爬蟲的實(shí)現(xiàn)過(guò)程進(jìn)行了講解。如今網(wǎng)絡(luò)資源數(shù)量巨大,掌握爬蟲技術(shù)能夠很好的幫助人們快速檢索信息資源,簡(jiǎn)化獲取資源的過(guò)程。本文從實(shí)際出發(fā)對(duì)靜態(tài)和動(dòng)態(tài)網(wǎng)頁(yè)爬蟲進(jìn)行分析設(shè)計(jì),完成了對(duì)靜態(tài)網(wǎng)頁(yè)的泛用性爬取,并結(jié)合特定實(shí)例(百度圖片、百度文庫(kù))實(shí)現(xiàn)了動(dòng)態(tài)網(wǎng)頁(yè)的特殊性爬取,從而快速高效的獲取資源信息,達(dá)到了預(yù)期效果。