Python分布式爬虫-elasticsearch搭建搜索引擎


 

一、elasticsearch使用

1、elasticsearch介绍

 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便

我们希望建立一个网站或应用程序,并要添加搜索功能,但是想要完成搜索工作的创建是非常困难的:
我们希望搜索解决方案要运行速度快;
我们希望能有一个零配置和一个完全免费的搜索模式;
我们希望能够简单地使用JSON通过HTTP来索引数据;
我们希望我们的搜索服务器始终可用;
我们希望能够从一台开始并扩展到数百台;
我们要实时搜索,我们要简单的多租户;
我们希望建立一个云的解决方案。
因此我们利用Elasticsearch来解决所有这些问题及可能出现的更多其它问题

 内部功能:
 分词 搜索结果打分 解析搜索要求
 全文搜索引擎:solr sphinx
 很多大公司都用elasticsearch

 elasticsearch对Lucene进行了封装,既能存储数据,又能分析数据,适合与做搜索引擎


 

2、elasticsearch安装与配置

1)elasticsearch依赖于java,需安装java SDK 依赖

2)elasticsearch的安装可以从官网下载,但不建议使用官网的版本,原因是该版本提供原始的插件不多。本项目使用的是elasticsearc-rtf,由国内的某大神组装整合,安装很多插件,比官方版好使用。GitHub(下载中文版):https://github.com/medcl/elasticsearch-rtf

3)运行elasticsearch的方法:在bin文件目录下进入终端,执行命令:elasticsearch.bat

成功运行:

 

浏览器输入:127.0.0.1:9200 ,经格式化处理,返回结果:

 

配置文件:conf/elasticsearch.yml


 

3、elasticsearch两个重要插件:head和kibana的安装

3.1、ealsticsearch-head安装

ealsticsearch只是后端提供各种api,要直观的使用它,就需要使用到elasticsearch-head,elasticsearch-head是一款专门针对于elasticsearch的客户端工具

elasticsearch-head配置包,下载地址:https://github.com/mobz/elasticsearch-head

elasticsearch-head是一个基于node.js的前端工程,使用它之前,我们需确定已安装node、npm,具体安装可参看:https://www.cnblogs.com/Eric15/articles/9517232.html

elasticsearch-head使用(这里针对的是elasticsearch 5.x以上的版本):

  1. 进入elasticsearch-head的文件夹,如:D:\xwj_github\elasticsearch-head
  2. 执行: npm install / cnpm install 。 npm install速度会很慢,建议使用cnpm淘宝镜像的方式安装:cnpm install(如没有cnpm命令,需安装:npm install -g cnpm --registry=https://registry.npm.taobao.org
  3. 执行: npm run start / cnpm run start (以后进来可以直接运行这一行代码)

在浏览器访问http://localhost:9100,可看到如下界面,表示启动成功:

 

在浏览器打开127.0.0.1:9100 ,结果显示:

 

 

 


 注意:为了让ealsticsearch-head前端页面连上ealsticsearch后台,即可以直接通过127.0.0.1:9100页面连接127.0.0.1:9200,需要对ealsticsearch的配置文件做些配置:ealsticsearch.yml

http.cors.enabled: true  # 跨域配置
http.cors.allow-origin: "*"  # 允许all连接
http.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE  # 请求方法
http.cors.allow-headers: "X-Requested-With, Content-Type. Content-Length, X-User"  #user-agent

 保存后,重新运行ealsticsearch.bat,在127.0.0.1:9100页面中重新连接127.0.0.1:9200 , 此时效果:

 


 

 3.2、kibana使用

kibana安装很简单,直接在网上搜索就可以找到资源。 

在安装成功后,进入bin目录 ,执行命令:kibana.bat

  

在浏览器中打开:127.0.0.1:5601 ,如显示如下页面,表示安装成功:

 


 

4、elasticsearch的基本概念

  1. 集群:一个或多个节点组织在一起
  2. 节点:一个集群中的一台服务器
  3. 分片:索引划分为多份的能力,允许水平分割,扩展容量,多个分片响应请求
  4. 副本:分片的一份或多分,一个节点失败,其他节点顶上

| index 类比→ | 数据库 |
| type 类比→  | 表 |
| document 类比→ | 行 |
| fields 类比→  | 列 |

http方法:

http1.0定义了三种请求方法:GET 、 POST 、 HEAD方法

http1.1后,新增五种方法:
OPTIONS 、 PUT 、 DELETE 、 TRACE 、 CONNECT


 

 5、倒序索引

 

 

 

上述问题elasticsearch已经解决,只需了解。


 6、 elasticsearch 基本的索引和文档CRUD操作

elasticsearch在kibana的基本操作

1)索引操作/index

