一,安裝
直接在github找到elasticsearch-rtf,下載下來,解壓
教程
二、概念
集羣:一個或者多個節點組織在一起
節點:一個節點是集羣中的服務器,由一個名字來標識,通常是一個隨機的漫威角色名字
分片:將索引劃分爲多份的能力,允許水平分割和擴展容量,多個分片響應請求,提高性能和吞吐量
副本:創建分片的一份或多份的能力,在一個節點失敗其餘節點可以頂上
HTTP方法:剛開始是GET,POST,HEAD,後來又新增了五種 OPTIONS,PUT,DELETE,TRACE,CONNECT
倒排索引 :倒排索引源於實際應用中需要根據屬性的值來查找記錄。這種索引表中的每一項都包括一個屬性值和具有該屬性值的各記錄的地址。由於不是由記錄來確定屬性值,而是由屬性值來確定記錄的位置,因而稱爲倒排索引(inverted index)。帶有倒排索引的文件我們稱爲倒排索引文件,簡稱倒排文件(inverted file)。
倒排索引待解決問題:1.大小寫轉換問題,如python和PYTHON 應該是同一個詞
2.詞幹抽取,looking和look應該處理爲一個詞
3.分詞,若屏蔽系統應該分詞爲“屏蔽”、“系統” 還是應該爲“屏蔽系統”
4.倒排索引文件過大-壓縮編碼
二 基礎
1 創建索引
一旦創建的索引,分片數是不可以改的,副本數可以改
PUT nga #創建索引
{
"settings": {
"index":{
"number_of_shards":5, #分片數
"number_of_replicas":1 #副本數
}
}
}
2 獲取setting信息
GET nga/_settings #獲取索引名爲nga的setting信息
GET _all/_settings #獲取所有索引的setting
GET .kibana,nga/_settings #獲取指定索引的setting
3 修改setting
PUT nga/_settings
{
"number_of_replicas": 2
}
4 獲取索引信息
#獲取索引信息
GET _all
GET nga
5保存文檔
PUT nga/job/1 #指明 索引/type/id 也可以寫成PUT nga/job/ 將會自動成一個id
{
"title":"python分佈式爬蟲開發",
"salary_min":15000,
"city":"beijing",
"company":{
"name":"baidu",
"company":"beijingruanjianyuan"
},
"publish_date":"2017-8-21",
"comments":23
}
6 獲取
GET nga/job/1 #指定id
GET nga/job/1?_source=title,city #指定id指定字段
GET nga/job/1?_source #獲取指定id所有字段
7 修改
方式一、完全覆蓋原有內容
PUT nga/job/1 #要指定id
{
"title":"python分佈式爬蟲開發",
"salary_min":15000,
"company":{
"name":"baidu",
"company":"beijingruanjianyuan"
},
"publish_date":"2017-8-21",
"comments":23
}
方式二:修改指定字段
POST nga/job/1/_update
{
"doc":{ #doc裏面放的就是要修改的內容
"comments":5
}
}
7刪除
1 刪除文檔
DELETE nga/job/1
2 刪除索引
DELETE nga
3.刪除index下的type裏面的所有數據
POST lagou/testjob2/_delete_by_query?conflicts=proceed
{
"query": {
"match_all": {}
}
}
三、批量操作
查詢type爲job1且id爲1,job2且id爲2的數據,這個方法也可以查不同索引下的數據
GET _mget
{
"docs":[{
"_index":"testdb",
"_type":"job1",
"_id":1
},{
"_index":"testdb",
"_type":"job2",
"_id":1
}
]
}
查詢相同index下的不同type還可以這樣
GET testdb/_mget
{
"docs":[{
"_type":"job1",
"_id":1
},{
"_type":"job2",
"_id":1
}
]
}
如果type也相同
GET testdb/job1/_mget
{
"docs":[{
"_id":1
},{
"_id":2
}
]
}
這樣的情況可以進一步簡寫:
GET testdb/job1/_mget
{
"ids":[1,2]
}
四、bulk批量操作
批量導入可以合併多個操作,比如index,delete,update,create等等,也可以從一個索引導入到另一個索引
bulk把一次請求的數據發送給一個節點,再給多個分片進行操作,全部執行完成後再返回結果,如果數據量比較大就會造成延遲,所以也不能一次性發送太多
用bulk操作插入數據
POST lagou/testjob/_bulk
{"index":{"_id":1}}
{"salary":10,"title":"python"}
{"index":{"_id":2}}
{"salary":20,"title":"scrapy"}
{"index":{"_id":3}}
{"salary":30,"title":"django"}
{"index":{"_id":4}}
{"salary":40,"title":"elasticsearch"}
五 映射(mapping)
創建索引的時候可以預定字段類型和相關屬性
elasticsearch會根據json源數據的基礎類型猜測你想要的字段映射,將輸入的數據轉變成可搜索的索引項,Mapping就是我們自己定義的字段的數據類型,同時告訴elasticsearch如何索引數據,以及是否可以被搜索
作用:會讓索引建立更加細緻和完善
類型:靜態映射 動態映射
內置類型:
Sting類型:text,keyword(string類型在es5開始已經廢棄)(keyword不會進行分詞)
數字類型:long,inteher,short,byte,double,float
日期類型:date
bool類型:boolean
binary類型:binary
複雜類型:object,nested
geo類型:geo-point(經緯度),geo-shape
專業類型:ip,competion
object:"company":{ "name":"baidu", "company":"beijingruanjianyuan" },
這就是一個object。
nexted:“company”:{
“name”:“baidu”,
“company”:“beijingruanjianyuan”,
“emplyee”:[{“name”:“tom”,“age”:26}],
“emplyee”:[{“name”:“tony”,“age”:23}]
},這裏 "emplyee":[{"name":"tom","age":26}], "emplyee":[{"name":"tony","age":23}] },
就是一個nested
屬性:
創建索引
PUT test_index
{
"mappings":{
"job":{
"properties":{
"title":{
"type":"text"
},
"salary_min":{
"type":"keyword"
},
"company":{
"properties":{
"name":{
"type":"text"
},
"company_addr":{
"type":"text"
},
"emplyee-count":{
"type":"integer"
}
}
},
"publish_date":{
"type":"date",
"format":"yyyy-MM-dd"
},
"comments":{
"type":"integer"
}
}
}
}
}
已經建好的索引不能修改,可以新增,如果要修改只能先刪除原來的索引,再新建索引,再導入數據
六 查詢
查詢分類:
基本查詢:使用elasticsearch內置的查詢條件進行查詢
組合查詢:把多個查詢組合在一起進行復合查詢
過濾:查詢同時,通過filter條件在不影響打分的條件下篩選
match查詢
會對搜索進行解析,match匹配指定字段的值,做分詞處理以後再做查詢
GET test_search/job/_search
{
"query": {
"match": {
"title": "python"
}
}
}
term查詢
不會對傳進來的值進行分詞處理
GET test_search/job/_search
{
"query": {
"term": {
"title": "python學習"
}
}
}
GET test_search/job/_search
{
"query": {
"terms": {
"title":[ "python", "java"]
}
}
}
控制返回的數量
GET test_search/job/_search
{
"query": {
"match": {
"title":"python"
}
},
"from":1, #從第幾個結果開始
"size":1 #返回多少條結果
}
查詢所有的數據
GET test_search/job/_search
{
"query": {
"match_all": {
}
}
}
短語查詢
要滿足查詢關鍵詞中的所有詞,比如說“python系統”,要有“python”,還要有“系統”,slop是兩個詞之間的最大距離
GET test_search/job/_search
{
"query":{
"match_phrase": {
"title": {
"query": "python系統",
"slop":6
}
}
}
}
multi_match
可以查詢多個字段
title,desc 是字段名 title^3意思是title的權重比desc大三倍
GET test_search/job/_search
{
"query":{
"multi_match": {
"query": "python",
"fields":["title^3","desc"]
}
}
}
指定返回字段
GET test_search/job/_search
{
"stored_fields": ["title","company"],
"query": {
"match": {
"title": "python"
}
}
}
排序
按comment排序:
GET test_search/job/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"comments": {
"order": "asc"
}
}
]
}
範圍查詢
gt:大於 gte:大於等於 lt:小於 lte:小於等於 boost:所屬字段的權重
GET test_search/job/_search
{
"query": {
"range": {
"comments": {
"gte": 10,
"lte": 20,
"boost": 1
}
}
}
}
查詢時間的
GET test_search/job/_search
{
"query": {
"range": {
"add_time": {
"gte": "2017-4-01",
"lte": "now"
}
}
}
}
wildcard查詢
wildcard裏面的關鍵詞支持通配符
GET test_search/job/_search
{
"query": {
"wildcard": {
"title": {
"value":"pyth*n",
"boost":2.0
}
}
}
}
bool查詢
用bool包括 must should must_not filter來完成
寫法如下:
bool{
"filter":[], #字段過濾 不參與打分
"must":[], #裏面的字段必須同時滿足
"should":[], #只要滿足一個或者多個
"must_not":[], #一個都不能滿足
}
如果不止一個條件,可以用“[ ]”來包括,只有一個就用"{ }"
例如,查詢薪資爲20的工作
測試數據:
POST lagou/testjob/_bulk
{"index":{"_id":1}}
{"salary":10,"title":"Python"}
{"index":{"_id":2}}
{"salary":20,"title":"scrapy"}
{"index":{"_id":3}}
{"salary":30,"title":"django"}
{"index":{"_id":4}}
{"salary":40,"title":"elasticsearch"}
sql語句爲:select * from testjob where salary=20
GET lagou/testjob/_search
{
"query": {
"bool": {
"must": {
"match_all":{}
},
"filter": {
"term": {
"salary": 20
}
}
}
}
}
此處把term改爲match結果也一樣,因爲salary是int類型,不會被分詞,但是在filter裏面一些精確的值最好用term來做,也可以查詢多個值,如10和20,則改成 “salary”: 20
"must": {
"match_all":{}
}
實際上可以省略,因爲是默認查詢所有的,但是這裏可以改成過濾標題之類的,比如
GET lagou/testjob/_search
{
"query": {
"bool": {
"must": {
"match":{"title":"scrapy"}
},
"filter": {
"match": {
"salary": 20
}
}
}
}
}
關於text類型大小寫
GET lagou/testjob/_search
{
"query": {
"bool": {
"must": {
"match_all":{}
},
"filter": {
"term": {
"title": "Python"
}
}
}
}
}
這樣是查詢不到結果的,把Python改爲python就可以查詢出來,因爲title是text類型的,text字段在做索引的時候會進行分詞並且進行大小寫轉換,這裏用term查詢,不會對查詢的python做預處理,會直接拿這個詞去做搜索,但是這裏面已經轉成小寫了所以查詢不到。
如果改成match就能查到。
如果一定要用大寫來查詢,就要在做mapping的時候把這裏的title的index設爲false,不做倒排索引。但是這樣查詢就不方便了
查看分析器的解析結果
可以查看分析器的所有解析結果,ik_max_word分析出來的結果集比較大,比較仔細,用ik_smart分析出來的結果是比較小的結果集
GET _analyze
{
"analyzer": "ik_max_word",
"text":"Python網絡開發工程師"
}
組合過濾查詢
查詢薪資等於20或者工作爲python的工作,排除薪資爲30的
sql:select * from testjob where (salary=20 or title=“Python”) and (salary!=30)
GET lagou/testjob/_search
{
"query": {
"bool": {
"should":[
{"term":{"salary":20}},
{"term":{"title":"python"}}
],
"must_not":{
"term":{"price":30}
}
}
}
}
嵌套查詢
sql: select * from testjob where title=“python” or (title=“django” and salary=30)
GET lagou/testjob/_search
{
"query": {
"bool": {
"should": [
{"term":{"title":"python"}},
{
"bool": {
"must":[
{"term":{"title":"django"}},
{"term":{"salary":30}}
]
}
}
]
}
}
}
過濾空和非空
測試數據
POST lagou/testjob2/_bulk
{"index":{"_id":1}}
{"tags":["search"]}
{"index":{"_id":2}}
{"tags":["search","python"]}
{"index":{"_id":3}}
{"other_field":["some data"]}
{"index":{"_id":4}}
{"tags":null}
{"index":{"_id":"5"}}
{"tags":["search",null]}
處理null 空值的方法:
sql:select tags from testjob2 where tags is not NULL
GET lagou/testjob2/_search
{
"query":{
"bool":{
"filter":{
"exists":{
"field":"tags"
}
}
}
}
}
“exists”:{
“field”:“tags”
} 這個組合是固定的,如果要查詢不存在的就是
"exists":{ "field":"tags" }
八、將爬取到的數據存儲到elasticsearch中
安裝
安裝es接口:elasticsearch-dsl:進入虛擬環境 輸入 install elasticsearch-dsl
官方文檔:官方文檔 例子在 persistence-doctype
創建一個pipeline
創建一個models文件夾,新建一個es-type的python文件,參考官方文檔的例子,在裏面寫一個duowanType,創建索引,然後連接服務器,代碼如下:
from datetime import datetime
from elasticsearch_dsl import DocType, Keyword, Text, Date, Integer
from elasticsearch_dsl.connections import connections
# 連接服務器 允許連接多個服務器
connections.create_connection(hosts=["localhost"])
class DuowanType(DocType):
# 多玩視頻文章類型
url = Keyword()
url_id = Keyword()
front_img_url = Keyword()
front_img_url_path = Keyword()
title = Text(analyzer="ik_max_word")
update_time = Date()
author = Text(analyzer="ik_max_word")
playnum = Integer()
comment_count = Integer()
like_count = Integer()
class Meta:
# 定義type index
index = "duowan"
doc_type = "video"
if __name__== "__main__":
# 通過定義的類 直接生成mapping
DuowanType.init()
但是由於我們有不同的items,需要不同的type,所以把type的定義放到items中
import datetime
from scrapy.loader.processors import MapCompose, TakeFirst
from NgaSpider.models.es_types import DuowanType
from w3lib.html import remove_tags
from scrapy.loader import ItemLoader
class NgaItemLoder(ItemLoader):
default_output_processor = TakeFirst()
def update_time(value):
if value:
time = value.replace('上傳於', '')
date = time.strip()
else:
date = datetime.datetime.now().date()
return date
def get_nums(value):
if value:
nums = value
else:
nums = 0
if re.match('(.*?)萬', nums):
# 字符串含有萬字去掉萬字 乘以1000
nums_str = str(nums).replace('萬', '')
nums = float(nums_str)
nums = nums*10000
if re.match('^[0-9]+,[0-9]+$', str(nums)):
# 字符串含有逗號
nums = float(str(nums).replace(',', '').strip())
else:
nums = float(nums)
return nums
class NgaspiderItem(scrapy.Item):
url = scrapy.Field()
url_id = scrapy.Field()
front_img_url = scrapy.Field()
front_img_url_path = scrapy.Field()
title = scrapy.Field()
update_time = scrapy.Field(
input_processor=MapCompose(update_time)
)
author = scrapy.Field()
playnum_text = scrapy.Field()
playnum = scrapy.Field(
input_processor=MapCompose(get_nums)
)
comment_count = scrapy.Field(
input_processor=MapCompose(get_nums)
)
comment_count_text = scrapy.Field()
like_count = scrapy.Field(
input_processor=MapCompose(get_nums)
)
like_count_text = scrapy.Field()
def save_to_es(self):
duowan = DuowanType()
duowan.url = self['url']
duowan.url_id = self['url_id']
if 'front_img_url' in self:
duowan.front_img_url = self['front_img_url']
duowan.front_img_url_path = self['front_img_url_path']
duowan.title = self['title']
duowan.update_time = self['update_time']
duowan.playnum_text = self['playnum_text']
duowan.playnum = self['playnum']
duowan.author = self['author']
duowan.comment_count = self['comment_count']
duowan.like_count = self['like_count']
# duowan.meta.id = item["url_object_id"]
duowan.save()
return
pipelines中改爲:
class ElasticsearchPipeline(object):
# 把爬取的數據保存到elasticsearch
def process_item(self, item, spider):
# 將item轉換爲es數據
item.save_to_es()
return item
如果type中需要用到html網頁內容可以這樣寫:
from w3lib.html import remove_tags
.....
duowan.html = remove_tags(html內容)