利用elasticsearch實現搜尋引擎
ElasticSearch是一個基於Lucene的搜尋伺服器。它提供了一個分散式多使用者能力的全文搜尋引擎,基於RESTful web介面。Elasticsearch是用Java開發的,並作為Apache許可條款下的開放原始碼釋出,是第二最流行的企業搜尋引擎。設計用於雲端計算中,能夠達到實時搜尋,穩定,可靠,快速,安裝使用方便。本篇是在windows平臺下的運用
起步
1. 安裝jdk
2. 安裝elasticsearch-rtf
解壓後,在bin目錄下開啟命令列工具,輸入elasticsearch.bat執行指令碼,開啟瀏覽器,在位址列輸入http://127.0.0.1:9200
3.安裝elasticsearch-head
這是(搜尋引擎)的視覺化管理工具,安裝要用到node.js的npm 外掛管理器,所以要先安裝node.js的npm 外掛管理器。
(1)安裝完後後,開啟命令列工具
執行命令 npm
npm命令是node.js的npm外掛管理器,也就是下載外掛和安裝外掛的管理器,國外映象很慢可能會掉線,我們使用淘寶的npm映象cnpm
執行命令:npm install -g cnpm --registry=https://registry.npm.taobao.org
啟用淘寶的npm映象cnpm,注意:啟用後當我們要輸入npm命令時,就需要輸入cnpm
(2)(搜尋引擎)的視覺化管理工具elasticsearch-head的安裝
下載後解壓到指定目錄
(3)cd進入到解壓的elasticsearch-head目錄,安裝elasticsearch-head的依賴包
執行命令:cnpm install
(4)啟動elasticsearch-head(搜尋引擎)的視覺化管理工具,訪問http://localhost:9100/,就可以看到(搜尋引擎)的視覺化管理工具。
執行命令:cnpm run start
點選連線會發現是灰色的未連線狀態,這是因為elsaticsearch的安全策略,在5.0之後的版本不允許第三方外掛直接連線,需要修改配置檔案,在config資料夾下字尾為yml的檔案中新增以下配置:
http.cors.enabled: true
http.cors.allow-origin: "*"
重啟服務,點選連線,即可成功
應用
elasticsearch的詳細內容還請參閱相關文件,接下來會直接貼上我在使用elasticsearch來實現搜尋引擎時的步驟和一些注意事項。搜尋引擎是為了資料而存在的,所以你需要準備一個數據源,因此我用scrapy實現了一個簡單的爬蟲。
在使用前,為了準備資料要先啟動爬蟲將資料提交到elasticsearch,如感興趣具體可前往https://github.com/xinyan818/SimpleSearch-TST
1.宣告doc_type
doc_type在elasticsearch中類似關係型資料庫中的資料表,我們需要實現它。
# 資料型別
from elasticsearch_dsl import DocType, Completion, Keyword, Text, Boolean, Integer, Date
# 引入連結函式
from elasticsearch_dsl.connections import connections
# 引入elasticsearch中的分析器
from elasticsearch_dsl.analysis import CustomAnalyzer
# 建立Es連結
connections.create_connection(hosts=["127.0.0.1"])
# 自定義分詞器
class MyAnalyzer(CustomAnalyzer):
def get_analysis_definition(self):
return {}
# 建立分析器物件
# 忽略大小寫的篩選器
ik_analyzer = MyAnalyzer('ik_max_word', filter=['lowercase'])
class NewsType(DocType):
# 搜尋建議欄位
# Completion 用來做搜尋建議的型別
# 不能直接指定分詞器名,需要指定一個自定義分詞器
suggest = Completion(analyzer=ik_analyzer)
# 分詞
title = Text(analyzer="ik_max_word")
category = Text()
f_url = Text()
intro = Text(analyzer="ik_max_word")
source = Text()
time = Date()
# Meta
class Meta:
# 索引名稱
index = 'news'
doc_type = 'fashion'
if __name__ == '__main__':
NewsType.init()
執行這個python檔案即可在服務中宣告一個名為news的index,和在其之下名為fashion的type,
2.搜尋結果的檢視函式
#搜尋結果頁面
def result(request):
if request.method == 'GET':
# 取出關鍵詞 搜尋型別 頁碼
keyword = request.GET.get('kw', None)
s_type = request.GET.get('s_type', 'blog')
page_num = request.GET.get('pn', 1)
# 沒有關鍵詞,定向到首頁
if not keyword:
return redirect('/')
# 判斷搜尋型別
# 搜尋
if s_type == 'news':
# 1. 搜尋的索引
index = 'news'
doc_type = 'fashion'
fields = ['title', 'intro']
start = datetime.now()
rs = es.search(
index=index,
doc_type=doc_type,
body={
'query': {
'multi_match': {
'query': keyword,
'fields': fields
}
},
'from': (int(page_num)-1)*10,
'size': 10,
'highlight': {
'pre_tags': ['<span class="KeyWord">'],
'post_tags': ['</span>'],
'fields': {
'title': {},
'intro': {}
}
}
}
)
# 搜尋花費時間
# total_seconds() 統計秒數
use_time = (datetime.now() - start).total_seconds()
hits_list = []
for hit in rs['hits']['hits']:
try:
h_dic = {}
# 判斷highlight中有沒有title
if 'title' in hit['highlight'].keys():
h_dic['title'] = hit['highlight']['title'][0]
else:
h_dic['title'] = hit['_source']['title']
# 再判斷intro
if 'intro' in hit['highlight'].keys():
intro_list = hit['highlight']['intro']
intro_list.reverse()
h_dic['content'] = ''.join(intro_list)
else:
h_dic['content'] = hit['_source']['intro']
# 詳情地址
h_dic['detail_url'] = hit['_source']['f_url']
except:
continue
hits_list.append(h_dic)
navs = NAVS
# 計算頁碼
total = rs['hits']['total']
page_nums = int(math.ceil(total/10))
page_num = int(page_num)
if page_num < 6:
if page_nums <= 10:
pages = range(1, page_nums+1)
else:
pages = range(1, 11)
elif (page_num >= 6) and (page_num <= page_nums - 5):
pages = range(page_num - 5, page_num + 5)
else:
if page_nums <= 10:
pages = range(1, page_nums+1)
else:
pages = range(page_nums-9, page_nums + 1)
data = {
'navs': navs,
'search_type': s_type,
'hits_list': hits_list,
'kw': keyword,
'pages': pages,
'page_nums': page_nums,
'pn': page_num,
'total': total,
'use_time': use_time
}
return render(request, 'result.html', data)