PUT lagou   # 更新/新增操作,增加lagou索引
{
   "settings":{
        "index":{
            "number_of_shards":5,  # 5个切片。一旦生成不能更改
            "number_of_replicas":1  # 1个副本,可以更改
        }
    }
}
# GET方法
GET lagou/_settings  # 获取lagou索引下的settings数据

GET _all/settings  # 获取所有索引下的settings数据

GET .kibana,lagou/_settings  # 获取kibana、lagou索引下的settings数据

 

 

2)保存文档/type

PUT lagou/job/1   # 修改/新增lagou索引下job的第1个文档数据(第1行数据),如果job后面的数字1不提供,则会自动生成一个随机uuid当作文档id
{
    "title":"python分布式爬虫开发",
    "salary_min":15000,
    "city":"北京",
    "company":{
        "name":"百度",
        "company_addr":"北京市软件园",
        },
    "publish_date":"2018-11-11",
    "comments":15
}
# GET
GET lagou/job/1   # 获取id为1的数据文档

GET lagou/job/1?_source=title  # 获取id为1的数据文档的title

GET lagou/job/1?_source=title,city # 获取id为1的数据文档的title、ciity

GET lagou/job/1?_source  # 获取id为1的数据文档下source的所有数据

 

# POST  修改数据 ,需注意的是PUT修改单个数据是完全覆盖,POST修改数据是以更新的形式,不会破坏其他未修改数据

POST lagou/job/1/_update
{
    "doc":{
        "comments":20   # 如果使用PUt方法,则其他未指明的数据均会被覆盖清除,只保留comments数据
        }
}
# DELETE  删除

DELETE lagou/job/1  # 删除id为1的文档数据

# DELETE lagou/job   # kibana中已经取消了这种删除方式

DELETE lagou   # 删除lagou索引

 3)elasticsearch的mget和bulk批量操作

 3.1)mget批量操作

在HTTP请求连接时,都会经历三次握手的过程,如果每查询一个数据都需要经过这过程,浪费时间也浪费开销,于是一次性批量查询数据的方法就出现了。

假设 testdb(index )索引下的job1、job2(type ),两张表下分别有id为1(第一行数据)、id为2(第2行数据)的文档数据,即:testdb/job1/1  、 testdb/job2/2

问题:查询index为testdb下的job1表的id为1和job2表的id为2的数据。 --使用mget方法

# 情景1
GET _mget
{
    "docs":[
    {
    "_index":"testdb",
    "_type":"job1",
    "_id":1
    },
    {
    "_index":"testdb",
    "_type":"job2",
    "_id":2
    }
    ]
}

# 情景2,指定index(testdb)相同,在doc中就不用指定
GET testdb/_mget{
    "docs":[
    {
    "_type":"job1",
    "_id":1
    },
    {
    "_type":"job2",
    "_id":2
    }
    ]
}

# 情景三,指定index(testdb)、job1相同,在doc中便不用指定
GET testdb/job1/_megt
{
    "docs":[
    {
    "_id":1
    },
    {
    "_id":2
    }
    ]
}

# 情景三 简写
GET testdb/job1/_megt
{
    "ids":[1,2]
}

 

 3.2)bulk批量处理

bulk批量处理:可以合并多个操作,比如多个index,delete,update,create等等,包括从一个索引到另一个索引:

  • action_and_meta_data\n
  • option_source\n
  • action_and_meta_data\n
  • option_source\n
  • ...
  • action_and_meta_data\n
  • option_source\n

每个操作都是由两行构成,除了delete除外,由元信息行和数据行组成
注意数据不能美化,即只能是两行的形式,而不能是经过解析的标准的json排列形式,否则会报错

 注意:delete操作时,只有一行,即元信息行,这点需要注意:

 

两行的形式,无论数据行多少数据都必须放成一行的形式,不能多行放置或用json美化的排列形式:

 

POST _bulk   # 请求类型,及操作类型需指明
{"index":...}  # 元信息行,
{"field":...}   # 数据操作行

 


 7、elasticsearch的mapping映射管理

elasticserach的mapping映射:创建索引时,可以预先定义字段的类型以及相关属性,每个字段定义一种类型,属性比mysql里面丰富,前面没有传入,因为elasticsearch会根据json源数据来猜测是什么基础类型。M挨批评就是我们自己定义的字段的数据类型,同时告诉elasticsearch如何索引数据以及是否可以被搜索。
作用:会让索引建立的更加细致和完善,对于大多数是不需要我们自己定义

 

