利用Lucene與Nutch構建簡單的全文搜索引擎

文章地址

1.簡介

本次實現分爲兩個部分,第一個部分是利用Lucene構建一個全文的搜索引擎,另外一部分則是利用Nutch實現同樣的功能。由於Lucene並不是一個可以直接運行的程序,且不具備爬蟲和文檔處理的功能,因此在這一部分利用到了Heritrix和HTMLParser這兩個工具分別實現爬蟲與HTML文檔解析的功能。而使用Nutch的時候只需要一些簡單的配置和安裝就可以直接運行。最後還對這兩者進行了一個簡單的對比,說明其各自的特點和適應的範圍。

2.Lucene

2.1 爬蟲的設計

由於Lucene不具備爬蟲的功能,因此這裏使用到了Heritrix這樣的一個工具。我們利用Heritrix爬取一個指定網站(如:南京大學信息管理學院官方網站)的內容,爲了簡單起見這裏只爬去網頁格式的文檔,而“.doc/.xls/.ppt/.pdf”等格式的文件則直接忽略,因此這裏還需要對爬蟲進行一個簡單的設計。
這裏使用的Heritrix版本爲Heritrix-1.14.4,爬蟲的配置安裝過程如下:
(1)源代碼的導入
由於這裏需要一些自定義的功能,因此使用的是Heritrix的源代碼版本。爲方便開發,首先我們需要將其導入Myeclipse。在Myeclipse下建立一個工程名爲“MyHeritrix”。
導入類庫
這一步需要將 Heritrix-1.14.4-src 下的 lib 文件夾拷貝到 MyHeritrix 項目根目錄;方法如下:在 MyHeritrix 工程上右鍵單擊選擇“Build Path Configure Build Path …”,然後選擇 Library 選項卡,單擊“Add JARs …”,在彈出的“JAR Selection”對話框中選擇 MyHeritrix 工程 lib 文件夾下所有的 jar 文件,然後點擊 OK 按鈕。
拷貝源代碼
將 Heritrix-1.14.4-src\src\java 下的 com、org 和 st 三個文件夾拷貝進 MyHeritrix 工程的 src 下。這三個文件夾包含了運行 Heritrix 所必須的核心源代碼;將 Heritrix-1.14.4-src\src\resources\org\archive\util 下的文件 tlds-alpha-by-domain.txt 拷貝到 MyHeritrix\src\org\archive\util 中。該文件是一個頂級域名列表,在 Heritrix 啓動時會被讀取;將 Heritrix-1.14.4-src\src 下 conf 文件夾拷貝至 Heritrix 工程根目錄。它包含了 Heritrix 運行所需的配置文件;將 Heritrix-1.14.4-src\src 中的 webapps 文件夾拷貝至 Heritrix 工程根目錄。該文件夾是用來提供 servlet 引擎的,包含了 Heritrix 的 web UI 文件。
(2)源代碼的修改
由於我們需要一個功能:不爬取網頁中的.doc/.xls/.ppt/.pdf等格式的文件,而是自定義需要爬取的網頁文件,因此我們需要自行修改程序。這裏實現的方法爲擴展 FrontierScheduler 來抓取特定網站內容。
FrontierScheduler 是org.archive.crawler.postprocessor 包中的一個類,它的作用是將在 Extractor 中所分析得出的鏈接加入到 Frontier 中,以待繼續處理。在該類的 innerProcess(CrawlURI) 函數中,首先檢查當前鏈接隊列中是否有一些屬於高優先級的鏈接。如果有,則立刻轉走進行處理;如果沒有,則對所有的鏈接進行遍歷,然後調用 Frontier 中的 schedule() 方法加入隊列進行處理。這裏我們自行設計一個類FrontierSchedulerForDaven繼承FrontierScheduler類來修改程序的功能:

1
2
3
4
5
6
7
8
9
public class FrontierSchedulerForDaven extends FrontierScheduler
    然後在該類中重寫schedule函數:
protected void schedule(CandidateURI caURI)
String uri=caURI.toString();
if(uri.indexOf("dns:")!=-1){getController().getFrontier().schedule(caURI);}else
if(uri.indexOf(“nju.edu.cn")!=-1)||uri.indexOf(".html")!=-1||uri.indexOf(".php")!=-1
||uri.indexOf(".jsp")!=-1||uri.indexOf(".asp")!=-1||uri.indexOf(".shtml")!=-1))

通過這樣的一個布爾表達式實現就可以實現只爬取後綴名爲html、php、jsp、asp、shtml格式的文件,其他類型的文件一律忽略。
程序修改完成之後,在修改conf/modules/Processor.options文件,在該文件中添加如下一行:
1
(3)Heritrix啓動與爬蟲創建
在啓動之前,我們還需要對Heritrix進行一些簡單的配置,找到conf/Heritrix.properties文件,在文件當中將Heritrix.cmdline.admin(用戶名、密碼)和Heritrix.cmdline.port(端口)兩個參數修改爲如下形式:
2
上面的配置代表,Heritrix啓動時的端口爲9999,用戶名密碼分別爲admin和admin。
完成上面步驟之後,找到org.archive.crawler.Heritrix類,該類是Heritrix的主類入口,直接運行該類就可以啓動Heritrix。啓動成功之後,控制檯會輸出如下信息:
3
從上圖可以看出,Heritrix內置了一個Jetty服務器,在瀏覽其啓動的地址爲:127.0.0.1:9999。在地址欄直接輸入該地址就可以進入Heritrix的登錄界面。賬戶名和密碼爲上面所設置的admin。
接下來就可以直接進入爬蟲的創建過程,過程如下:在Job選項卡的create new job中選擇with default即進入爬蟲的創建界面:
4
上面所表單的第一行爲爬蟲的名稱,第二行爲對爬蟲的描述,下面的文本框中輸入爬蟲的入口地址,例如:http://im.nju.edu.cn/。
(4)爬蟲配置的修改與爬蟲運行
在上面一步的基礎上單擊”modules”按鈕,在相應的頁面爲此次任務設置各個處理模塊,一共有七項可配置的內容:
1)Select Crawl Scope:Crawl Scope 用於配置當前應該在什麼範圍內抓取網頁鏈接。例如選擇 BroadScope 則表示當前的抓取範圍不受限制,選擇 HostScope 則表示抓取的範圍在當前的 Host 範圍內。在這裏我們選擇 BroadScope。
2)Select URI Frontier:Frontier 是一個 URL 的處理器,它決定下一個被處理的 URL是什麼。同時,它還會將經由處理器鏈解析出來的 URL 加入到等待處理的隊列中去。
3)Select Pre Processors:這個隊列的處理器是用來對抓取時的一些先決條件進行判斷。比如判斷 robot.txt 信息等,它是整個處理器鏈的入口。
4)Select Fetchers:這個參數用於解析網絡傳輸協議,比如解析 DNS、HTTP 或 FTP 等。
5)Select Extractors:主要是用於解析當前服務器返回的內容,取出頁面中的 URL,等待下次繼續抓取。
6)Select Writers:它主要用於設定將所抓取到的信息以何種形式寫入磁盤。一種是採用壓縮的方式(Arc),還有一種是鏡像方式(Mirror)。這裏我們選擇簡單直觀的鏡像方式。
7)Select Post Processors:這個參數主要用於抓取解析過程結束後的掃尾工作,比如將 Extrator 解析出來的 URL 有條件地加入到待處理的隊列中去,這裏我們選擇之前自己定義的類:org.archive.crawler.postprocessor.FrontierSchedulerForDaven
上面的7個參數,除了特別指出來的除外,其他的幾個採用默認形式即可。
設置完“Modules”後,點擊“Settings”按鈕,這裏只需要設置 user-agent 和 from,其中:“@VERSION@”字符串需要被替換成 Heritrix 的版本信息。“PROJECT_URL_HERE”可以被替換成任何一個完整的 URL 地址。“from”屬性中不需要設置真實的 E-mail 地址,只要是格式正確的郵件地址就可以了。
當爬蟲配置完成之後,就可以運行爬蟲了,選擇console選項卡,點擊start就可以啓動爬蟲,爬蟲運行過程中的截圖如下:
5
爬蟲運行完成之後會在MyHeritrix/jobs文件夾下面生成一個Mirror文件夾,裏面存放了爬去的的文件內容。

