Whoosh搜尋引擎
whoosh 是一個純python實作的全文搜尋引擎,它小巧輕便,安裝簡單,設計上參考了 Lucene ,性能上雖有欠缺,但貴在友善,無需複雜安裝,對于建構小型網站的搜尋引擎來說,是一個不錯的選擇。
1. 快速入門
whoosh 可以使用pip進行安裝
pip install whoosh
現在通過官網的例子,快速體驗
from whoosh.index import create_in
from whoosh.fields import *
# 建構索引
schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT)
ix = create_in("indexdir", schema)
writer = ix.writer()
writer.add_document(title=u"First document", path=u"/a",content=u"This is the first document we've added!")
writer.add_document(title=u"Second document", path=u"/b", content=u"The second one is even more interesting!")
writer.commit()
# 搜尋
from whoosh.qparser import QueryParser
with ix.searcher() as searcher:
query = QueryParser("content", ix.schema).parse("first")
results = searcher.search(query)
print(results[0])
程式最終輸出結果為
官網上的例子,我沒有做任何修改,隻是添加了兩行注釋。整個程式分為兩部分,第一部分是建構索引的過程,第二部分是搜尋的過程。
2. 建構索引
2.1 反向索引
搜尋引擎的關鍵技術是建立反向索引,反向索引記錄了哪些文檔中包含了某個單詞,比如 “酷python” 這個詞出現在了你正在看的這篇文章中,假設這篇文章的編号是111, 那麼索引中就會記錄一條 酷python:111的記錄。當你搜尋 酷python 這個詞的時候,搜尋引擎從反向索引中找到 酷python所對應的文檔,如果有多個,搜尋引擎則計算文檔與搜尋詞的相關性,并根據相關性進行排序傳回給你結果。
2.2 分詞
我們在搜尋時,所搜尋的關鍵詞可能是一個句子,文檔裡那麼多内容,但索引隻記錄詞與文檔編号之間的映射關系,是以,不論是建構索引還是根據關鍵詞進行搜尋,都得進行分詞。對于英國文檔,分詞是一件簡單的事情,因為英語的句子是由若幹個單次組成的。而中文的分詞則相對複雜,因為我們的詞是由單個漢字組成的,而詞與詞之間是沒有空格這種明顯的分界的,具體哪幾個漢字組成一個詞,要看所處的語境,比如 “ 軍任命了一名中将 ”, 這裡中将就是一個詞,但在句子“ 産量三年中将增長兩倍 ”, 中将 就不再是一個詞。
但你大可不必擔心,因為現在的中文分詞技術已經非常成熟了,開源庫jieba可以滿足你絕大部分需求。
2.3 索引模式
現在要為100篇文章建構索引,一篇文章的資訊可能包括 文章标題,内容,作者,在建構索引的時候,你需要定義索引模式,就如同定義一張mysql裡的表,你需要指出需要存儲哪些字段,以及這些字段的類型
from whoosh.fields import TEXT, SchemaClass
from jieba.analyse import ChineseAnalyzer
analyzer = ChineseAnalyzer()
class ArticleSchema(SchemaClass):
title = TEXT(stored=True, analyzer=analyzer)
content = TEXT(stored=True, analyzer=analyzer)
author = TEXT(stored=True, analyzer=analyzer)
與官網中的例子不同,我通過繼承SchemaClass 來實作一個新的類,以此定義索引模式。而且我設定了analyzer 為ChineseAnalyzer, 這樣whoosh就可以支援中文索引了,analyzer會對文檔中的中文進行分詞。
2.4 添加文檔
schema = ArticleSchema()
ix = create_in("indexdir", schema, indexname='article_index')
writer = ix.writer()
writer.add_document(title="登鹳雀樓", author="王之渙",content="白日依山盡,黃河入海流,欲窮千裡目,更上一層樓")
writer.add_document(title="登高", author="杜甫", content="風急天高猿嘯哀,渚清沙白鳥飛回")
writer.add_document(title="胡亂寫的", author="黃河戀", content="展示效果")
writer.commit()
create_in 會建立一個名為indexdir 的檔案夾,添加文檔時,一定要根據你所定義的索引模式進行添加,這樣就建立好了索引,添加文檔的過程,就如同向mysql的表裡寫入資料。
3. 搜尋
搜尋的過程,需要使用open_dir函數打開索引檔案,建立Searcher 對象
from whoosh.qparser import QueryParser
from whoosh.index import open_dir
ix = open_dir("indexdir", indexname='article_index')
with ix.searcher() as searcher:
query = QueryParser("content", ix.schema).parse("黃河")
results = searcher.search(query)
print(results[0])
程式輸出結果
3.1 高亮顯示
我們在百度搜尋引擎搜尋關鍵詞所得到的結果,那些與關鍵詞比對的部分會被高亮顯示,這樣友善使用者檢視内容,這個功能,whoosh同樣支援
with ix.searcher() as searcher:
query = QueryParser("content", ix.schema).parse("黃河")
results = searcher.search(query)
data = results[0]
text = data.highlights("content")
print(text)
程式輸出結果為
白日依山盡,
黃河入海流,欲窮千裡目
在html檔案中,你可以自己來定義match 和 term0 的樣式。
3.2 多個字段同時搜尋
對多個字段同時搜尋,需要使用MultifieldParser
from whoosh.qparser import QueryParser, MultifieldParser
from whoosh.index import open_dir
ix = open_dir("indexdir", indexname='article_index')
with ix.searcher() as searcher:
query = MultifieldParser(["content", 'author'], ix.schema).parse("黃河")
results = searcher.search(query)
for data in results:
print(data)
content中有黃河,或者author有黃河的文檔,都可以被搜尋出來,程式輸出結果
3.3 多個關鍵詞同時搜尋
如果你所搜尋的内容并不僅僅是一個關鍵詞,而是多個,或者你搜尋的是一個句子,搜尋引擎會把你的句子進行分詞,得到若幹個詞,這些詞作為條件進行搜尋,隻有被搜尋的字段同時滿足這些關鍵詞時,才能得到搜尋結果,比如下面的搜尋
query = MultifieldParser(["content", 'author'], ix.schema).parse("黃河 杜甫")
這個搜尋條件不會得到任何結果,原因在于搜尋條件等價于
((content:黃河 OR author:黃河) AND (content:杜甫 OR author:杜甫))
被搜尋的字段中,比如同時包含黃河與杜甫。如果你希望這些關鍵詞之間是或的關系,那麼需要你自己來建構搜尋條件
from whoosh.qparser import QueryParser, MultifieldParser
from whoosh.index import open_dir
from whoosh.query import compound, Term
ix = open_dir("indexdir", indexname='article_index')
with ix.searcher() as searcher:
author_query = [Term('author', '黃河'), Term('author', '杜甫')]
content_query = [Term('content', '黃河'), Term('content', '杜甫')]
query = compound.Or([compound.Or(author_query), compound.Or(content_query)])
print(query)
results = searcher.search(query)
for data in results:
print(data)
三個文檔都會被搜尋到, 如果你搜尋的是一個句子,那麼你可以使用analyzer 對整個句子進行分詞,然後構造搜尋條件,我所說的analyzer就是 analyzer = ChineseAnalyzer() 語句建立的對象。
3.4 分頁搜尋
如果搜尋結果太多,那麼你需要分頁查詢
results = searcher.search_page(query, 1) # 搜尋第1頁,預設每頁10個結果
print(results.total) # 搜尋到的文檔總量,幫助你進行分頁
你擷取的是第一頁的搜尋結果,但results.total 會告訴你搜尋結果一共有多少條,這樣,你就知道該搜尋多少頁的資料了。