想查數據就免不了搜索,搜索就離不開搜索引擎,百度、谷歌都是一個很是龐大複雜的搜索引擎,他們幾乎索引了互聯網上開放的全部網頁和數據。然而對於咱們本身的業務數據來講,確定就不必用這麼複雜的技術了,若是咱們想實現本身的搜索引擎,方便存儲和檢索,Elasticsearch 就是不二選擇,它是一個全文搜索引擎,能夠快速地儲存、搜索和分析海量數據。html
Elasticsearch 是一個開源的搜索引擎,創建在一個全文搜索引擎庫 Apache Lucene™ 基礎之上。git
那 Lucene 又是什麼?Lucene 多是目前存在的,不論開源仍是私有的,擁有最早進,高性能和全功能搜索引擎功能的庫,但也僅僅只是一個庫。要用上 Lucene,咱們須要編寫 Java 並引用 Lucene 包才能夠,並且咱們須要對信息檢索有必定程度的理解才能明白 Lucene 是怎麼工做的,反正用起來沒那麼簡單。github
那麼爲了解決這個問題,Elasticsearch 就誕生了。Elasticsearch 也是使用 Java 編寫的,它的內部使用 Lucene 作索引與搜索,可是它的目標是使全文檢索變得簡單,至關於 Lucene 的一層封裝,它提供了一套簡單一致的 RESTful API 來幫助咱們實現存儲和檢索。數據庫
因此 Elasticsearch 僅僅就是一個簡易版的 Lucene 封裝嗎?那就大錯特錯了,Elasticsearch 不只僅是 Lucene,而且也不只僅只是一個全文搜索引擎。 它能夠被下面這樣準確的形容:json
一個分佈式的實時文檔存儲,每一個字段能夠被索引與搜索瀏覽器
一個分佈式實時分析搜索引擎bash
能勝任上百個服務節點的擴展,並支持 PB 級別的結構化或者非結構化數據服務器
總之,是一個至關牛逼的搜索引擎,維基百科、Stack Overflow、GitHub 都紛紛採用它來作搜索。網絡
咱們能夠到 Elasticsearch 的官方網站下載 Elasticsearch:https://www.elastic.co/downloads/elasticsearch,同時官網也附有安裝說明。app
首先把安裝包下載下來並解壓,而後運行 bin/elasticsearch
(Mac 或 Linux)或者 bin\elasticsearch.bat
(Windows) 便可啓動 Elasticsearch 了。
我使用的是 Mac,Mac 下我的推薦使用 Homebrew 安裝:
brew install elasticsearch
複製代碼
Elasticsearch 默認會在 9200 端口上運行,咱們打開瀏覽器訪問
http://localhost:9200/ 就能夠看到相似內容:
{
"name" : "atntrTf",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "e64hkjGtTp6_G2h1Xxdv5g",
"version" : {
"number": "6.2.4",
"build_hash": "ccec39f",
"build_date": "2018-04-12T20:37:28.497551Z",
"build_snapshot": false,
"lucene_version": "7.2.1",
"minimum_wire_compatibility_version": "5.6.0",
"minimum_index_compatibility_version": "5.0.0"
},
"tagline" : "You Know, for Search"
}
複製代碼
若是看到這個內容,就說明 Elasticsearch 安裝並啓動成功了,這裏顯示個人 Elasticsearch 版本是 6.2.4 版本,版本很重要,之後安裝一些插件都要作到版本對應才能夠。
接下來咱們來了解一下 Elasticsearch 的基本概念以及和 Python 的對接。
在 Elasticsearch 中有幾個基本的概念,如節點、索引、文檔等等,下面來分別說明一下,理解了這些概念對熟悉 Elasticsearch 是很是有幫助的。
Elasticsearch 本質上是一個分佈式數據庫,容許多臺服務器協同工做,每臺服務器能夠運行多個 Elasticsearch 實例。
單個 Elasticsearch 實例稱爲一個節點(Node)。一組節點構成一個集羣(Cluster)。
Elasticsearch 會索引全部字段,通過處理後寫入一個反向索引(Inverted Index)。查找數據的時候,直接查找該索引。
因此,Elasticsearch 數據管理的頂層單位就叫作 Index(索引),其實就至關於 MySQL、MongoDB 等裏面的數據庫的概念。另外值得注意的是,每一個 Index (即數據庫)的名字必須是小寫。
Index 裏面單條的記錄稱爲 Document(文檔)。許多條 Document 構成了一個 Index。
Document 使用 JSON 格式表示,下面是一個例子。
同一個 Index 裏面的 Document,不要求有相同的結構(scheme),可是最好保持相同,這樣有利於提升搜索效率。
Document 能夠分組,好比 weather 這個 Index 裏面,能夠按城市分組(北京和上海),也能夠按氣候分組(晴天和雨天)。這種分組就叫作 Type,它是虛擬的邏輯分組,用來過濾 Document,相似 MySQL 中的數據表,MongoDB 中的 Collection。
不一樣的 Type 應該有類似的結構(Schema),舉例來講,id 字段不能在這個組是字符串,在另外一個組是數值。這是與關係型數據庫的表的一個區別。性質徹底不一樣的數據(好比 products 和 logs)應該存成兩個 Index,而不是一個 Index 裏面的兩個 Type(雖然能夠作到)。
根據規劃,Elastic 6.x 版只容許每一個 Index 包含一個 Type,7.x 版將會完全移除 Type。
即字段,每一個 Document 都相似一個 JSON 結構,它包含了許多字段,每一個字段都有其對應的值,多個字段組成了一個 Document,其實就能夠類比 MySQL 數據表中的字段。
在 Elasticsearch 中,文檔歸屬於一種類型(Type),而這些類型存在於索引(Index)中,咱們能夠畫一些簡單的對比圖來類比傳統關係型數據庫:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
複製代碼
以上就是 Elasticsearch 裏面的一些基本概念,經過和關係性數據庫的對比更加有助於理解。
Elasticsearch 實際上提供了一系列 Restful API 來進行存取和查詢操做,咱們可使用 curl 等命令來進行操做,但畢竟命令行模式沒那麼方便,因此這裏咱們就直接介紹利用 Python 來對接 Elasticsearch 的相關方法。
Python 中對接 Elasticsearch 使用的就是一個同名的庫,安裝方式很是簡單:
pip3 install elasticsearch
複製代碼
官方文檔是:https://elasticsearch-py.readthedocs.io/,全部的用法均可以在裏面查到,文章後面的內容也是基於官方文檔來的。
咱們先來看下怎樣建立一個索引(Index),這裏咱們建立一個名爲 news 的索引:
from elasticsearch import Elasticsearch
es = Elasticsearch()
result = es.indices.create(index='news', ignore=400)
print(result)
複製代碼
若是建立成功,會返回以下結果:
{'acknowledged': True, 'shards_acknowledged': True, 'index': 'news'}
複製代碼
返回結果是 JSON 格式,其中的 acknowledged 字段表示建立操做執行成功。
但這時若是咱們再把代碼執行一次的話,就會返回以下結果:
{'error': {'root_cause': [{'type': 'resource_already_exists_exception', 'reason': 'index [news/QM6yz2W8QE-bflKhc5oThw] already exists', 'index_uuid': 'QM6yz2W8QE-bflKhc5oThw', 'index': 'news'}], 'type': 'resource_already_exists_exception', 'reason': 'index [news/QM6yz2W8QE-bflKhc5oThw] already exists', 'index_uuid': 'QM6yz2W8QE-bflKhc5oThw', 'index': 'news'}, 'status': 400}
複製代碼
它提示建立失敗,status 狀態碼是 400,錯誤緣由是 Index 已經存在了。
注意這裏咱們的代碼裏面使用了 ignore 參數爲 400,這說明若是返回結果是 400 的話,就忽略這個錯誤不會報錯,程序不會執行拋出異常。
假如咱們不加 ignore 這個參數的話:
es = Elasticsearch()
result = es.indices.create(index='news')
print(result)
複製代碼
再次執行就會報錯了:
raise HTTP_EXCEPTIONS.get(status_code, TransportError)(status_code, error_message, additional_info)
elasticsearch.exceptions.RequestError: TransportError(400, 'resource_already_exists_exception', 'index [news/QM6yz2W8QE-bflKhc5oThw] already exists')
複製代碼
這樣程序的執行就會出現問題,因此說,咱們須要善用 ignore 參數,把一些意外狀況排除,這樣能夠保證程序的正常執行而不會中斷。
刪除 Index 也是相似的,代碼以下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
result = es.indices.delete(index='news', ignore=[400, 404])
print(result)
複製代碼
這裏也是使用了 ignore 參數,來忽略 Index 不存在而刪除失敗致使程序中斷的問題。
若是刪除成功,會輸出以下結果:
{'acknowledged': True}
複製代碼
若是 Index 已經被刪除,再執行刪除則會輸出以下結果:
{'error': {'root_cause': [{'type': 'index_not_found_exception', 'reason': 'no such index', 'resource.type': 'index_or_alias', 'resource.id': 'news', 'index_uuid': '_na_', 'index': 'news'}], 'type': 'index_not_found_exception', 'reason': 'no such index', 'resource.type': 'index_or_alias', 'resource.id': 'news', 'index_uuid': '_na_', 'index': 'news'}, 'status': 404}
複製代碼
這個結果代表當前 Index 不存在,刪除失敗,返回的結果一樣是 JSON,狀態碼是 400,可是因爲咱們添加了 ignore 參數,忽略了 400 狀態碼,所以程序正常執行輸出 JSON 結果,而不是拋出異常。
Elasticsearch 就像 MongoDB 同樣,在插入數據的時候能夠直接插入結構化字典數據,插入數據能夠調用 create() 方法,例如這裏咱們插入一條新聞數據:
from elasticsearch import Elasticsearch
es = Elasticsearch()
es.indices.create(index='news', ignore=400)
data = {'title': '美國留給伊拉克的是個爛攤子嗎', 'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm'}
result = es.create(index='news', doc_type='politics', id=1, body=data)
print(result)
複製代碼
這裏咱們首先聲明瞭一條新聞數據,包括標題和連接,而後經過調用 create() 方法插入了這條數據,在調用 create() 方法時,咱們傳入了四個參數,index 參數表明了索引名稱,doc_type 表明了文檔類型,body 則表明了文檔具體內容,id 則是數據的惟一標識 ID。
運行結果以下:
{'_index': 'news', '_type': 'politics', '_id': '1', '_version': 1, 'result': 'created', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 0, '_primary_term': 1}
複製代碼
結果中 result 字段爲 created,表明該數據插入成功。
另外其實咱們也可使用 index() 方法來插入數據,但與 create() 不一樣的是,create() 方法須要咱們指定 id 字段來惟一標識該條數據,而 index() 方法則不須要,若是不指定 id,會自動生成一個 id,調用 index() 方法的寫法以下:
es.index(index='news', doc_type='politics', body=data)
複製代碼
create() 方法內部其實也是調用了 index() 方法,是對 index() 方法的封裝。
更新數據也很是簡單,咱們一樣須要指定數據的 id 和內容,調用 update() 方法便可,代碼以下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
data = {
'title': '美國留給伊拉克的是個爛攤子嗎',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2011-12-16'
}
result = es.update(index='news', doc_type='politics', body=data, id=1)
print(result)
複製代碼
這裏咱們爲數據增長了一個日期字段,而後調用了 update() 方法,結果以下:
{'_index': 'news', '_type': 'politics', '_id': '1', '_version': 2, 'result': 'updated', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 1, '_primary_term': 1}
複製代碼
能夠看到返回結果中,result 字段爲 updated,即表示更新成功,另外咱們還注意到有一個字段 _version,這表明更新後的版本號數,2 表明這是第二個版本,由於以前已經插入過一次數據,因此第一次插入的數據是版本 1,能夠參見上例的運行結果,此次更新以後版本號就變成了 2,之後每更新一次,版本號都會加 1。
另外更新操做其實利用 index() 方法一樣能夠作到,寫法以下:
es.index(index='news', doc_type='politics', body=data, id=1)
複製代碼
能夠看到,index() 方法能夠代替咱們完成兩個操做,若是數據不存在,那就執行插入操做,若是已經存在,那就執行更新操做,很是方便。
若是想刪除一條數據能夠調用 delete() 方法,指定須要刪除的數據 id 便可,寫法以下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
result = es.delete(index='news', doc_type='politics', id=1)
print(result)
複製代碼
運行結果以下:
{'_index': 'news', '_type': 'politics', '_id': '1', '_version': 3, 'result': 'deleted', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 2, '_primary_term': 1}
複製代碼
能夠看到運行結果中 result 字段爲 deleted,表明刪除成功,_version 變成了 3,又增長了 1。
上面的幾個操做都是很是簡單的操做,普通的數據庫如 MongoDB 都是能夠完成的,看起來並無什麼了不得的,Elasticsearch 更特殊的地方在於其異常強大的檢索功能。
對於中文來講,咱們須要安裝一個分詞插件,這裏使用的是 elasticsearch-analysis-ik,GitHub 連接爲:https://github.com/medcl/elasticsearch-analysis-ik,這裏咱們使用 Elasticsearch 的另外一個命令行工具 elasticsearch-plugin 來安裝,這裏安裝的版本是 6.2.4,請確保和 Elasticsearch 的版本對應起來,命令以下:
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip
複製代碼
這裏的版本號請替換成你的 Elasticsearch 的版本號。
安裝以後從新啓動 Elasticsearch 就能夠了,它會自動加載安裝好的插件。
首先咱們新建一個索引並指定須要分詞的字段,代碼以下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
mapping = {
'properties': {
'title': {
'type': 'text',
'analyzer': 'ik_max_word',
'search_analyzer': 'ik_max_word'
}
}
}
es.indices.delete(index='news', ignore=[400, 404])
es.indices.create(index='news', ignore=400)
result = es.indices.put_mapping(index='news', doc_type='politics', body=mapping)
print(result)
複製代碼
這裏咱們先將以前的索引刪除了,而後新建了一個索引,而後更新了它的 mapping 信息,mapping 信息中指定了分詞的字段,指定了字段的類型 type 爲 text,分詞器 analyzer 和 搜索分詞器 search_analyzer 爲 ik_max_word,即便用咱們剛纔安裝的中文分詞插件。若是不指定的話則使用默認的英文分詞器。
接下來咱們插入幾條新的數據:
datas = [
{
'title': '美國留給伊拉克的是個爛攤子嗎',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2011-12-16'
},
{
'title': '公安部:各地校車將享最高路權',
'url': 'http://www.chinanews.com/gn/2011/12-16/3536077.shtml',
'date': '2011-12-16'
},
{
'title': '中韓漁警衝突調查:韓警平均天天扣1艘中國漁船',
'url': 'https://news.qq.com/a/20111216/001044.htm',
'date': '2011-12-17'
},
{
'title': '中國駐洛杉磯領事館遭亞裔男子槍擊 嫌犯已自首',
'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
'date': '2011-12-18'
}
]
for data in datas:
es.index(index='news', doc_type='politics', body=data)
複製代碼
這裏咱們指定了四條數據,都帶有 title、url、date 字段,而後經過 index() 方法將其插入 Elasticsearch 中,索引名稱爲 news,類型爲 politics。
接下來咱們根據關鍵詞查詢一下相關內容:
result = es.search(index='news', doc_type='politics')
print(result)
複製代碼
能夠看到查詢出了全部插入的四條數據:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 1.0,
"hits": [
{
"_index": "news",
"_type": "politics",
"_id": "c05G9mQBD9BuE5fdHOUT",
"_score": 1.0,
"_source": {
"title": "美國留給伊拉克的是個爛攤子嗎",
"url": "http://view.news.qq.com/zt2011/usa_iraq/index.htm",
"date": "2011-12-16"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dk5G9mQBD9BuE5fdHOUm",
"_score": 1.0,
"_source": {
"title": "中國駐洛杉磯領事館遭亞裔男子槍擊,嫌犯已自首",
"url": "http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml",
"date": "2011-12-18"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dU5G9mQBD9BuE5fdHOUj",
"_score": 1.0,
"_source": {
"title": "中韓漁警衝突調查:韓警平均天天扣1艘中國漁船",
"url": "https://news.qq.com/a/20111216/001044.htm",
"date": "2011-12-17"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dE5G9mQBD9BuE5fdHOUf",
"_score": 1.0,
"_source": {
"title": "公安部:各地校車將享最高路權",
"url": "http://www.chinanews.com/gn/2011/12-16/3536077.shtml",
"date": "2011-12-16"
}
}
]
}
}
複製代碼
能夠看到返回結果會出如今 hits 字段裏面,而後其中有 total 字段標明瞭查詢的結果條目數,還有 max_score 表明了最大匹配分數。
另外咱們還能夠進行全文檢索,這纔是體現 Elasticsearch 搜索引擎特性的地方:
dsl = {
'query': {
'match': {
'title': '中國 領事館'
}
}
}
es = Elasticsearch()
result = es.search(index='news', doc_type='politics', body=dsl)
print(json.dumps(result, indent=2, ensure_ascii=False))
複製代碼
這裏咱們使用 Elasticsearch 支持的 DSL 語句來進行查詢,使用 match 指定全文檢索,檢索的字段是 title,內容是「中國領事館」,搜索結果以下:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 2.546152,
"hits": [
{
"_index": "news",
"_type": "politics",
"_id": "dk5G9mQBD9BuE5fdHOUm",
"_score": 2.546152,
"_source": {
"title": "中國駐洛杉磯領事館遭亞裔男子槍擊,嫌犯已自首",
"url": "http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml",
"date": "2011-12-18"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dU5G9mQBD9BuE5fdHOUj",
"_score": 0.2876821,
"_source": {
"title": "中韓漁警衝突調查:韓警平均天天扣1艘中國漁船",
"url": "https://news.qq.com/a/20111216/001044.htm",
"date": "2011-12-17"
}
}
]
}
}
複製代碼
這裏咱們看到匹配的結果有兩條,第一條的分數爲 2.54,第二條的分數爲 0.28,這是由於第一條匹配的數據中含有「中國」和「領事館」兩個詞,第二條匹配的數據中不包含「領事館」,可是包含了「中國」這個詞,因此也被檢索出來了,可是分數比較低。
所以能夠看出,檢索時會對對應的字段全文檢索,結果還會按照檢索關鍵詞的相關性進行排序,這就是一個基本的搜索引擎雛形。
另外 Elasticsearch 還支持很是多的查詢方式,詳情能夠參考官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/6.3/query-dsl.html
以上即是對 Elasticsearch 的基本介紹以及 Python 操做 Elasticsearch 的基本用法,但這僅僅是 Elasticsearch 的基本功能,它還有更多強大的功能等待着咱們的探索,後面會繼續更新,敬請期待。
本節代碼:https://github.com/Germey/ElasticSearch。
另外推薦幾個不錯的學習站點:
Elasticsearch 權威指南:https://es.xiaoleilu.com/index.html
全文搜索引擎 Elasticsearch 入門教程:http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html
Elastic 中文社區:https://www.elasticsearch.cn/
https://es.xiaoleilu.com/index.html
https://blog.csdn.net/y472360651/article/details/76468327
https://elasticsearch-py.readthedocs.io/en/master/
https://es.xiaoleilu.com/010_Intro/10_Installing_ES.html
https://github.com/medcl/elasticsearch-analysis-ik
我我的比較喜歡總結和分享,此次很榮幸在掘金的平臺把本身的爬蟲相關經驗分享給你們,包括爬取、解析、防反爬、加速等等部分,但願你們聽完以後有所收穫。
本週日(10月21日)我會作客掘金Bilibili直播間爲你們作一場《健壯高效的網絡爬蟲》的直播【網頁連接】。直播中我也會抽出 5 名小夥伴贈送各贈送一本《Python3網絡爬蟲開發實戰》書籍一本,但願你們能夠支持。