2.2文檔的預處理

在實際的應用當中,網站內會有大量除了網頁文件之外的文件,例如ms office文件、pdf文件等等。而這些不同的文件需要不同的工具來進行處理,下面我總結了一些常見格式文檔與其對應的解析工具:
文檔類型 HTML Word Excel PPT RTF PDF
文檔解析器 HTMLParser POI POI POI POI PDFBox
下面對上面所列舉的工具進行一個簡單的介紹:
(1)HTMLParser
HTMLParser是一個專門用來解析HTML格式文檔的開源Java庫,毫不誇張地說,HTMLParser就是目前最好的HTML解析和分析的工具,它的特點是快速健壯。其主要的功能有:
文本信息抽取,例如對HTML進行有效信息搜索
鏈接提取,用於自動給頁面的鏈接文本加上鍊接的標籤
資源提取,例如對一些圖片、聲音的資源的處理
鏈接檢查,用於檢查HTML中的鏈接是否有效
頁面內容的監控
鏈接重寫,用於修改頁面中的所有超鏈接
網頁內容拷貝,用於將網頁內容保存到本地
內容檢驗,可以用來過濾網頁上一些令人不愉快的字詞
HTML信息清洗,把本來亂七八糟的HTML信息格式化
轉成XML格式數據
在項目實施過程中,我利用HTMLParser將一個HTML文檔解析成三行,第一行爲文檔所對應的URL地址,第二行爲文檔的標題,第三行爲文檔的摘要。
第一行:URL
this.page.setURL(getURL(this.filename));
第二行:title
this.page.setTITLE(visitor.getTitle());
第三行:summary
this.page.setCONTEXT(combineNodeText(visitor.getBody().toNodeArray()));
然後將所有解析出來的文檔存入另外一個文件夾。該文件夾中的文件可以直接用於索引的構建。
(2)POI
POI是Apache軟件基金會的開放源碼函式庫,POI提供API給Java程序對Microsoft Office格式文檔讀和寫的功能,它主要有五個模塊,其名稱和功能如下:
模塊 功能
HSSF 提供讀寫Microsoft Excel格式檔案的功能
XSSF 提供讀寫Microsoft Excel OOXML格式檔案的功能
HWPF 提供讀寫Microsoft Word格式檔案的功能
HSLF 提供讀寫Microsoft PowerPoint格式檔案的功能
HDGF 提供讀寫Microsoft Visio格式檔案的功能
由於這裏不做MS OFFICE文檔的解析,因此不再詳細解釋其用法。
(3)PDFBox
PDFBox同樣是Apache下的一個開源項目,專門用於PDF文檔的解析。它允許在項目中利用程序設計語言創建PDF文檔、操作多個PDF文檔以及從PDF文檔中解析出內容。
其主要的功能如下:從PDF文檔中解析出內容;合併和分割PDF文檔;PDF問的打印;將PDF文檔轉換爲照片格式;PDF文檔中表格的填充;PDF/A的驗證;PDF文檔的創建;與Lucene的整合開發。

2.3 索引的構建

索引的構建直接利用Lucene即可完成,索引中利用的分詞工具爲MMAnalyzer極易中文分詞組件,爲了得到更好的效果我們可以使用中科院的分詞ICTCLAS,但是需要我們自行編寫分析器Analyzer,這裏爲了簡單起見,利用了現成的中文分詞工具MMAnalyzer。
創建索引的時候,首先需要創建IndexWriter,然後再創建的document,最後創建field來存放不同部分的數據。在本實驗中總共創建了4個域(fields),第一個域名稱爲”id”,用於標識一個文件;第二個爲”url”,用於存放該文件所對應的URL;第三個域爲”title”,用於存放該文件的標題,這個標題也被用作在搜索結果中的標題;第四個域爲”context”,用於存放文本的內容,即搜索結果中的摘要部分。
具體的程序代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
IndexWriter writer=new IndexWriter(path,new MMAnalyzer());//MMAnalyzer極易中文分詞組件
reader=new BufferedReader(new FileReader(files[i]));
fields[0] =new Field("id",String.valueOf(i),Field.Store.YES,Field.Index.NO);
fields[1]= new Field("url",reader.readLine(),Field.Store.YES,Field.Index.NO);
fields[2]=new Field("title",reader.readLine(),Field.Store.YES,Field.Index.TOKENIZED);
fields[3]=new Field("context",getBodyFile(files[i].getAbsolutePath(),reader),Field.Store.YES,Field.Index.TOKENIZED);
//創建Document
Document doc=new Document();
//將五個field添加到doc中
for(int j=0;j<fields.length;j++)
{doc.add(fields[j]);}
//將Document添加到IndexWriter中
writer.addDocument(doc);
//優化和關閉
writer.optimize();
writer.close();
reader.close()

