dySE:一個 Java 搜索引擎的實現,第 3 部分: 查詢服務

在以前的兩個部分中,您瞭解到 spider 的編寫和對原始網頁庫的預處理:經過 spider 咱們獲得一個原始網頁庫,而經過預處理部分創建網頁的索引,並用分詞器對網頁進行分詞進而建立倒排索引。本部份內容將要介紹查詢服務的編寫,查詢服務經過接收用戶的輸入,調用後臺程序對輸入進行分詞以及查詢操做以後,將返回的查詢結果在網頁上顯示。本文分三個步驟介紹查詢服務的實現過程:首先使程序在控制檯下可以返回查詢結果,爲查詢結果的顯示作準備;而後,搭建 Web 服務器進行網絡編程使得程序可以方便的輸入並進行結果返回;最後,介紹網頁的排名策略和實現。下面就讓咱們逐步介紹查詢服務的設計和實現。html

回頁首java

查詢服務的總體結構正則表達式

查詢服務的總體結構以下:算法


圖 1. 查詢服務總體結構
圖 1. 查詢服務總體結構  

在前面兩部分的敘述中,咱們有了放在文件中的原始網頁庫、放在數據庫中的網頁索引 ( 指示某個網頁所在原始網頁庫的位置 )、倒排索引,以及一些小工具:分詞器。在這些部件的基礎上,咱們開始搭建咱們搜索引擎的界面而且實現信息的輸入和輸出。數據庫

如下的章節安排以下:首先咱們完善後臺服務,使得程序可以在控制檯輸入查詢的狀況下,在控制檯中返回須要的結果信息,這些結果將在後續的部分中返回給網頁進行顯示;其次,咱們搭建 Web 服務器,進行網頁編程,使得查詢服務與後臺服務程序可以交互;最後咱們介紹網頁結果返回時的一些優化,好比網頁排名的實現。apache

回頁首編程

簡單查詢tomcat

在第二部分預處理以後,咱們現有的待用數據以下:原始網頁庫,網頁索引,倒排索引,分詞器。爲了方便您對於後文的理解,咱們再次說明這些資源的用途:原始網頁庫記錄了爬蟲獲取的各個網頁信息,按照必定的格式保留在本地;然而這些網頁信息不便於隨機的進行訪問,因此咱們經過網頁索引記錄某個網頁在原始網頁庫中的位置,以方便查詢;倒排索引是一個關鍵字和包含這個關鍵字的網頁 URL 集合的映射,經過倒排索引能夠方便的獲得哪些網頁包含肯定的關鍵詞;分詞器的做用在於能夠對用戶輸入的文字進行分詞,由於用戶可能會輸入多個詞組因此分詞器在查詢服務中也起着必不可少的做用。服務器

簡單的查詢服務過程以下:對於用戶的輸入,首先進行分詞,對於每一個詞組,搜索倒排索引獲取包含該詞組的網頁 URL 信息, 找到各個分詞對應的 URL 集合中共同的 URL,根據結果 URL 集合查詢網頁索引得到 URL 對應的網頁信息,整合網頁信息以後進行返回。網絡

結果集合的生成

在上述的過程當中,分詞和倒排索引的具體結構已經在第二部分預處理模塊中說起,這裏就再也不贅述。分詞結果集 (keywords) 在倒排索引中搜索結果的算法以下:


清單 1. 結果檢索算法
結果集合,URLs 初始化爲空
 For each keyword in keywords do 
 begin 
獲取 keyword 對應的倒排索引中的數據項:urls;
合併 URLs 與 urls,保留二者的共同 url,若是 URLs 爲空,則將 urls 賦值給 URLs;
 end;
 if(URLs 爲空 ) return null;
 else return URLs;

因爲可能會產生信息查詢不到的狀況,因此算法中追加了檢索結果爲空的判斷,保證程序的健壯性。合併結果集合 URLs 和 keyword 對應的網頁集合 urls 中相同的元素有許多種方法,簡單的能夠採用創建一個臨時的集合,存儲二者的公共 url,等到該次執行結束,將該臨時集合中的值賦值給 URLs 便可。

