姜夢(mèng)稚
目前,對(duì)于全球大多數(shù)互聯(lián)網(wǎng)用戶(hù)來(lái)說(shuō),搜索引擎是其準(zhǔn)確獲得所需要信息或者知識(shí)的最有效的工具。但是對(duì)于所有的搜索引擎來(lái)說(shuō),最重要的性能指標(biāo)有兩個(gè):查全率和查準(zhǔn)率。查全率與搜索引擎搜集的網(wǎng)頁(yè)數(shù)量和質(zhì)量有關(guān)。
本文介紹的是用于搜集網(wǎng)頁(yè), 提高查全率的最重要的工具—網(wǎng)絡(luò)爬蟲(chóng)(Web Crawler)的設(shè)計(jì)與實(shí)現(xiàn)。網(wǎng)絡(luò)爬蟲(chóng)的主要作用是搜集互聯(lián)網(wǎng)的網(wǎng)頁(yè),也可以用它來(lái)定期搜集某個(gè)網(wǎng)站的內(nèi)容,跟蹤判斷網(wǎng)站的發(fā)展,或者做站內(nèi)搜索引擎。從網(wǎng)絡(luò)爬蟲(chóng)的工作原理來(lái)看,“網(wǎng)絡(luò)爬蟲(chóng)”是一個(gè)比較形象的名字,它是在互聯(lián)網(wǎng)內(nèi),通過(guò)網(wǎng)頁(yè)鏈接,從當(dāng)前網(wǎng)頁(yè)爬到下一個(gè)網(wǎng)頁(yè)來(lái)進(jìn)行網(wǎng)頁(yè)內(nèi)容搜集的工具。它所需完成的工作[1]如下:(1)在一個(gè)網(wǎng)頁(yè)上,獲取網(wǎng)頁(yè)的標(biāo)題和網(wǎng)頁(yè)中的摘要;(2)將搜集到的網(wǎng)頁(yè)標(biāo)題,鏈接,網(wǎng)頁(yè)的摘要放入數(shù)據(jù)庫(kù)中;(3)根據(jù)當(dāng)前網(wǎng)頁(yè)的內(nèi)容,搜集網(wǎng)頁(yè)中的鏈接信息,并根據(jù)鏈接順序搜索相應(yīng)鏈接網(wǎng)頁(yè)的內(nèi)容。
不同的Web Crawler,在設(shè)計(jì)的時(shí)候側(cè)重各有不同,本文所介紹的Web Crawler在設(shè)計(jì)的時(shí)候主要考慮解決以下幾個(gè)問(wèn)題:(1)Web Crawler遍歷網(wǎng)頁(yè)中的所有鏈接,并且能對(duì)所搜索的網(wǎng)頁(yè)進(jìn)行搜索深度的限制;(2)Web Crawler能夠提取出網(wǎng)頁(yè)中的摘要和標(biāo)題信息,并且保存到數(shù)據(jù)庫(kù)中;(3)要求能夠?qū)σ延械乃阉饕娴乃阉鹘Y(jié)果再優(yōu)化,提高所設(shè)計(jì)的Crawler的擴(kuò)展能力;(4)要求能夠采用多線程的方式,提高搜索的效率。
針對(duì)前述的四個(gè)目標(biāo),在設(shè)計(jì)Crawler的時(shí)候,具體考慮了如下一些問(wèn)題。首先Crawler的搜索深度的問(wèn)題,網(wǎng)頁(yè)中的鏈接關(guān)系是相當(dāng)復(fù)雜的,一組網(wǎng)頁(yè)之間可以互相包含,網(wǎng)頁(yè)A中的鏈接可以指向網(wǎng)頁(yè)B,同時(shí)網(wǎng)頁(yè)B中的鏈接也可以指向網(wǎng)頁(yè)A。因此,網(wǎng)頁(yè)的搜索深度必須作一定的限制,不能無(wú)限制的遞歸搜索;其次網(wǎng)頁(yè)的搜索方式也有差別,有些爬蟲(chóng)采用廣度優(yōu)先策略 (BFS),有些采用了深度優(yōu)先策略(DFS)[2],考慮到實(shí)現(xiàn)時(shí)采用多線程,且所設(shè)計(jì)的 Crawler需在搜索的深度上作了限制,所以采用深度搜索的方式。對(duì)于搜集所有url鏈接,可以有不同的方式,本文采用的正則表達(dá)式。盡管具體的實(shí)現(xiàn)有多種方式,搜集鏈接也可以采用后面所提到的HtmlParser包,但是采用正則表達(dá)式,是一種方便,快捷的手段,Java語(yǔ)言也提供對(duì)正則表達(dá)式的支持。
多線程程序是編程中比較復(fù)雜的問(wèn)題,除了對(duì)線程的調(diào)試比較困難外,對(duì)線程所使用的資源的控制也同樣復(fù)雜。在Java平臺(tái)下對(duì)多線程的編程有充分支持,因此在設(shè)計(jì)Crawler的多線程實(shí)現(xiàn)時(shí),采用了Synchronous關(guān)鍵字,原子數(shù)(AtomicInteger)和線程池[3],通過(guò)使用線程池,在應(yīng)用啟動(dòng)的時(shí)候,設(shè)置所需創(chuàng)建的線程數(shù)量,一旦線程池為空,則掛起當(dāng)前請(qǐng)求,等待空閑的線程出現(xiàn);原子數(shù)則可以保證程序中的計(jì)數(shù)以互斥的方式操作,保證了遞增和遞減操作的原子性;而Synchronous保證了不同線程在訪問(wèn)數(shù)據(jù)的時(shí)候不會(huì)出現(xiàn)兩個(gè)線程同時(shí)訪問(wèn)一個(gè)數(shù)據(jù)。
本文中實(shí)現(xiàn)的Crawler中第三個(gè)考慮的問(wèn)題就是獲取網(wǎng)頁(yè)的內(nèi)容、提取網(wǎng)頁(yè)摘要信息和標(biāo)題信息。網(wǎng)頁(yè)內(nèi)容的獲取方式有多種,比較常用的就是想網(wǎng)頁(yè)發(fā)出一個(gè) Http請(qǐng)求,并獲取返回的字符流??紤]到實(shí)現(xiàn)這種請(qǐng)求/響應(yīng)方式的復(fù)雜性,本文采用了HtmlParser[4]包來(lái)具體實(shí)現(xiàn)網(wǎng)頁(yè)內(nèi)容的獲取。對(duì)于標(biāo)簽的獲取采用兩種方式,一種是采用HtmlParser包來(lái)獲取,另外一種提取文本的方式也可以使用正則表達(dá)式[5],在構(gòu)造合適的正則表達(dá)式時(shí),需要考慮到標(biāo)簽的特殊結(jié)構(gòu),為了提高文字的抽取效率,可以對(duì)一段 html源碼首先過(guò)濾掉一些不需要的標(biāo)簽。采用HtmlParser包除了能夠獲得網(wǎng)頁(yè)的內(nèi)容外,該包還能提供一系列的獲取網(wǎng)頁(yè)內(nèi)容的工具類(lèi),獲取特定標(biāo)簽,并通過(guò)標(biāo)簽篩選規(guī)則的運(yùn)用,獲取包含有文字的標(biāo)簽,提取出其中的文字作為摘要;對(duì)于標(biāo)題通過(guò)獲取
本節(jié)介紹Web Crawler的設(shè)計(jì),包括類(lèi)的設(shè)計(jì)和時(shí)序圖的設(shè)計(jì)。本文所實(shí)現(xiàn)的Web Crawler采用MVC的設(shè)計(jì)方式,前臺(tái)設(shè)計(jì)成Web頁(yè)面,發(fā)送Web請(qǐng)求;后臺(tái)Servlet接受前臺(tái)發(fā)送過(guò)來(lái)的請(qǐng)求。在后臺(tái),有如下幾部分的內(nèi)容:描述數(shù)據(jù)實(shí)現(xiàn)的ICrawlerModel接口及其實(shí)現(xiàn);表示多線程搜索的ICrawler接口、AbstractCrawler和 MultipleThreadCrawler類(lèi);工具類(lèi)IParser接口和HtmlParser類(lèi);表示鏈接的數(shù)據(jù)結(jié)構(gòu)Link和LinkDepth類(lèi);以及存儲(chǔ)結(jié)果的DbAccess類(lèi)。上述類(lèi)的實(shí)現(xiàn)都是采用Java,數(shù)據(jù)庫(kù)使用MySQL,除此以外還要用Tomcat Web服務(wù)器,如圖1所示。
前臺(tái)的設(shè)計(jì)使用 jsp+jQuery的方式,jQuery是一種javascript工具包,提供對(duì)ajax的支持,能夠?qū)崿F(xiàn)無(wú)刷新的頁(yè)面布局。Ajax是目前一種流行的設(shè)計(jì)網(wǎng)頁(yè)的方式。后臺(tái)采用Servlet運(yùn)行方式,獲取前臺(tái)通過(guò)ajax方式提交的參數(shù),根據(jù)不同的選擇(可能是關(guān)鍵詞的搜索,也可能是某一個(gè)網(wǎng)址的搜索)進(jìn)行處理。
圖1 Web Crawler的類(lèi)圖[6]
MultipleThreadCrawler實(shí)現(xiàn)了抽象類(lèi)AbstractCrawler,它是Crawler實(shí)現(xiàn)的核心內(nèi)容,主要對(duì)每一次的搜索數(shù)據(jù)的處理,多線程的協(xié)調(diào)等。在該類(lèi)中實(shí)現(xiàn)兩個(gè)私有類(lèi)Worm和TextExtractor,前者實(shí)現(xiàn)對(duì)網(wǎng)頁(yè)鏈接的搜索,并填充入Model中的toVisitUrls數(shù)據(jù)結(jié)構(gòu)中,后者則是對(duì)當(dāng)前搜索的網(wǎng)頁(yè)提取標(biāo)題和摘要信息。
MaxDepthModel是一個(gè)存儲(chǔ)數(shù)據(jù)的類(lèi),其包括已經(jīng)訪問(wèn)過(guò)的Url和未訪問(wèn)過(guò)的Url,并且提供向數(shù)據(jù)結(jié)構(gòu)中填充未訪問(wèn)的Url。采用接口的方式,也是為了能夠在今后擴(kuò)展具體實(shí)現(xiàn)。
HtmlParser提供了對(duì)文本解析的方法,圖2給出的用戶(hù)提交待搜索的網(wǎng)站的時(shí)序圖:
圖2 時(shí)序圖[6]
本文設(shè)計(jì)的Web Crawler采用Java語(yǔ)言和MySql來(lái)實(shí)現(xiàn),作為開(kāi)源工具的組合,被應(yīng)用在很多重要的領(lǐng)域。除了前述提到的實(shí)現(xiàn)細(xì)節(jié)外,在獲取html文本中的url鏈接時(shí)使用了正則表達(dá)式,可以提高文字的匹配速率和程序的運(yùn)行效率(采用Java包匹配往往比較復(fù)雜)。同時(shí)在抽取html文本的文字信息時(shí),也使用了這種技術(shù),由于正則表達(dá)式表述的靈活性,并沒(méi)有一種能夠適合所有情況的匹配表達(dá)式,只有在實(shí)踐過(guò)程中不斷改進(jìn)和優(yōu)化。
本文總結(jié)了Web Crawler在設(shè)計(jì)和實(shí)現(xiàn)過(guò)程中遇到的問(wèn)題,并結(jié)合軟件設(shè)計(jì)模式,給出一種Web Crawler可行的軟件結(jié)構(gòu),并實(shí)現(xiàn)檢索,存儲(chǔ),顯示等一系列問(wèn)題,所給出的解決方法有一定的通用性,軟件的框架能夠根據(jù)實(shí)際的需要進(jìn)行改寫(xiě),可以在對(duì)當(dāng)前已有得搜索引擎的搜索結(jié)果進(jìn)行優(yōu)化。Web Crawler是一個(gè)需要不斷優(yōu)化和改進(jìn)的工具,其設(shè)計(jì)和實(shí)現(xiàn)可以采用多種方式,也可以根據(jù)Crawler的實(shí)際需要來(lái)設(shè)計(jì)。
[1]宋暉,張嶺,葉允明.基于標(biāo)記樹(shù)對(duì)象抽取技術(shù)的 Hidden Web獲取研究[J].計(jì)算機(jī)工程與應(yīng)用,2002(23).
[2]赫楓齡.用有向圖法解決網(wǎng)頁(yè)爬行中循環(huán)鏈接問(wèn)題[J].吉林大學(xué)學(xué)報(bào),2004.3.
[3]陳昊鵬,饒若楠.Java編程思想,第3版[M].機(jī)械工業(yè)出版社,2005.5.
[4]Derrick Oswald等 HtmlParser參考文檔,http://htmlparser.sourceforge.net. [OL].
[5]史壽偉.正則表達(dá)式參考文檔,http://www.regexlab.com/zh/regref.htm [OL].
[6]Martin Fowler UML精粹:標(biāo)準(zhǔn)對(duì)象建模語(yǔ)言簡(jiǎn)明指南[M].2006.3.