用python作一個搜索引擎(Pylucene)

  1. 什麼是搜索引擎?

搜索引擎是「對網絡信息資源進行蒐集整理並提供信息查詢服務的系統,包括信息蒐集、信息整理和用戶查詢三部分」。如圖1是搜索引擎的通常結構,信息蒐集模塊從網絡採集信息到網絡信息庫之中(通常使用爬蟲);而後信息整理模塊對採集的信息進行分詞、去停用詞、賦權重等操做後創建索引表(通常是倒排索引)構成索引庫;最後用戶查詢模塊就能夠識別用戶的檢索需求並提供檢索服務啦。html

 

 

圖1  搜索引擎的通常結構python

2.  使用python實現一個簡單搜索引擎web

2.1  問題分析算法

從圖1看,一個完整的搜索引擎架構從互聯網蒐集信息開始,能夠使用python編寫一個爬蟲,這是python的強項。flask

接着,信息處理模塊。分詞?停用詞?倒排表?what?什麼亂七八糟的?不用管它,咱們有前輩們造好的輪子---Pylucene(lucene的python封裝版本,Lucene可以幫助開發者爲軟件、系統增添檢索功能。Lucene是一套用於全文檢索和搜尋的開源程序庫)。使用Pylucene能夠簡單的幫助咱們完成對採集到的信息進行處理,包括索引的創建和搜索。數組

最後,爲了能在網頁上使用咱們的搜索引擎,咱們使用flask這個輕量級 Web 應用框架作一個小網頁獲取搜索語句並反饋搜索結果。瀏覽器

2.2 爬蟲設計網絡

主要蒐集如下內容:目標網頁的標題、目標網頁的主要文字內容、目標網頁指向其餘頁面的URL地址。網絡爬蟲的工做流程如圖2所。爬蟲的主要數據結構是隊列。首先,起始的種子節點進入隊列,而後從隊列中取出一個節點訪問,抓取該節點頁面上的目標信息,再將該節點頁面指向其餘頁面的URL連接放進隊列,再從隊列中取出新的節點進行訪問,直至隊列爲空。經過隊列「先進先出」的特色實現廣度優先的遍歷算法,逐個訪問站點的每一頁面。數據結構

 

                                    圖2架構

2.3  pylucene的使用

Pylucene中關於創建索引的類主要有Directory、Analyzer、IndexWriter、Document、Filed。

Directory是Pylucene中關於文件操做的類。它有SimpleFSDirectory和RAMDirectory、CompoundFileDirectory、FileSwitchDirectory等11個子類,列舉的四個是與索引目錄的保存相關的子類,SimpleFSDirectory是將構建的索引保存至文件系統之中;RAMDirectory是將索引保存至RAM內存之中;CompoundFileDirectory是一種複合的索引保存方式;而FileSwitchDirectory容許臨時切換索引的保存方式以發揮各類索引保存方式的優勢。

Analyzer,分析器。它是對爬蟲得到的將要進行構建索引的文本進行處理的類。包括了文本進行分詞操做、去掉停用詞、轉換大小寫等操做。Pylucene自帶了若干分析器,構建索引時也可以使用第三方分析器或者自寫分析器。分析器的好壞關係到構建索引的質量與搜索服務的所能提供的精準度與速度。

IndexWriter,索引寫入類。在Directory開闢的儲存空間中IndexWriter能夠進行索引的寫入、修改、增添、刪除等操做,但不可進行索引的讀取也不能搜索索引。

Document,文檔類。在Pylucene中創建索引的基本單位是「文檔」(Document),一個Document多是一個網頁、一篇文章、一封郵件。Document是用以構建索引的單位同時也是進行搜索時的結果單位,對它進行合理的設計可以提供個性化的搜索服務。

Filed,域類。一個Document之中能夠包含多個域(Field)。Filed是Document的組成部分,就如一篇文章的組成多是文章標題、文章主體、做者、發表日期等多個Filed。

將一個頁面做爲一個Document,包含三個Field分別是頁面的URL地址(url)、頁面的標題(title)、頁面的主要文字內容(content)。對於索引的儲存方式選擇使用SimpleFSDirectory類,將索引保存至文件之中。分析器選擇Pylucene自帶的CJKAnalyzer,該分析器對中文支持較好,適用於中文內容的文本處理。

使用Pylucene構建索引的具體操做步驟以下:

 