如此咱們獲得了做爲簡單結果的 URL 集合,下一步咱們要經過這個集合生成詳細結果而且進行返回。咱們可使用其餘的搜索引擎好比 google、百度等來了解一個網頁結果具體須要包含哪些信息,下圖是在 google 中搜索「中國教育」關鍵字的返回結果:



Figure xxx. Requires a heading  

從圖中能夠看出,在第一行顯示的是該網頁的標題,而且是一個超級連接,第二行是正文內容的一個摘要,該摘要最好可以包含搜索的關鍵詞,從而用戶能夠判斷該網頁與用戶查詢的相關度。最後一行顯示的是該網頁的 URL 以及網頁快照的一個功能。快照功能是指收錄的網頁的純文本備份,在網速很慢或者原始網頁沒法打開的狀況下,可使用快照功能查看該網頁的文本內容,快照功能的實現咱們將在本文的末尾說起。下面咱們主要完成標題的提取、正文摘要的提取兩個部分。

在第二部分中,咱們介紹瞭如何經過原始網頁庫的文件名和文件內偏移進行某個 URL 對應的頁面數據查詢,因此這部分咱們只是再簡單的說起,經過數據庫的查詢,咱們能夠獲得某個 URL 所對應的文件的所在位置,經過 BufferedReader 類中提供的 skip 函數能夠完成偏移量的跳轉從而直接開始讀取所須要的頁面信息。標題的獲取比較直觀,因爲 html 中 <title></title> 標籤對中的內容即對應該網頁的標題,因此提取該標籤便可,對於頁面數據,咱們能夠用正則表達式來比配這個標籤對,該匹配的正則表達式以下:title[^>]*?>[\\s\\S]*?</title>。正則表達式具體的匹配過程在第二部分中有示例,這裏再也不贅述。

正文摘要的生成主要有兩種方法,一種是在 html 標籤中提取 description 信息,網頁的摘要信息會放在形如:<META content="關注搜索引擎…" name=description> 的標籤中,仍舊經過正則表達式,咱們能夠匹配獲得網頁的摘要信息,這種方法比較經常使用,同時也很方便。第二種方法在網頁正文的基礎上生成,因爲某些網頁中可能不包含 description 標籤,這樣就須要在正文中抽取網頁摘要,這種方法也是第一種方法的一個備用方法。網頁正文能夠經過去掉網頁的 html 標籤來得到。正文摘要的目標是使摘要能儘可能多的在一段內容中顯示更多與查詢關鍵字相關的信息,爲此,咱們能夠採用以下策略來進行摘要的生成:

  • 首先,用戶查詢的關鍵字在摘要中最好能處於相鄰位置。因爲 URL 結果是在分詞的基礎上搜索生成的,因此 URL 對應頁面包含的關鍵字可能也是分散的,例如,咱們搜索「搜索引擎」關鍵字,若是一個頁面上有以下兩段文字:(1) 本文介紹搜索引擎的具體實現步驟…;(2) 警察經過搜索發現,汽車的引擎不知去向…。很顯然,文本 (1) 更加適合作該關鍵字的摘要。
  • 在提取的限定長度的摘要中,關鍵詞的出現頻率應該要比較高;
  • 若是第一點不能達到,那麼在摘要中,關鍵詞之間的間隔應該要儘量的小。

根據這些策略,咱們就能夠提取摘要的文字信息。

除了上述的兩項信息,百度在搜索結果中還有網頁的日期這一項,咱們能夠參照這點在結果中顯示日期信息。因爲咱們在將網頁格式化存儲時包含了摘錄該網頁的時間,咱們能夠直接獲取該日期顯示在結果中。

爲了更好的封裝返回的結果,咱們建立 Result 類來存儲單個網頁的返回信息,這其中主要包括了標題 (title)、正文動態摘要、日期、URL 四種數據。而查詢的最終結果是返回一個 Result 的 List。

因爲這些數據都是字符串類型,因此能夠很容易的在控制檯上進行顯示並進行測試,咱們能夠在控制檯下測試以下:


