为什么要用ElasticSearch?

一般来说数据库都会自带模糊搜索的功能,但其实上真正使用的时候,遇到中文搜索这种问题时,搜索速度会非常慢,可能会需要O(n)或者O(logn)的复杂度。

在实际使用中,这是不允许的,因为用户就搜索一条语句。假设n非常大的话,等待时间可能会非常的久。所以肯定需要使用索引机制,加速搜索效率。

搜索引擎使用的是倒排索引,建立好索引后,可以在O(c)的时间完成搜索功能。不过这步只是一个粗略的查询,还要对搜索的结果进行排序,这里可能会用到如BM25Query Likelihood Model等给文档打分的方法,通过打分对搜索结果进行排序。

如果我们自己去实现这些功能的话,首先可能要学学什么是倒排索引,还有文档的打分的各种方法,再加上代码,要用起来不知道要到哪年哪月了。而ElasticSearch将这些操作变得非常简单。它是一个基于Lucene的搜索服务器,它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。

下面我们将要讲解如何使用ElasticSearch构建我们的帖子搜索。

ElasticSearch安装

“工欲善其事,必先利其器”,我们这里先讲解一些ElasticSearch的安装步骤,下面的操作是在mac系统上进行的。因为ElasticSearch是基于Java开发的,Java本身是跨平台的,所以其他系统上面其实操作都是基本一样的。

ElasticSearch安装

这里首先安装JDK,然后下载安装ElasticSearch-6.0.0版本。

下载解压后,我们可以看到这里有一个bin文件夹。

bin文件夹里都是ElasticSearch相关操作的脚本。我们把ElasticSearch的bin目录添加到PATH中,之后在终端中就不需要输入绝对路径了。

1
export PATH=$HOME/ProgramFiles/elasticsearch-6.0.0/bin:${PATH}

ik分词工具的安装

因为我们做的是中文搜索,而ElasticSearch自带的中文分词做的不好,所以这里需要使用到ik的一个插件。使用以下命令安装即可。

1
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.0.0/elasticsearch-analysis-ik-6.0.0.zip

Python库的安装

最后,我们因为要使用python操作ElasticSearch,通过pip命令安装ElasticSearch的API库。

1
pip install elasticsearch

服务启动

在建立索引之前,我们要先打开ElasticSearch服务。在终端输入elasticsearch命令即可。

启动后可以看到ElasticSearch服务监听了本地的9300端口。

索引构建

接下来,我们来看看如何建立索引。

创建Mappings

首先像数据库一样,我们先建立一个索引。这里我们管它叫:tiezi_index。在创建索引的过程中,要配置映射一个Mappings(映射)。

我们先来看下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from elasticsearch import Elasticsearch
es = Elasticsearch()
# 初始化索引的Mappings设置,只有一个字段: 标题。
index_mappings = {
"mappings": {
"tiezi": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
}
},
}
}
if es.indices.exists(index='tieba_index') is not True:
print("create tieba_index")
es.indices.create(index='tieba_index', body=index_mappings)

这里主要看一下index_mappings这个变量,在Mappings下面是一个Type(类型),这里Type的名称我起名为:tiezi。Type其实就类似于mongodb中集合的概念,是对文档分区的。

对于tiezi这个类型,还要配置一下它所包含的属性。由于我们的帖子只爬取了标题,所以只有一个title就好了。

可以看到在title字段里面还有东西,这里指定了title是文本类型,并且使用ik分词工具进行分析索引。

索引数据

完成映射后,我们开始把所有数据进行索引。这里只需要遍历一遍我们爬的帖子数据,然后索引到ElasticSearch里就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def init_collection():
client = pymongo.MongoClient(host="localhost", port=27017)
db = client['tieba']
return db["beike"]
coll = init_collection()
for tiezi in coll.find():
_id = str(tiezi["_id"])
doc = {
"id": _id,
"title": tiezi["title"]
}
print(doc)
res = es.index(index="tieba_index", doc_type="tiezi", id=_id, body=doc)
print(res)

这里索引需要花一定的时间,耐心等待即可。

搜索测试

最后,我们进行一下搜索测试。搜索直接可以调用search即可。搜索的返回结果是字典类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from elasticsearch import Elasticsearch
def search(query):
query_contains = {
'query': {
'match': {
'title': query,
}
}
}
es = Elasticsearch()
searched = es.search("tieba_index", doc_type="tiezi", body=query_contains, size=20)
return searched
for res in search("假期都做什么呢")["hits"]["hits"]:
print(res["_source"]["title"], res["_score"])

我们这里可以打印一下,搜索出来的标题和搜索的得分(默认是BM25得分)。

结束语

到这里,本系列教程的所有内容都结束了。网络爬虫是一个非常实践的应用,在真正爬取数据时,可能会遇到各种各样的问题。从数据存储、反反爬虫以及算法上可能都要踩很多坑。

本系列教程力求能够从浅入深的讲解网络爬虫,尽可能覆盖到爬虫所涉及的内容的方方面。当然有些部分也会比较粗糙,只是大概提了一句。希望通过本系统教程,能让读者能够对爬虫有一个宏观的认识与了解。