lucene.initVM()

INDEXIDR = self.__index_dir

indexdir = SimpleFSDirectory(File(INDEXIDR))①

analyzer = CJKAnalyzer(Version.LUCENE_30)②

index_writer = IndexWriter(indexdir, analyzer, True, IndexWriter.MaxFieldLength(512))③

document = Document()④

document.add(Field("content", str(page_info["content"]), Field.Store.NOT, Field.Index.ANALYZED))⑤

document.add(Field("url", visiting, Field.Store.YES, Field.Index.NOT_ANALYZED))⑥

document.add(Field("title", str(page_info["title"]), Field.Store.YES, Field.Index.ANALYZED))⑦

index_writer.addDocument(document)⑧

index_writer.optimize()⑨

index_writer.close()⑩

 

索引的構建有10個主要的步驟:

①實例化一個SimpleFSDirectory對象,將索引保存至本地文件之中,保存的路徑爲自定義的路徑「INDEXIDR」。

②實例化一個CJKAnalyzer分析器,實例化時的參數Version.LUCENE_30爲Pylucene的版本號。

③實例化一個IndexWriter對象,所攜帶的四個參數分是前面的實例化的SimpleFSDirectory對象和CJKAnalyzer分析器,布爾型的變量true表示建立一個新的索引,IndexWriter.MaxFieldLength指定了一個索引最大的域(Filed)數量。

④實例化一個Document對象,取名爲document。

⑤爲document添加名稱爲「content」的域。該域的內容爲爬蟲獲取的某一網頁頁面的主要文字內容。該操做的參數是實例化並立刻使用的Field對象;Field對象的四個參數分別是:

(1)「content」,域的名稱。

(2)page_info["content"],爬蟲蒐集到的網頁頁面的主要文字內容。

(3)Field.Store是用於表示該域的值是否能夠恢復原始字符的變量,Field.Store.YES表示存儲在該域中的內容能夠恢復至原始文本內容,Field. Store.NOT表示不可恢復。

(4)Field.Index變量表示該域的內容是否應用分析器處理,Field. Index.ANALYZED表示對該域字符處理使用分析器,Field. Index. NOT_ANALYZED則表示不對該域使用分析器處理字符。

⑥添加名稱爲「url」的域用以保存該頁面地址。

⑦添加名稱爲「title」的域用以保存該頁面的標題。

⑧實例化IndexWriter對像將文檔document寫入索引文件。

⑨優化索引庫文件,合併索引庫中的小文件爲大文件。

⑩單個週期內構建索引操做完成後關閉IndexWriter對像。

 

Pylucene關於創建索引的搜索的類主要有IndexSearcher、Query、QueryParser[16]。

IndexSearcher,索引搜索類。用於在IndexWriter構建的索引庫中進行搜索操做。

Query,描述查詢請求的類。它將查詢請求遞交給IndexSearcher完成搜索操做。Query擁有許多子類以完成不一樣的查詢請求。例如TermQuery是按詞條搜索,它是最基本最簡單的查詢類型,用來在指定域中匹配特定項的文檔;RangeQuery,指定範圍內搜索,用於在指定域中匹配特定範圍內的文檔;FuzzyQuery,一種模糊查詢,可以簡單地識別近義詞匹配與查詢關鍵字語義相近的項。

QueryParser,Query解析器。須要實現不一樣的查詢需求時必須使用Query提供的不一樣子類,致使Query使用起來容易形成混亂。於是Pylucene還提供了Query語法解析器QueryParser。QueryParser可以解析提交的Query語句,根據Query語法挑選合適Query子類完成相應的查詢,開發者沒必要關心底層使用的是什麼Query實現類。例如Query語句「關鍵字1 and 關鍵字2」 QueryParser解析爲查詢同時匹配關鍵字1和關鍵字2的文檔;Query語句「id[123 to 456]」 QueryParser解析成爲查詢名稱爲「id」的域中的值在指定範圍「123」到「456」之間的文檔;Query語句「關鍵字 site:www.web.com」QueryParser解析成爲查詢同時知足名稱爲「site」的域中值爲「www.web.com」  和匹配「關鍵字」兩個查詢條件的文檔。

索引的搜索是Pylucene所專一的領域之一,爲實現索引的搜索編寫了一個名爲query的類,query實現索引的搜索有如下主要步驟:

 

lucene.initVM()

