用python做一個(gè)搜索引擎(Pylucene)的實(shí)例代碼
1.什么是搜索引擎?
搜索引擎是“對(duì)網(wǎng)絡(luò)信息資源進(jìn)行搜集整理并提供信息查詢服務(wù)的系統(tǒng),包括信息搜集、信息整理和用戶查詢?nèi)糠帧薄H鐖D1是搜索引擎的一般結(jié)構(gòu),信息搜集模塊從網(wǎng)絡(luò)采集信息到網(wǎng)絡(luò)信息庫(kù)之中(一般使用爬蟲(chóng));然后信息整理模塊對(duì)采集的信息進(jìn)行分詞、去停用詞、賦權(quán)重等操作后建立索引表(一般是倒排索引)構(gòu)成索引庫(kù);最后用戶查詢模塊就可以識(shí)別用戶的檢索需求并提供檢索服務(wù)啦。

圖1 搜索引擎的一般結(jié)構(gòu)
2. 使用python實(shí)現(xiàn)一個(gè)簡(jiǎn)單搜索引擎
2.1 問(wèn)題分析
從圖1看,一個(gè)完整的搜索引擎架構(gòu)從互聯(lián)網(wǎng)搜集信息開(kāi)始,可以使用python編寫(xiě)一個(gè)爬蟲(chóng),這是python的強(qiáng)項(xiàng)。
接著,信息處理模塊。分詞?停用詞?倒排表?what?什么亂七八糟的?不用管它,我們有前輩們?cè)旌玫妮喿?--Pylucene(lucene的python封裝版本,Lucene能夠幫助開(kāi)發(fā)者為軟件、系統(tǒng)增添檢索功能。Lucene是一套用于全文檢索和搜尋的開(kāi)源程序庫(kù))。使用Pylucene可以簡(jiǎn)單的幫助我們完成對(duì)采集到的信息進(jìn)行處理,包括索引的建立和搜索。
最后,為了能在網(wǎng)頁(yè)上使用我們的搜索引擎,我們使用flask這個(gè)輕量級(jí) Web 應(yīng)用框架做一個(gè)小網(wǎng)頁(yè)獲取搜索語(yǔ)句并反饋搜索結(jié)果。
2.2 爬蟲(chóng)設(shè)計(jì)
主要搜集以下內(nèi)容:目標(biāo)網(wǎng)頁(yè)的標(biāo)題、目標(biāo)網(wǎng)頁(yè)的主要文字內(nèi)容、目標(biāo)網(wǎng)頁(yè)指向其他頁(yè)面的URL地址。網(wǎng)絡(luò)爬蟲(chóng)的工作流程如圖2所。爬蟲(chóng)的主要數(shù)據(jù)結(jié)構(gòu)是隊(duì)列。首先,起始的種子節(jié)點(diǎn)進(jìn)入隊(duì)列,然后從隊(duì)列中取出一個(gè)節(jié)點(diǎn)訪問(wèn),抓取該節(jié)點(diǎn)頁(yè)面上的目標(biāo)信息,再將該節(jié)點(diǎn)頁(yè)面指向其他頁(yè)面的URL鏈接放進(jìn)隊(duì)列,再?gòu)年?duì)列中取出新的節(jié)點(diǎn)進(jìn)行訪問(wèn),直至隊(duì)列為空。通過(guò)隊(duì)列“先進(jìn)先出”的特點(diǎn)實(shí)現(xiàn)廣度優(yōu)先的遍歷算法,逐個(gè)訪問(wèn)站點(diǎn)的每一頁(yè)面。