内置类型:

  • String类型: 两种text keyword。text会对内部的内容进行分析,索引,进行倒排索引等,为设置为keyword则会当成字符串,不会被分析,只能完全匹配才能找到String。 在es5已经被废弃了
  • 日期类型:date 以及datetime等
  • 数据类型:integer long double等等
  • bool类型
  • binary类型
  • 复杂类型:object nested
  • geo类型:geo-point地理位置
  • 专业类型:ip competition
  • object :json里面内置的还有下层{}的对象
  • nested:数组形式的数据

 

 

 

 

关于映射管理,类似于数据库中对于字段类型的设置。通过生成表的形式,给每个字段设定字段类型,再通过插入相应数据等。具体用法可百度


 

8、 elasticsearch的简单查询

大概分为三类:

  • 基本查询:
  • 组合查询:
  • 过滤:查询同时,通过filter条件在不影响打分的情况下筛选数据

 1)match查询:

match后面为关键词,如下面demo->关于python的都会被提取出来。match查询会对内容进行分词,并且会自动对传入的关键词进行大小写转换,内置ik分词器会进行切分,如python网站,只要搜到存在的任何一部分,
都会返回该相关数据
GET lagou/job/_search
{
    "query":{
        "match":{
            "title":"python"
        }
    }
}

 

 2)term查询

区别,对传入的值不会做任何处理,就像keyword,只能查询完全匹配的类型,一部分匹配是不成功的

 

 3)terms查询

term的批量查询操作,可以传入多个值。只要有任一个词完全匹配,就会返回结果

 

注:控制查询的返回数量,如:一页n个数据,翻到下一页再返回n个数据,即分页显示数据:

GET lagou/_serach
{
    "query":{
        "match":{   # match查询
            "title":"python"   # 匹配关键词:python
        }
    },
    "form":1,   # 从第一个数据开始显示,也可以从第0个数据开始显示
    "size":2   # 一页两个数据
}

 

4)match_all

 匹配所有数据,返回所有数据

GET lagou/_search  # 返回lagou索引下的所有数据
{
"query":{
"match_all":{}
}
}

。。。


 

9、将爬取的数据保存至elasticsearch

1)从GitHub上下载 elasticsearch-dsl-py,并安装:pip install elasticsearch-dsl-py

2)新建py文件:es_types.py

 elasticsearch的字段类型定义,及数据保存,与django中的ORM相似,可以往models方面理解

from datetime import datetime
from elasticsearch_dsl import DocType, Date, Nested, Boolean, \
    analyzer, InnerObjectWrapper, Completion, Keyword, Text, Integer

from elasticsearch_dsl.analysis import CustomAnalyzer as _CustomAnalyzer

from elasticsearch_dsl.connections import connections
connections.create_connection(hosts=["localhost"])   # 连接es

class ArticleType(DocType):
    #伯乐在线文章类型
    suggest = Completion(analyzer=ik_analyzer)  #
    title = Text(analyzer="ik_max_word")
    create_date = Date()
    url = Keyword()
    url_object_id = Keyword()
    front_image_url = Keyword()
    front_image_path = Keyword()
    praise_nums = Integer()
    comment_nums = Integer()
    fav_nums = Integer()
    tags = Text(analyzer="ik_max_word")
    content = Text(analyzer="ik_max_word")

    class Meta:
        index = "jobbole"   # es中的index索引
        doc_type = "article"   # es中的type -表

 

3)在item中,将爬取到的数据赋值到es字段中

from w3lib.html import remove_tags
from models.es_types import ArticleType 
   def save_to_es(self):
        article = ArticleType()
        article.title = self['title']
        article.create_date = self["create_date"]
        article.content = remove_tags(self["content"])
        article.front_image_url = self["front_image_url"]
        if "front_image_path" in self:
            article.front_image_path = self["front_image_path"]
        article.praise_nums = self["praise_nums"]
        article.fav_nums = self["fav_nums"]
        article.comment_nums = self["comment_nums"]
        article.url = self["url"]
        article.tags = self["tags"]
        article.meta.id = self["url_object_id"]

        article.suggest = gen_suggests(ArticleType._doc_type.index, ((article.title,10),(article.tags, 7)))   # 

        article.save()

        redis_cli.incr("jobbole_count")   #

        return

 

4)在pipelines.py中,执行save_to_es函数:

class ElasticsearchPipeline(object):
    #将数据写入到es中

    def process_item(self, item, spider):
        #将item转换为es的数据
        item.save_to_es()

        return item

 

5)settings.py中设置:item_pipelines

ITEM_PIPELINES = {
   # 'ArticleSpider.pipelines.JsonExporterPipleline': 2,

    'ArticleSpider.pipelines.ElasticsearchPipeline': 1
}

 



 

posted on 2018-10-06 02:26  Eric_nan  阅读(939)  评论(0编辑  收藏  举报