if query_str.find(":") ==-1 and query_str.find(":") ==-1:

query_str="title:"+query_str+" OR content:"+query_str①

indir= SimpleFSDirectory(File(self.__indexDir))②

lucene_analyzer= CJKAnalyzer(Version.LUCENE_CURRENT)③

lucene_searcher= IndexSearcher(indir)④

my_query = QueryParser(Version.LUCENE_CURRENT,"title",lucene_analyzer).parse(query_str)⑤

total_hits = lucene_searcher.search(my_query, MAX)⑥

for hit in total_hits.scoreDocs:⑦

            print"Hit Score: ", hit.score

            doc = lucene_searcher.doc(hit.doc)

            result_urls.append(doc.get("url").encode("utf-8"))

            result_titles.append(doc.get("title").encode("utf-8"))

            print doc.get("title").encode("utf-8")

 

 result = {"Hits": total_hits.totalHits, "url":tuple(result_urls), "title":tuple(result_titles)}

 return result

 

索引的搜索有7個主要的步驟:

①首先對搜索語句進行判斷,若語句不是針對標題或文章內容進行單一域的查詢,即不包含關鍵詞「title:」或「content:」時默認搜索title和content兩個域。

②實例化一個SimpleFSDirectory對象,指定它的工做路徑爲先前建立索引的路徑。

③實例化一個CJKAnalyzer分析器,搜索時使用的分析器應與索引構建時使用的分析器在類型版本上均一致。

④實例化一個IndexSearcher對象lucene_searcher,它的參數爲第○2步的SimpleFSDirectory對象。

⑤實例化一個QueryParser對象my_query,它描述查詢請求,解析Query查詢語句。參數Version.LUCENE_CURRENT爲pylucene的版本號,「title」指默認的搜索域,lucene_analyzer指定了使用的分析器,query_str是Query查詢語句。在實例化QueryParser前會對用戶搜索請求做簡單處理,若用戶指定了搜索某個域就搜索該域,若用戶未指定則同時搜索「title」和「content」兩個域。

⑥lucene_searcher進行搜索操做,返回結果集total_hits。total_hits中包含結果總數totalHits,搜索結果的文檔集scoreDocs,scoreDocs中包括搜索出的文檔以及每篇文檔與搜索語句相關度的得分。

⑦lucene_searcher搜索出的結果集不能直接被Python處理,於是在搜索操做返回結果以前應將結果由Pylucene轉爲普通的Python數據結構。使用For循環依次處理每一個結果,將結果文檔按相關度得分高低依次將它們的地址域「url」的值放入Python列表result_urls,將標題域「title」的值放入列表result_titles。最後將包含地址、標題的列表和結果總數組合成一個Python「字典」,將最後處理的結果做爲整個搜索操做的返回值。

 

用戶在瀏覽器搜索框輸入搜索詞並點擊搜索,瀏覽器發起一個GET請求,Flask的路由route設置了由result函數響應該請求。result函數先實例化一個搜索類query的對象infoso,將搜索詞傳遞給該對象,infoso完成搜索將結果返回給函數result。函數result將搜索出來的頁面和結果總數等傳遞給模板result.html,模板result.html用於呈現結果

以下是Python使用flask模塊處理搜索請求的代碼:

 

app = Flask(__name__)#建立Flask實例

@app.route('/')#設置搜索默認主頁

def index():

html="<h1>title這是標題</h1>"

return render_template('index.html')

@app.route("/result",methods=['GET', 'POST'])#註冊路由,並指定HTTP方法爲GET、POST

def result(): #resul函數

if request.method=="GET":#響應GET請求

key_word=request.args.get('word')#獲取搜索語句

   if len(key_word)!=0:

      infoso = query("./glxy") #建立查詢類query的實例

       re = infoso.search(key_word)#進行搜索,返回結果集

       so_result=[]

       n=0

       for item in re["url"]:

temp_result={"url":item,"title":re["title"][n]}#將結果集傳遞給模板

        so_result.append(temp_result)

                n=n+1

        return render_template('result.html', key_word=key_word, result_sum=re["Hits"],result=so_result)

    else:

        key_word=""

    return render_template('result.html')

if __name__ == '__main__':

    app.debug = True

    app.run()#運行web服務

 

 

原文:https://www.cnblogs.com/lucky-pin/p/7117182.html

相關文章
相關標籤/搜索