注意在創建完成field之後,通過document的add函數將field添加到document中,完成之後還要利用Indexwriter的addDocument函數將document添加到IndexWriter中。
最後一步不要忘了利用optimize和close函數來優化索引和關閉資源。
索引建立完成之後會在指定索引文件夾中生成四個文件:
_o.cfs/_w.cfs/segments.gen/segements_1f。
接下來就可以對這些索引文件進行檢索了。

2.4查詢實現

Lucene查詢實現的方法主要有兩種,第一種是Query,另外一種是Queryparser,兩者有明顯的差別。QueryParer類用來轉換用戶的檢索關鍵詞,其作用就是把用戶輸入的非格式化檢索詞轉換爲後臺檢索可以理解的Query對象。用Queryparser時需要一個參數Analyzer,如果要進行分詞處理的話就可能需要這個參數。
Query類是一個抽象類,需要使用Query的某個子類的構造函數來實例化並以此來表示與用戶檢索有關的內容。具體使用的時候,用戶可以通過Query的一系列子類,如TermQuery/BooleanQuery/RangeQuery/PrefixQuery/PhraseQuery/MultiPhraseQuery/FuzzyQuery/WildcardQuery/SpanQuery/Regexuery的構造函數來直接生成對應的Query對象。這些檢索方式各有其特點,通過命名大致可以猜測出它的功能,這裏不再做詳細的介紹。
爲了方便起見,實驗中用的檢索方式爲TermQuery,這個檢索方式只支持詞的檢索,因此需要在檢索前將問句進行分詞處理,而這裏使用的分詞工具爲ICTCLAS。各個檢索詞之間採用的是布爾運算的“或”運算。

1
2
TermQuery[] term=new TermQuery[length];
query.add(term[i],BooleanClause.Occur.SHOULD);//通過後面參數確定布爾檢索類型

在Lucene中布爾檢索中各運算符與其所對應的參數如下表:
運算類型 所對應的參數
AND(與) BooleanClause.Occur.MUST
NOT(非) BooleanClause.Occur.MUST_NOT
OR(或) BooleanClause.Occur.SHOULD
舉例說明布爾或運算的運算規則,如果輸入查詢詞“信息”和“管理”,查詢結果爲包含“信息”與“管理”這兩個詞的文檔與只包含“信息”這個檢索詞的文檔以及只包含“管理”這個詞的文檔。當然在實際的操作中可以採用完全不同的查詢方式。
下面爲ICTCLAS的分詞過程:

1
2
3
4
5
6
7
8
9
10
11
public String[] getParserResult(ICTCLAS instance,String sentence){
  String result;
  result=instance.paragraphProcess(sentence);
  String[] res;
  res=result.split("\\s+");
  for(int i=0;i<res.length;i++){
  int position=res[i].indexOf("/");
  System.out.println(position);
  res[i]=res[i].substring(0, position);
  System.out.println(res[i]); }
return res;}

這裏對上面的程序進行一個簡單的介紹,其中最重要的一行爲:
result=instance.paragraphProcess(sentence);
其中sentence代表輸入的問句,result就是經過分詞得到的結果,不過輸出結果形式有一個特別之處,例如:句子“信息檢索”其result的結果形式爲“信息/n 檢索/v”,而在實際的過程中我們並不需要後面的詞性以及”/”,因此後面的程序代碼都是爲了將分詞結果變成一個字符數組,去掉空格以及後面的”/”和詞性。
分詞完成之後就可以把分詞結果放入term中進行檢索了。