圖2
2.3 pylucene的使用
Pylucene中關(guān)于建立索引的類主要有Directory、Analyzer、IndexWriter、Document、Filed。
Directory是Pylucene中關(guān)于文件操作的類。它有SimpleFSDirectory和RAMDirectory、CompoundFileDirectory、FileSwitchDirectory等11個(gè)子類,列舉的四個(gè)是與索引目錄的保存相關(guān)的子類,SimpleFSDirectory是將構(gòu)建的索引保存至文件系統(tǒng)之中;RAMDirectory是將索引保存至RAM內(nèi)存之中;CompoundFileDirectory是一種復(fù)合的索引保存方式;而FileSwitchDirectory允許臨時(shí)切換索引的保存方式以發(fā)揮各種索引保存方式的優(yōu)點(diǎn)。
Analyzer,分析器。它是對(duì)爬蟲(chóng)獲得的將要進(jìn)行構(gòu)建索引的文本進(jìn)行處理的類。包括了文本進(jìn)行分詞操作、去掉停用詞、轉(zhuǎn)換大小寫(xiě)等操作。Pylucene自帶了若干分析器,構(gòu)建索引時(shí)也可使用第三方分析器或者自寫(xiě)分析器。分析器的好壞關(guān)系到構(gòu)建索引的質(zhì)量與搜索服務(wù)的所能提供的精準(zhǔn)度與速度。
IndexWriter,索引寫(xiě)入類。在Directory開(kāi)辟的儲(chǔ)存空間中IndexWriter可以進(jìn)行索引的寫(xiě)入、修改、增添、刪除等操作,但不可進(jìn)行索引的讀取也不能搜索索引。
Document,文檔類。在Pylucene中建立索引的基本單位是“文檔”(Document),一個(gè)Document可能是一個(gè)網(wǎng)頁(yè)、一篇文章、一封郵件。Document是用以構(gòu)建索引的單位同時(shí)也是進(jìn)行搜索時(shí)的結(jié)果單位,對(duì)它進(jìn)行合理的設(shè)計(jì)能夠提供個(gè)性化的搜索服務(wù)。
Filed,域類。一個(gè)Document之中可以包含多個(gè)域(Field)。Filed是Document的組成部分,就如一篇文章的組成可能是文章標(biāo)題、文章主體、作者、發(fā)表日期等多個(gè)Filed。
將一個(gè)頁(yè)面作為一個(gè)Document,包含三個(gè)Field分別是頁(yè)面的URL地址(url)、頁(yè)面的標(biāo)題(title)、頁(yè)面的主要文字內(nèi)容(content)。對(duì)于索引的儲(chǔ)存方式選擇使用SimpleFSDirectory類,將索引保存至文件之中。分析器選擇Pylucene自帶的CJKAnalyzer,該分析器對(duì)中文支持較好,適用于中文內(nèi)容的文本處理。
使用Pylucene構(gòu)建索引的具體操作步驟如下:
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()⑩
索引的構(gòu)建有10個(gè)主要的步驟:
①實(shí)例化一個(gè)SimpleFSDirectory對(duì)象,將索引保存至本地文件之中,保存的路徑為自定義的路徑“INDEXIDR”。
②實(shí)例化一個(gè)CJKAnalyzer分析器,實(shí)例化時(shí)的參數(shù)Version.LUCENE_30為Pylucene的版本號(hào)。
③實(shí)例化一個(gè)IndexWriter對(duì)象,所攜帶的四個(gè)參數(shù)分是前面的實(shí)例化的SimpleFSDirectory對(duì)象和CJKAnalyzer分析器,布爾型的變量true表示創(chuàng)建一個(gè)新的索引,IndexWriter.MaxFieldLength指定了一個(gè)索引最大的域(Filed)數(shù)量。
④實(shí)例化一個(gè)Document對(duì)象,取名為document。
⑤為document添加名稱為“content”的域。該域的內(nèi)容為爬蟲(chóng)獲取的某一網(wǎng)頁(yè)頁(yè)面的主要文字內(nèi)容。該操作的參數(shù)是實(shí)例化并馬上使用的Field對(duì)象;Field對(duì)象的四個(gè)參數(shù)分別是:
(1)“content”,域的名稱。
(2)page_info["content"],爬蟲(chóng)搜集到的網(wǎng)頁(yè)頁(yè)面的主要文字內(nèi)容。
(3)Field.Store是用于表示該域的值是否可以恢復(fù)原始字符的變量,F(xiàn)ield.Store.YES表示存儲(chǔ)在該域中的內(nèi)容可以恢復(fù)至原始文本內(nèi)容,F(xiàn)ield. Store.NOT表示不可恢復(fù)。
(4)Field.Index變量表示該域的內(nèi)容是否應(yīng)用分析器處理,F(xiàn)ield. Index.ANALYZED表示對(duì)該域字符處理使用分析器,F(xiàn)ield. Index. NOT_ANALYZED則表示不對(duì)該域使用分析器處理字符。
⑥添加名稱為“url”的域用以保存該頁(yè)面地址。
⑦添加名稱為“title”的域用以保存該頁(yè)面的標(biāo)題。
⑧實(shí)例化IndexWriter對(duì)像將文檔document寫(xiě)入索引文件。
⑨優(yōu)化索引庫(kù)文件,合并索引庫(kù)中的小文件為大文件。
⑩單個(gè)周期內(nèi)構(gòu)建索引操作完成后關(guān)閉IndexWriter對(duì)像。
Pylucene關(guān)于建立索引的搜索的類主要有IndexSearcher、Query、QueryParser[16]。
IndexSearcher,索引搜索類。用于在IndexWriter構(gòu)建的索引庫(kù)中進(jìn)行搜索操作。
Query,描述查詢請(qǐng)求的類。它將查詢請(qǐng)求遞交給IndexSearcher完成搜索操作。Query擁有許多子類以完成不同的查詢請(qǐng)求。例如TermQuery是按詞條搜索,它是最基本最簡(jiǎn)單的查詢類型,用來(lái)在指定域中匹配特定項(xiàng)的文檔;RangeQuery,指定范圍內(nèi)搜索,用于在指定域中匹配特定范圍內(nèi)的文檔;FuzzyQuery,一種模糊查詢,能夠簡(jiǎn)單地識(shí)別近義詞匹配與查詢關(guān)鍵字語(yǔ)義相近的項(xiàng)。
QueryParser,Query解析器。需要實(shí)現(xiàn)不同的查詢需求時(shí)必須使用Query提供的不同子類,導(dǎo)致Query使用起來(lái)容易造成混亂。因而Pylucene還提供了Query語(yǔ)法解析器QueryParser。QueryParser能夠解析提交的Query語(yǔ)句,根據(jù)Query語(yǔ)法挑選合適Query子類完成相應(yīng)的查詢,開(kāi)發(fā)者不必關(guān)心底層使用的是什么Query實(shí)現(xiàn)類。例如Query語(yǔ)句“關(guān)鍵字1 and 關(guān)鍵字2” QueryParser解析為查詢同時(shí)匹配關(guān)鍵字1和關(guān)鍵字2的文檔;Query語(yǔ)句“id[123 to 456]” QueryParser解析成為查詢名稱為“id”的域中的值在指定范圍“123”到“456”之間的文檔;Query語(yǔ)句“關(guān)鍵字 site:www.web.com”QueryParser解析成為查詢同時(shí)滿足名稱為“site”的域中值為“www.web.com” 和匹配“關(guān)鍵字”兩個(gè)查詢條件的文檔。
索引的搜索是Pylucene所專注的領(lǐng)域之一,為實(shí)現(xiàn)索引的搜索編寫(xiě)了一個(gè)名為query的類,query實(shí)現(xiàn)索引的搜索有以下主要步驟:
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個(gè)主要的步驟:
①首先對(duì)搜索語(yǔ)句進(jìn)行判斷,若語(yǔ)句不是針對(duì)標(biāo)題或文章內(nèi)容進(jìn)行單一域的查詢,即不包含關(guān)鍵詞“title:”或“content:”時(shí)默認(rèn)搜索title和content兩個(gè)域。
②實(shí)例化一個(gè)SimpleFSDirectory對(duì)象,指定它的工作路徑為先前創(chuàng)建索引的路徑。
③實(shí)例化一個(gè)CJKAnalyzer分析器,搜索時(shí)使用的分析器應(yīng)與索引構(gòu)建時(shí)使用的分析器在類型版本上均一致。
④實(shí)例化一個(gè)IndexSearcher對(duì)象lucene_searcher,它的參數(shù)為第○2步的SimpleFSDirectory對(duì)象。
⑤實(shí)例化一個(gè)QueryParser對(duì)象my_query,它描述查詢請(qǐng)求,解析Query查詢語(yǔ)句。參數(shù)Version.LUCENE_CURRENT為pylucene的版本號(hào),“title”指默認(rèn)的搜索域,lucene_analyzer指定了使用的分析器,query_str是Query查詢語(yǔ)句。在實(shí)例化QueryParser前會(huì)對(duì)用戶搜索請(qǐng)求作簡(jiǎn)單處理,若用戶指定了搜索某個(gè)域就搜索該域,若用戶未指定則同時(shí)搜索“title”和“content”兩個(gè)域。
⑥lucene_searcher進(jìn)行搜索操作,返回結(jié)果集total_hits。total_hits中包含結(jié)果總數(shù)totalHits,搜索結(jié)果的文檔集scoreDocs,scoreDocs中包括搜索出的文檔以及每篇文檔與搜索語(yǔ)句相關(guān)度的得分。
⑦lucene_searcher搜索出的結(jié)果集不能直接被Python處理,因而在搜索操作返回結(jié)果之前應(yīng)將結(jié)果由Pylucene轉(zhuǎn)為普通的Python數(shù)據(jù)結(jié)構(gòu)。使用For循環(huán)依次處理每個(gè)結(jié)果,將結(jié)果文檔按相關(guān)度得分高低依次將它們的地址域“url”的值放入Python列表result_urls,將標(biāo)題域“title”的值放入列表result_titles。最后將包含地址、標(biāo)題的列表和結(jié)果總數(shù)組合成一個(gè)Python“字典”,將最后處理的結(jié)果作為整個(gè)搜索操作的返回值。
用戶在瀏覽器搜索框輸入搜索詞并點(diǎn)擊搜索,瀏覽器發(fā)起一個(gè)GET請(qǐng)求,F(xiàn)lask的路由route設(shè)置了由result函數(shù)響應(yīng)該請(qǐng)求。result函數(shù)先實(shí)例化一個(gè)搜索類query的對(duì)象infoso,將搜索詞傳遞給該對(duì)象,infoso完成搜索將結(jié)果返回給函數(shù)result。函數(shù)result將搜索出來(lái)的頁(yè)面和結(jié)果總數(shù)等傳遞給模板result.html,模板result.html用于呈現(xiàn)結(jié)果
如下是Python使用flask模塊處理搜索請(qǐng)求的代碼:
app = Flask(__name__)#創(chuàng)建Flask實(shí)例
@app.route('/')#設(shè)置搜索默認(rèn)主頁(yè)
def index():
html="<h1>title這是標(biāo)題</h1>"
return render_template('index.html')
@app.route("/result",methods=['GET', 'POST'])#注冊(cè)路由,并指定HTTP方法為GET、POST
def result(): #resul函數(shù)
if request.method=="GET":#響應(yīng)GET請(qǐng)求
key_word=request.args.get('word')#獲取搜索語(yǔ)句
if len(key_word)!=0:
infoso = query("./glxy") #創(chuàng)建查詢類query的實(shí)例
re = infoso.search(key_word)#進(jìn)行搜索,返回結(jié)果集
so_result=[]
n=0
for item in re["url"]:
temp_result={"url":item,"title":re["title"][n]}#將結(jié)果集傳遞給模板
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()#運(yùn)行web服務(wù)
以上這篇用python做一個(gè)搜索引擎(Pylucene)的實(shí)例代碼就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python使用BeautifulSoup和Scrapy抓取網(wǎng)頁(yè)數(shù)據(jù)的具體教程
在當(dāng)今信息爆炸的時(shí)代,數(shù)據(jù)無(wú)處不在,如何有效地抓取、處理和分析這些數(shù)據(jù)成為了許多開(kāi)發(fā)者和數(shù)據(jù)科學(xué)家的必修課,本篇博客將深入探討如何使用Python中的兩個(gè)強(qiáng)大工具:BeautifulSoup和Scrapy來(lái)抓取網(wǎng)頁(yè)數(shù)據(jù),需要的朋友可以參考下2025-01-01
pycharm+django創(chuàng)建一個(gè)搜索網(wǎng)頁(yè)實(shí)例代碼
這篇文章主要介紹了pycharm+django創(chuàng)建一個(gè)搜索網(wǎng)頁(yè)實(shí)例代碼,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
Python使用Rich實(shí)現(xiàn)美化終端顯示效果
Rich庫(kù)的功能就像它的名字一樣,使Python編程更加豐富(rich),用來(lái)幫助開(kāi)發(fā)者在控制臺(tái)(命令行)輸出中創(chuàng)建豐富、多彩和具有格式化的文本,下面我們就來(lái)了解下它的具體使用吧2024-02-02
新手如何發(fā)布Python項(xiàng)目開(kāi)源包過(guò)程詳解
這篇文章主要介紹了新手如何發(fā)布Python項(xiàng)目開(kāi)源包過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
pymysql實(shí)現(xiàn)增刪改查的操作指南(python)
python中可以使用pymysql來(lái)MySQL數(shù)據(jù)庫(kù)的連接,并實(shí)現(xiàn)數(shù)據(jù)庫(kù)的各種操作,這篇文章主要給大家介紹了關(guān)于pymsql實(shí)現(xiàn)增刪改查的相關(guān)資料,需要的朋友可以參考下2021-05-05
簡(jiǎn)單的Python人臉識(shí)別系統(tǒng)
這篇文章主要介紹了Python人臉識(shí)別系統(tǒng)的實(shí)現(xiàn),文中講解非常詳細(xì),代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07

