基於 Elasticsearch 的站內搜索引擎實戰

站內搜索,能夠認爲是針對一個網站特性內容的搜索功能。因爲內容、格式可控,站內搜索比全網搜索的實現要簡單不少。html

簡書這個網站自己自帶一個搜索,可是缺少針對我的文章的搜索,因此本文的實戰內容是解決這個痛點。python

代碼在 github.com/letiantian/…,可使用下面的方式把代碼下載下來查看:git

git clone https://github.com/letiantian/jianshu-site-search.git
複製代碼

代碼在Python2.7下運行。須要安裝如下依賴:github

pip install elasticsearch==6.0.0 --user
pip install uniout --user
pip install requests --user
pip install beautifulsoup4 --user
pip install Django --user
複製代碼

1. 數據源

若是是簡書給本身作我的搜索,從數據庫裏拿就好了。web

我這種狀況,天然用爬蟲抓取。數據庫

1.1 抓取什麼內容?

抓取某我的全部的文章,最終是URL標題正文三個部分。瀏覽器

1.2 如何抓取?

www.jianshu.com/u/7fe2e7bb7…這個(隨便找的)用戶主頁爲例。7fe2e7bb7d47能夠認爲是這個用戶的ID。 文章地址相似http://www.jianshu.com/p/9c2fdb9fa5d19c2fdb9fa5d1是文章 ID。bash

通過分析,能夠以此請求下面的地址,從中解析出文章地址,獲得地址集合:elasticsearch

http://www.jianshu.com/u/7fe2e7bb7d47?order_by=shared_at&page=1
http://www.jianshu.com/u/7fe2e7bb7d47?order_by=shared_at&page=2
http://www.jianshu.com/u/7fe2e7bb7d47?order_by=shared_at&page=3
// ... page的值不斷增長
// ... 當page不存在的時候,簡書會返回page=1的內容,這時候中止抓取
複製代碼

而後,依次抓取文章內容,保存下來。分佈式

crawler.py 用於抓取文章,使用方法:

python crawler.py 7fe2e7bb7d47
複製代碼

文章對應的網頁會保存到data目錄,用文章ID命名。

2. 最簡單的搜索引擎實現

對於每一個搜索詞查看每一個文章的標題和正文中有無該詞:

  1. 標題中有該搜索詞,爲該文章加2分。
  2. 正文中有該搜索詞,爲該文章加1分。

一篇文章命中的搜索詞越多,分值越高。

將結果排序輸出便可。

代碼實如今simple_search.py ,使用方法:

$ python simple_search.py 人民 名義 
你輸入了: 人民 名義
搜索結果:

url:    http://www.jianshu.com/p/6659d5fc5503
title:  《人民的名義》走紅的背後 文化產業投資難以言說的痛
score:  6

url:    http://www.jianshu.com/p/ee594ea42815
title:  LP由《人民的名義》反思 GP投資權力真空怎麼破
score:  6

url:    http://www.jianshu.com/p/4ef650769f73
title:  弘道資本:投資人人貸、ofo 人民幣基金逆襲的中國樣本
score:  3
複製代碼

這種方法的缺點是:

  • 由於是遍歷每一個文章,文章變多後,速度會變慢
  • 搜索結果排序不理想
  • 沒有引入中文分詞特性

3. 基於 Elasticsearch 的實現

Elasticsearch 是一個通用的搜索引擎解決方案,提供了優雅的 HTTP Restful 接口、豐富的官方文檔。 阮一峯爲它寫了一份簡明易懂的教程:全文搜索引擎 Elasticsearch 入門教程,推薦閱讀。

Elasticsearch 基本原理:

  1. 對搜索內容進行分詞,獲得若干搜索詞。
  2. 經過倒排索引找到含有搜索詞的文章集合。
  3. 經過TF-IDF、餘弦類似性計算文章集合中每一個文章和搜索內容的類似性。
  4. 根據類似性進行排序,獲得搜索結果。

3.1 環境搭建

咱們先搭建環境:

  1. 安裝Java。
  2. 官網下載最新的 6.0.0 版本,解壓。
  3. 安裝ik分詞插件。
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.0.0/elasticsearch-analysis-ik-6.0.0.zip
複製代碼

或者下載下來解壓到Elasticsearch的plugins目錄。 4. 啓動:

./bin/elasticsearch
複製代碼

環境搭建完成。

3.2 建立索引

python es_create_index.py
複製代碼

建立時指定了分詞器。

3.3 索引數據

python es_index_data.py
複製代碼

爲了防止一篇文章被重複索引,添加索引時 Document ID 設置爲文章 ID。

3.4 搜索

python es_search.py 人民的名義
複製代碼

高亮搜索結果:

python es_hl_search.py 人民的名義
複製代碼

3.5 基於web的搜索

基於Django實現了一個簡單的web界面。運行:

python webui/manage.py runserver
複製代碼

瀏覽器訪問http://127.0.0.1:8000/便可。體驗效果:

4. 關於 Elasticsearch 的一些思考

4.1 如何看待中止詞?

中止詞是很是常見的單詞,例如the等。通常用法是在分詞後去掉中止詞,而後進行索引。這種作法的常見理由是減小索引大小。同時,從理論上看,也能夠提高檢索速度。 相應的,這裏有兩個問題須要探討:

  1. 索引大小的減少量是什麼數量級,若是隻是減小了1%,這種優化並沒有必要。
  2. 檢索速度的提高是什麼數量級,若是隻是提高1%,說服力並不大。

是否能達到業務的需求才是目標。若是須要在搜索這個詞的時候有結果,那麼上面的作法就是不合理的。

我更傾向於底層索引不啓用中止詞,而是根據業務需求在業務層進行必要的中止詞處理。

4.2 防止深度搜索

要Elasticsearch返回搜索結果的第10001條到第10010條數據,是一個耗時的操做,由於Elasticsearch要先獲得打分最高的前10010條數據,而後從中取出第10001條到第10010條數據。

用戶感知到的搜索界面是分頁的,每頁是固定數量的數據(如10條),用戶會跳轉到第1001頁的搜索結果嗎?不會。第1001頁的搜索結果有意義嗎?沒有意義,用戶應該調整搜索詞。

綜上,應限制用戶獲得的搜索結果數量。

4.3 處理海量數據

本文的示例的數據由一個用戶的全部文章組成,數據量很小。若是簡書全站搜索也是用Elasticsearch,它能處理好嗎?

事實上,Elasticsearch 支持分片和分佈式部署,能近實時的處理海量數據。注意,索引耗時會很大,可是搜索很快。

4.4 如何在搜索結果中加入推廣內容

推廣內容自己也能夠被Elasticsearch索引發來,根據狀況插入搜索結果中就好了。

本文發佈於樂天的開發筆記-掘金,同時發佈於樂天筆記

相關文章
相關標籤/搜索