2.5界面實現

界面部分需要兩個文件一個index.html文件,一個是search.jsp文件,其中index.html主要作用是查詢主界面的實現。而search.jsp則用戶處理問句、問句檢索以及檢索結果展示。
下面是index.html文件的表單部分代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    <form action="search.jsp" method="GET">
    <input type="text" name="keys" size=50 height=100px class=kw maxlength="100">
    <input type="submit" value="search" class=sb>
    <font size="2" face="宋體" >
可以看到其表單的內容其實都交給了search.jsp來處理。而search.jsp的部分代碼如下:
    <jsp:directive.page import="com.davenzhang.util.*"/>
    <jsp:directive.page import="com.xjt.nlp.word.ICTCLAStest"/>
    ……
    <form action="search.jsp" method="get">
    <input type="text" name="keys" size=36 maxlength="100">
    <input type="submit" value="search">
    <a href="index.html">Home</a>
    ……
    out.println("<font size=4px color=\"blue\">"+title+"</font><br>");
    out.print("摘要:"+summary);
    out.print("<br><a href=http://"+url+"><font color=\"green\">"+url+"</font></a> <br>");

前面的import部分,是導入一些java包,方便在jsp代碼中直接使用其功能,com.davenzhang.util.*主要包含了Lucene的查詢函數,而com.xjt.nlp.word.ICTCLAStest包則包含分詞所用到的函數。後面的out.println()則用於查詢結果的展示

3.Nutch

3.1 Nutch安裝與配置

與Lucene不同,Nutch是一個可以直接運行的程序,基本上不需要自己編寫任何代碼就能實現簡單的全文搜索引擎的功能。下面介紹Nutch的安裝和配置過程。
(1) 安裝JDK/Tomcat/Cygwin,這些軟件的安裝都有詳細的教程,這裏不再贅述。
(2) 修改環境變量
變量名 NUTCH_JAVA_HOME
變量值 %JAVA_HOME%
運行cygwin,輸入命令cd /cygdrive/d/ Nutch-1.2,再輸入bin/Nutch,如果出現Nutch的命令行介紹就代表Nutch安裝成功。
6

(3) 爬蟲設置
與Lucene不同,Nutch自帶了爬蟲的功能,但是需要進行一些配置,下面對這個過程做一個詳細的介紹。
設置需要抓取的網站主域名
在Nutch-1.2的安裝目錄下建立一個名爲urls的文件夾,並在文件夾下建立url.txt文件,在文件中寫入口地址http://im.nju.edu.cn/
設置網站過濾規則
編輯conf/crawl-urlfilter.txt文件,修改MY.DOMAIN.NAME部分。將
# accept hosts in MY.DOMAIN.NAME
+^http://([a-z0-9]*\.)*MY.DOMAIN.NAME/
改爲:
# accept hosts in MY.DOMAIN.NAME
+^http://([a-z0-9]*\.)*nju.edu.cn/
修改conf/Nutch-site.xml代理信息
在和之間添加如下文件:

http.agent.name
http://im.nju.edu.cn/value> http.agent.url
http://im.nju.edu.cn/value> http.robots.agents
http://im.nju.edu.cn/ 設置代理名
編輯Nutch-1.2\conf\Nutch-default.xml文件,找http.agent.name,然後隨便設置Value值。例如:

http.agent.name
test
執行Nutch抓取url數據
在Cygwin命令行窗口中輸入:cd /cygdrive/d/Nutch-1.2
再輸入bin/Nutch crawl urls –dir crawl –depth 3 –threads 4 –topN 30 >& crawl.log
命令說明:
crawl:是Nutch檢索數據命令,後面跟上要檢索的URL文件。urls就是第一部分部份創建的文件;
-di:是檢索後的結果存放目錄參數,後面跟上結果存放地址。如果我們存放到Nutch目錄下的crawl目錄,注意此目錄當前是不存在的。檢索完後Nutch會創建出來。-threads 抓取時的線程數參數
-depth:抓取時的深度參數
-topN:抓取時每頁的最大抓取鏈接
最後把執行信息寫入crawl.log日誌文件中,方便查找錯誤。
爬蟲執行完成之後會在Nutch的目錄下生成一個crawl文件夾,裏面包含五個子文件夾,分別爲:crawldb/index/indexes/linkdb/segments。
部署到Tomcat
將Nutch-1.2目錄下的Nutch-1.2.war複製到Tomcat目錄webapp下面
修改配置文件WEB-INF\classes\Nutch-site.xml爲如下形式,其中的value值就是剛纔爬蟲爬取得信息。