清單 2. 控制檯下檢索結果檢測
public static void main(String[] args) { 
 Scanner cin=new Scanner(System.in); 
 String keyword = cin.next();  //read the keyword from console 
 Response response = new Response(); 
 ArrayList<Result> results = response.getResponse(keyword); 
 System.out.println("返回結果以下:"); 
 for(Result result : results){ 
 System.out.println(result.getTitle()); 
 System.out.println(result.getContent()); 
 System.out.println(result.getUrl() + "  " + result.getDate()); 
 } 
 }

在控制檯輸入「中國教育」返回的結果大體以下:


圖 2. 查詢「中國教育」返回的結果
圖 2. 查詢「中國教育」返回的結果  

回頁首

搭建 Web 服務器提供查詢服務

通常的搜索引擎都是經過 Web 程序提供應用接口,從而提供服務,在本節咱們介紹 Web 服務器的搭建提供查詢服務。咱們按照 Web 服務器的搭建、與後臺查詢模塊的鏈接兩個部分來進行敘述。

Web 服務器搭建

因爲咱們的後臺 ( 即以前所述的倒排索引創建查詢和結果返回等部分 ) 是用 Java 編寫,因此很天然的,咱們想到用 JSP(Java Server Page) 來提供查詢服務,在這小節中,咱們重點介紹如何搭建服務器提供 JSP 服務。

咱們使用 Tomcat 做爲 Web 應用服務器,Tomcat 是一個小型的開源輕量級應用服務器,是開發和調試 JSP 程序的很好選擇。咱們先來介紹 Tomcat 服務器的搭建過程:

  • 下載 Tomcat6.0,參考地址:http://tomcat.apache.org/;
  • Tomcat 是免安裝的,因此解壓到本地進行環境變量的配置便可使用;
  • 雙擊在 tomcat 的 bin 目錄下的 startup.bat 文件,啓動 tomcat 服務器;

Web 頁面編寫

有了服務器的支持,咱們便可進行 Web 頁面的編寫,以提供查詢服務的入口和結果返回頁面。查看大部分搜索引擎的界面,不管是主界面仍是搜索結果顯示界面,其顯示的內容都較爲簡單,因此 JSP 的頁面開發環境您能夠根據您的習慣和喜愛自由選擇,本文主要在 MyEclipse 中進行頁面編寫。


清單 3. 查詢服務入口 index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%> 
 <% 
 String path = request.getContextPath(); 
 String basePath = request.getScheme()+"://"
 +request.getServerName()+":"
 +request.getServerPort()+path+"/"; 
 %> 
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
 <html> 
  <head> 
    <base href="<%=basePath%>"> 
        <title>dySE</title> 
          <style> 
 #search{ 
 width:78px; 
 height:28px; 
 font:14px "宋體"
 } 
 #textArea{ 
 width:300px; 
 height:30px; 
 font:14px "宋體"
 } 
 </style> 
  </head> 
  
  <body> 
 <p align="center"><img src="dySE-logo.jpg" /></p> 
 <form action="search.jsp" name="search" method="get" 
 enctype="application/x-www-form-urlencoded"> 
 <table border="0" height="30px" width="450px" align="center"> 
 <tr> 
 <td width ="66%"><input name="keyword" type="text" maxlength="100" 
 id="textArea"></td> 
 <td height="29" align="center"><input type="submit" value="搜索一下" 
 id = "search"></td> 
 </tr> 
 </table> 
 </form> 
  </body> 
 </html>

咱們在 MyEclipse 中新建一個 WEB PROJECT,並新建一個 JSP 頁面,命名爲 index.jsp,MyEclipse 會自動生成基本的頁面代碼,咱們編寫的代碼主要是兩個部分,一部分是 <style></style> 標籤對中的 CSS 樣式,這部分指定了頁面中關鍵字輸入文本框和按鈕的樣式,這裏就此略過。另外一部分是 <body></body> 標籤對中的代碼,第一行居中顯示 dySE 的 logo 圖標,而後空行,以後就是一個表單,其中包括了一個含有文本輸入框和按鈕的表格—— <table> </table> 標籤對中,在 form 標籤中,設定了按下按鈕的動做——轉到 search.jsp 頁面,其中的 enctype="application/x-www-form-urlencoded"指定了編碼格式,若是沒有指定,在搜索中文的時候會致使亂碼。該代碼顯示結果以下:


圖 3. 搜索界面
圖 3. 搜索界面  

接下來咱們編寫搜索結果顯示頁面。


清單 4. 查詢結果顯示 search.jsp
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%> 
 <jsp:directive.page import="core.query.Response" /> 
 <jsp:directive.page import="core.util.Result" /> 

 <% 
 String path = request.getContextPath(); 
 String basePath = request.getScheme()+"://"+request.getServerName()+":
 "+request.getServerPort()+path+"/"; 
 %> 

 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
 <html> 
  <head> 
    <base href="<%=basePath%>"> 
    <title>Search Result</title> 
    <style> 
 #search{ 
 width:78px; 
 height:28px; 
 font:14px "宋體"
 } 
 #textArea{ 
 width:300px; 
 height:30px; 
 font:14px "宋體"
 } 
 </style> 
  </head> 
  
  <body> 
    <form action="search.jsp" name="search" method="get"> 
 <table border="0" height="30px" width="450px" align="center"> 
 <tr> 
 <td><img src="dySE-logo.jpg" /></td> 
 <td width ="66%"><input name="keyword" type="text" maxlength="100" id="textArea" ></td> 
 <td height="29" align="center"><input type="submit" value="搜索一下" id = "search"></td> 
 </tr> 
 </table> 
      </form> 
 <%  
 String keyword = new String(request.getParameter("keyword") .getBytes("
 ISO-8859-1"),"GB2312"); 
 Response resp = new Response(); 
 ArrayList<Result> results = resp.getResponse(keyword); 
 for(Result result : results) 
 { 
 %> 
 <h2><a href=<%=result.getUrl()%>><%=result.getTitle()%></a></h2> 
 <p><%=result.getContent()%><p> 
 <p><%=result.getUrl()%> &nbsp;&nbsp;&nbsp; <%=result.getDate()%><p> 
 <%  
 } 
 %>  
 </body> 
 </html>

Search.jsp 在開頭引入了 response 和 result 兩個類,其後的代碼與 index.jsp 有很大部分的類似之處,這裏再也不贅述,主要說明一下 <form></form> 標籤對以後查詢服務的調用以及返回的結果的顯示方式。第一行先獲取了用戶在文本框內輸入的查詢關鍵字,爲了防止編碼問題,咱們在獲取結果時候加入編碼格式。以後經過咱們創建的 Response 類來進行結果的得到,經過傳入搜索的關鍵字,Response 類在 getResponse 操做中對倒排索引進行查詢,將查詢的結果放入到結果列表中(算法可參見簡單查詢部分),操做返回的結果是一個 Result 類型的 List,遍歷這個 List 而且按照必定的格式顯示這些數據便可獲得所須要的輸出,輸出的內容將按照必定的 html 格式進行設置。第一行創建一個超連接,連接的顯示文字是 Result 類型中頁面的 title 屬性,連接的地址是對應的 url。第二行將頁面的內容簡介進行顯示,並在第三行顯示頁面對應的 url 和頁面的抓取日期。


圖 4. 搜索結果返回
圖 4. 搜索結果返回  

因爲咱們在試驗過程當中,主要爬取的是幾大門戶網站的網頁,因此搜索「中國教育」並不會出來中國教育網之類的網站,可是,咱們的結果返回了新浪和網易的教育頻道,可見咱們的搜索引擎是能夠正確運行的。

回頁首

網頁排名

到目前爲止,咱們的網頁已經能夠正確的返回所輸入和查詢的結果,可是還有一個問題須要咱們考慮,那就是網頁排名策略。網頁排名簡單來講就是搜索引擎對搜索某個關鍵字產生的結果網頁集合的返回順序,因爲對於用戶來講,用戶感興趣的網頁最好可以排在前面來顯示,從而減小用戶篩選結果的開銷。網頁排名策略便是考評結果網頁集合排列順序的算法策略,最基本的策略要求就是使得與用戶輸入最相關的網頁排在以前,那麼如何肯定網頁內容與用戶輸入關鍵詞的相關程度呢?

咱們仍是以搜索「中國教育」爲例解釋網頁排名策略。咱們知道,「中國教育」能夠分爲兩個關鍵詞:中國、教育。根據經驗,咱們知道,包含這兩個詞多的網頁要比包含這兩個詞少的網頁相關,因此咱們能夠統計網頁中,包含的關鍵詞的總數,從而簡單的肯定網頁的相關性。可是,這樣的方法有個問題,那就是長的網頁比短的網頁跟佔優點,因此咱們須要根據網頁的長度,對關鍵詞的次數進行歸一化,也就是用關鍵詞的次數除以網頁的總字數,這個商叫作「關鍵詞詞頻」(Term Frequency),好比,某個 1000 詞的網頁中,中國出現了 10 詞,教育出現了 3 次,那麼二者的詞頻分別爲 0.01 和 0.003,則其和 0.013 就是該網頁與「中國教育」的相關度的一個簡單度量。相關性的一個簡單的度量。歸納地講,若是一個查詢包含關鍵詞 w1,w2,...,wn,它們在一個特定網頁中的詞頻分別是 :TF1,TF2,...,TFn (TF: Term Frequency)。那麼,這個查詢和該網頁的相關性就是:TF1+TF2+...+TFn。

進一步咱們能夠發現,「中國」這個詞很普通,而「教育」是一個較爲專業的詞,因此後者在相關性排名中應該比前者重要,所以咱們引入關鍵詞的權重,以區分各個關鍵詞之間的重要性。該權重應該具備以下特性:首先一個詞預測主題能力越強,權重越大,反之則權重越小;其次,停用詞的權重爲 0。那麼,這個權重如何肯定呢?在信息檢索中,使用最多的權重計算方法是「逆文本頻率指數」(Inverse Document Frequency:IDF)。其公式爲 log(D/DW), 其中,D 是所有網頁數,而 DW 是關鍵詞 W 在 DW 個網頁中出現過。假設所有網頁 D=10 億,「教育」在 2 百萬個網頁中出現,則其權重 IDF=log(500)=6.2,同理若「中國」在 5 億個網頁中出現,則其權重爲 IDF=log(2)=0.7。因此,咱們網頁相關性的計算公式也轉變爲:

TF1*IDF1+TF2*IDF2+...+TFn*IDFn。

第三,既然搜索「中國教育」,那咱們但願網頁中「中國」和「教育」這兩個詞的出現位置是更多的是處於相鄰位置,諸如「淺談中國教育」的網頁內容應該比「中國工人先進性教育」更符合咱們的搜索目標。關於位置信息須要在倒排索引創建的過程當中進行抽取,因爲在第二部分的倒排索引中,爲了方便理解,咱們只是創建了最簡單的倒排索引,而沒有加入位置信息,因此這部分的策略咱們將在後續的優化部分進行說明。

回頁首

總結

到如今爲止,咱們已經完成了 dySE 搜索引擎的實現過程講解,咱們按照搜索引擎中處理的三個模塊進行分塊介紹,從第一部分的網絡爬蟲獲取原始網頁庫,到第二部分的預處理創建索引網頁庫、分詞以及創建倒排索引,到此文中搭建 Web 服務器提供網絡查詢服務而且進行網頁的排名。這其中爬蟲是搜索引擎的基礎,提供了原始數據集,而預處理是核心,提供後臺的查詢服務而且返回給前臺 Web,而第三部分是與用戶交互的接口,提供查詢結果的輸入和輸出。三者互相依賴,互相配合完成搜索引擎的工做。

然而,目前咱們還只是對各個模塊進行了簡單的實現和鏈接,一些優化方案和具體實現過程將在後續的優化章節中進行介紹。

以上就是咱們爲您呈現的搜索引擎的實現過程,項目的所有源代碼您能夠在 Google Code 中得到,網址是 :http://code.google.com/p/dynasty-search-engine/,歡迎您的評價並提出不足,衷心但願能對您有所啓發和幫助,感謝您的閱讀,謝謝。


參考資料

學習

相關文章
相關標籤/搜索