构建垂直搜索引擎

##定义与原理

  搜索引擎( Search Engine)是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。

  垂直搜索引擎是应用于某一个行业、专业的搜索引擎,是搜索引擎的延伸和应用细分化。垂直搜索引擎为用户提供的并不是上百甚至上千万相关网页,而是范围极为缩小、极具针对性的具体信息。因此,特定行业的用户更加青睐垂直搜索引擎。

  搜索引擎的基本工作原理包括如下三个过程:首先在互联网中发现、搜集网页信息;同时对信息进行提取和组织建立索引库;再由检索器根据用户输入的查询关键字,在索引库中快速检出文档,进行文档与查询的相关度评价,对将要输出的结果进行排序,并将查询结果返回给用户。

##功能介绍

  搜索引擎对大家来说都不陌生,如百度,谷歌…… 我们今天就来构建我们自己的搜索引擎。

  下面我们就来介绍一下我们要实现的功能:

  • 创建ES索引,建立mapping;
  • 网络数据抓取(即网络爬虫);
  • 抓取数据解析;
  • 解析数据保存到ES中;
  • 建立搜索页面,用户通过搜索框,输入关键字进行搜索;
  • 点击搜索按钮,实现对数据的检索;
  • 搜索数据内容展示。
  • ##技术选型

      本项目主要使用到NodeJSPythonElasticsearch,详细介绍如下:

      NodeJS:Node.js采用Google Chrome浏览器的V8引擎,一个后端的Javascript运行环境,提供很多系统级的API,如文件操作、网络编程等。

      Python:Python(英语发音:/ˈpaɪθən/), 是一种面向对象、解释型计算机程序设计语言,Python语法简洁而清晰,具有丰富和强大的类库。能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。

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

      有对以上技术不了解的同学,可以去看我们的课程。

  • ##结构划分

      项目主要分为以下几大模块:数据搜索模块,数据展示模块,网络数据抓取与存储模块。

  • 网络数据抓取:对网络上的数据进行抓取解析;
  • 数据存储模块:对抓取到数据按规则进行存储。
  •   后面我们将会按功能来实现这些模块。

  • 数据搜索展示模块:用户通在搜索框输入关键字进行搜索,搜索到的数据进行展示;
  •  
  • ##开发环境

      由于本项目用到技术较多,而在我们右面的编辑环境中只能用来做部分代码的测试,所以我们要在自己的电脑上进行开发环境的安装,本项目用到的操作系统是ubuntu,当然你也可以选择其它系统。

      1.安装nodejs开发环境,我们的环境安装的是nodejs v0.10.38,并且安装EXPRESS框架;

      2.安装Elasticsearch及中文分词ik,ES安装的是v 1.5.2;

      3.安装python,并且安装pyspider,我们的环境安装的是python 2.7;

      将以上开发程序安装完成以后,就可以开始我们的项目了,有不了解以上技术的同学,可以去课程中心查看对应的课程

  • ##建立索引

      在我们安装完Elasticsearch与中文分词以后,我们要在Elasticsearch中建立一个索引来存储数据。比如我创建的索引是article。因为ES是安装在ubuntu系统下,所以需要用到ubuntu中的curl命令,不了解的同学可以去查一下curl命令的相关资料。

      下面代码是建立索引为article的索引。

    curl -XPUT 'http://localhost:9200/article/'

      返回结果为:

    {"acknowledged":true}

      索引建立完成。

  • ##建立mapping

      在前面一节中我们已经建立索引名为article的索引,我们在这将对article中的内容进行约束,进行验证。从而在存取数据时按照我们预定的规则进行存储。也就是我们在这里要建立article的mapping。下面代码是建立索引为article,索引类型为detail的mapping,同时指定中文分词ik。

    curl -XPUT 'http://localhost:9200/article/_mapping/detail' -d ' 
    {
            "detail" : {
                "dynamic" : true,
                "properties" : {
                    "title" : { "type" :  "string"  },
                    "url"   : { "type" :  "string" },
                   "content" :{ "type" :  "string", "analyzer" : "ik" }
                }
            }
        }
    '

    返回结果为:

    {"acknowledged":true}

      索引mapping建好完成,这样我们的数据存储准备工作就已经完成。

  • ##数据保存

      我们想要把下面一组json数据数据存入ES的article索引中,例如:

    {
        "title":"hello world!",
        "url": "http://www.hubwiz.com",
        "content":"一个在线学习编程的网站。"
    }

      我们要根据前面两节建立内容,对以上json数据用下面的语句来实现:

    curl -XPOST  'http://localhost:9200/article/detail' -d '{
        "title":"hello world!",
        "url": "http://www.hubwiz.com",
        "content":"一个在线学习编程的网站。"
    }'

    返回结果如下:

    {"_index":"article","_type":"detail","_id":"AU2tlMrXq0fYwjnKKzWI","_version":1,"created":true}

      其中id是系统自动产生的,这样我们就将以上数据存入了ES中。

  • ##ES数据检索

      ES中已经存储了我们需要的数据,我们现在要通过ES的request body来查找属性为“content”,值可以匹配“编程”的记录,代码如下:

    curl -XGET 'http://localhost:9200/article_one/detail/_search' -d '
     {
        "query":
           {
           "match":
             {"content":"编程"}
           }
      }'

    查询结果返回如下:

    {
      "took" : 5,
      "timed_out" : false,
      "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
      },
      "hits" : {
        "total" : 1,
        "max_score" : 0.076713204,
        "hits" : [ {
          "_index" : "article_one",
          "_type" : "detail",
          "_id" : "AU2tlMrXq0fYwjnKKzWI",
          "_score" : 0.076713204,
          "_source":{
        "title":"hello world!",
        "url": "http://www.hubwiz.com",
        "content":"一个在线学习编程的网站"
            }
        } ]
      }
    }
  • ##pyspider简介

      pyspider的设计基础是:以python脚本驱动的抓取环模型爬虫;

  • 通过python脚本进行结构化信息的提取,follow链接调度抓取控制,实现最大的灵活性;
  • 通过web化的脚本编写、调试环境。web展现调度状态;
  • 抓取环模型成熟稳定,模块间相互独立,通过消息队列连接,从单进程到多机分布式灵活拓展。
  •   pyspider作为一个开源的数据抓取框架,它提供了数据抓取,数据解析,数据展示等功能。

      pyspider 的主要特性:

  • python 脚本控制,可以用任何你喜欢的html解析包(内置 pyquery)
  • WEB 界面编写调试脚本,起停脚本,监控执行状态,查看活动历史,获取结果产出
  • 支持 MySQL, MongoDB, SQLite
  • 支持抓取 JavaScript 的页面
  • 组件可替换,支持单机/分布式部署,支持 Docker 部署
  • 强大的调度控制
  • ##pyspider数据抓取

      由于教程是基于 pyspider 的,你可以安装一个 pyspiderhttp://demo.pyspider.org/)。

      网络数据抓取实际就是去爬网页,而爬网页实际上就是:

  • 找到包含我们需要的信息的网址(URL)列表;
  • 通过 HTTP 协议把页面下载回来;
  • 从页面的 HTML 中解析出需要的信息;
  • 找到更多这个的 URL,回到 2 继续。
  •   在数据抓取的过程中,我们需要对万维网有一些简单的认识,万维网使用http协议传输,采用html描述外观和语义,使用网址(URL)定位,并链接彼此等。这样我们更容易对抓取到的数据进行解析。

    from libs.base_handler import *
      class Handler(BaseHandler):
        crawl_config = {
        }
    
        @every(minutes=24 * 60)
        def on_start(self):
            self.crawl('http://scrapy.org/', callback=self.index_page)
    
        @config(age=10 * 24 * 60 * 60)
        def index_page(self, response):
            for each in response.doc('a[href^="http"]').items():
                self.crawl(each.attr.href, callback=self.detail_page)
    
        @config(priority=2)
        def detail_page(self, response):
            return {
                "url": response.url,
                "title": response.doc('title').text(),
            }

      以上代码是创建任务后默认生成的一个脚本示例。

      通过 _onstart 回调函数,作为爬取的入口点,当点击主面板上的 run 的时候,就是调用这个函数,启动抓取。 self.crawl 告诉调度器,默认抓取 'http://scrapy.org/' 这个页面,然后使用 _callback=self.indexpage 这个回调函数进行数据解析。所有 return 的内容默认会被捕获到 resultdb 中。

  • ##pyspider数据解析

      在上一节中我们介绍pyspider抓取数据,而其中有一个函数_detailpage,这个函数的功能是对数据进行解析。由于示例代码不满足我们要存取的数据格式,所以要对该函数进行修改,修改成我们想要的内容如下:

    def detail_page(self, response):
            return {
                "url": response.url,
                "title": response.doc('title').text(),
                "content":response.doc('#article-content').text(),
            }
  • ##数据存储

      pyspider是个非常强大简单易用的爬虫框架,默认软件会把采集的所有字段打包保存到默认的数据库中,但是我们不希望存到默认数据库中,要存到ES中。这就要求重写pyspider的_onresult函数,我们将重写的_onresult函数放到_detailpage函数后面,当程序运行时就会执行我们的_onresult函数了。

      重写的_onresult函数的功能就是将已经解析完成的数据保存到ES中。具体实现我们将在后面进行讲解。

  • ##建立项目

      前面讲解了pyspider的抓取示例代码,现在我们就来创建一个pyspider的项目。

      我们在安装完成pyspider启动以后,就可以访问pyspider的dashboard地址为http://localhost:5000, 在pyspider 的 dashboard 的右下角,点击 "Create" 按钮,项目名为:_pytest

      替换 _onstart 函数的 self.crawl 的 URL,我们要抓取的地址是:'http://blog.csdn.net/qust_waiwai/article/details/18564231'

    def on_start(self):
            self.crawl('http://blog.csdn.net/qust_waiwai/article/details/18564231', callback=self.index_page)

    这样我们的抓取路径就修改完成。

  • ##解析

      在前一节中已经建立了_pytest 的项目, 在 _onstart 函数后面还有 _indexpage 与_detailpage两个函数,其中_indexpage函数通过回调_detailpage函数来对数据进行解析。比如我们前一节中抓取的页面的html结构如下图,其中深蓝色的部分就是我们想要的内容,由于pyspider支持CSS选择器来获取抓取页面元素的值,如下代码取到id为article-content的值,而在_indexpage函数中的CSS表达式.search-list-con dl dt a[href^="http"] 的作用是进一步去抓取该页中满足条件的链接。

    def index_page(self, response):
            for each in response.doc('.search-list-con dl dt a[href^="http"]').items():
                self.crawl(each.attr.href, callback=self.detail_page)
    
        @config(priority=2)
        def detail_page(self, response):
            return {
                "url": response.url,
                "title": response.doc('title').text(),
                "content":response.doc('#article-content').text(),
            }

    这样我们的数据解析部分就完成了。

    ##ES存储

      前面已经提到把数据存储到ES中,我们在这里就来实现,代码如下:

    def on_result(self, result):
            if not result or not result['title']:
                return
            es = Elasticsearch()
            es.index(index="article", doc_type="detail", body={"content": result['txt'],"title" :result['title'],"url":result['url']})

      在代码的开头部分我们还需要引入操作ES的模块:from elasticsearch import Elasticsearch

      这样存储部分也完成了,然后点击save。回到dashboard页面,找到项目_pytest,修改statusrunning,点击run就可以抓取了。

  • ##搜索页面

      首先要建立一个类似于百度那样的搜索页面,在这里我们用nodejs来实现。具体效果如图:

      

      首先呢,我们先新建一个项目工程目录,然后在目录下创建启动文件app.js,开始我们的第一步。这里我们会用到Express框架来实现相关功能,所以,需要先安装它,具体安装方法这里就不在做介绍了。在启动文件添加如下内容,来测试Express框架是否引用成功。

    var express = require('express');
    var app = express();
    app.get('/', function (req, res) {  
       res.send('Hello World!');
    });
    
    app.listen(80);

      然后创建目录,使用engine函数注册模板引擎并指定处理后缀名为html的文件。

    app.set( 'view engine', 'html' );
    app.engine( '.html', require( 'ejs' ).__express );

      最后在view文件夹中按照效果图的样子建立一个文件名为index.html的页面。这样我们的搜索页面就完成了。

    <div class="input-group">
                 <input type="text" id="key_search" maxlength="256" size="46" style=" width:450px"/>
                 <button class="btn btn-primary" data-toggle="button" id="btn_search" style="margin-bottom:7px !important;">search</button>
       </div>

    ##数据检索

      当我们在输入框,输入要检索的关键字后,点击查询按钮,这时我们要做的就是去实现数据的检索。主要代码如下:

    $("#btn_search").click(function () {
                       var key = $("#key_search").val();
                       var data = '{"query":{"match":{"content":"' + key + '"}}}';
                       $.ajax({
                           url: 'http://192.168.1.200:9200/article/detail/_search?source=' + data,
                           type: 'POST',
                           data: data,
                           dataType: "jsonp",
                           async: false,
                           jsonp: "callback",
                           success: function (data) {
                              //展示结果
                              // data中的数据即为我们要查询的数据,然后解析出我们需要的数据,展示出来。
                           }, error: function (data) {
                               alert("Error!");
                           }
                       });
                   });

      我们这里主要用了jqueryajax技术来实现数据的搜索,对搜索结果具体解析,得到我们想要的数据,在页面上展示出来,其中data就是我们要解析展示的数据。

  • ##数据解析

      根据上一节的内容,我们按关键字“编程”查询,得到的结果如下:

    {
      "took" : 2,
      "timed_out" : false,
      "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
      },
      "hits" : {
        "total" : 1,
        "max_score" : 1.0,
        "hits" : [ {
          "_index" : "article_one",
          "_type" : "detail",
          "_id" : "AU2tlMrXq0fYwjnKKzWI",
          "_score" : 1.0,
          "_source":{
        "title":"hello world!",
        "url": "http://www.hubwiz.com",
        "content":"一个在线学习编程的网站。"
              }
        } ]
      }
    }

      按照搜索的结果,我们就可以根据json格式取出想要的数据,然后对数据进行加工修饰,以达到想要的效果。

      简单效果图如下:

      这样我们的搜索引擎基本上已经完成了,下面就开启ES的服务,用nodejs运行我们的网站吧。

转载于:https://my.oschina.net/fymoon/blog/785545

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值