7

然後重啓tomcat就可以完成Nutch的安裝於配置的全部過程。

3.2Nutch的運行

在瀏覽器中輸入http://localhost:8080/Nutch-1.2/zh/,即可進入Nutch主頁面,輸入檢索詞“南京大學”所得到的檢索結果如下圖所示:
8
下面是Nucth的一些特性:
默認把詞語分成單個詞來查詢,例如,“計算機”會以“計”、“算”、“機”來做查詢。
Nutch中所有的parsing(分析)、indexing(索引)和searching(查詢)都是通過不同的插件來實現的。Nutch把自己可以提供的Plugin接口用一個xml文檔來描述。
自定義插件時,只需要自行設計一些符合自身功能需求的類(繼承Nutch提供的父類或者實現一些接口),然後修改配置文件就行了。

4.Lucene與Nutch對比

Lucene其實是一個提供全文文本搜索的函數庫,它不是一個應用軟件。它提供很多API函數讓你可以運用到各種實際應用程序中。現在,它已經成爲Apache的一個項目並被廣泛應用着。Lucene不管是爬蟲的設計,還是文檔的預處理,還是索引的建立,還是系統的分詞等等,都需要用戶編寫代碼,實現功能。Lucene給用戶提供了非常強大的功能,但要享受這些功能的則必須具備編寫複雜代碼的能力。無論是在爬蟲的選擇、搜索的優化、排序算法的設計等等用戶都需要自行決定。
Nutch是一個建立在Lucene核心之上的Web搜索的實現,它是一個真正的應用程序。也就是說,你可以直接下載下來拿過來用。它在Lucene的基礎上加了網絡爬蟲和一些和Web相關的東西。其目的就是想從一個簡單的站內索引和搜索推廣到全球網絡的搜索上。Nutch主要分爲兩個部分:爬蟲crawler和查詢searcher。Crawler主要用於從網絡上抓取網頁併爲這些網頁建立索引。Searcher主要利用這些索引檢索用戶的查找關鍵詞來產生查找結果。兩者之間的接口是索引,所以除去索引部分,兩者之間的耦合度很低。Crawler和Searcher兩部分儘量分開的目的主要是爲了使兩部分可以分佈式配置在硬件平臺上,例如將Crawler和Searcher分別放在兩個主機上,這樣可以提升性能。
另外,還有一個和Lucene類似的工具Solr。Solr是Lucene的服務器化,內嵌了jetty,用戶可以直接post數據給Solr,然後由Solr進行索引。Solr不包含下載系統,用戶需要負責下載,轉成Solr所需要的格式。Nutch和Solr都是基於Lucene的,二者都是可直接運行的應用程序。 一般可以使用Nutch做crawler,而使用Solr做indexer和查詢接口。

參考文獻:
[1]. Otis Gospodnetic;Erik Hatcher(著). lucene in action(中文版).譚鴻;黎俊鴻等譯.北京:電子工業出版社, 2007
[2]. 羅剛.解密搜索引擎技術實戰:Lucene&Java精華版. 北京:電子工業出版社,2011
[3]. 袁津生, 李羣主編.搜索引擎基礎教程.北京:清華大學出版社, 2010
[4]. 高凱,郭立煒,許雲峯編著.網絡信息檢索技術及搜索引擎系統開發.北京:科學出版社, 2010
[5]. http://lucene.apache.org/
[6]. http://nutch.apache.org/
[7]. http://crawler.archive.org/index.html
[8]. http://sourceforge.net/p/htmlparser/wiki/Home/
[9]. http://pdfbox.apache.org/
[10]. poi


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章