ElasticSearch简介

  • 全文搜索引擎
  • 存储:无需建表,使用json格式的文档
  • 搜索:按关键词搜索,无需拼装查询语句

ElasticSearch入门

REST简介

  • HTTP方法 + 地址
  • 方法是动词,地址是名词
  • 方法包括GET/PUT/POST/DELETE/PACTH/HEAD等

host:9200/index/type/id

这是访问ElasticSearch的层次结构,对比关系型数据库

  • index相当于database
  • type相当于table
  • 不需要预先创建index和type
  • type中数据类型可以不一致

用法注意

  • 获取数据使用Get方法
  • 添加数据若使用Put方法,必须带id,并在Request Body中携带要添加的内容
  • 添加数据若使用Post方法,可省略id,并在Request Body中携带要添加的内容
  • 查询内容使用Get或Post方法,并且Request Body为空
  • 全局查询/_search,特定查询在Request Parameters中添加条件

数据存储

使用elasticsearch go client通过代码将爬取到的有价值的信息存储到其中

重新设计数据结构

  • 原Profile结构体保存的是用户个人信息,而对于爬虫应用来讲,访问链接信息编号是必须具备的
  • 从elasticsearch的index/type/id三元组的格式出发,不同type相当于不同的table,id最好与type有关联,比如珍爱网的数据编号最好与珍爱网这张表有关
  • 设计新的数据格式如下
type Item struct {
    Url string
    Type string
    Id string

    Payload interface{}
}

数据去重

1.爬取的时候进行url去重
由于珍爱网会展示重复的用户,所以使用一个map来存储已经访问过的用户页面的地址,每次提交爬取任务前,在map中检查该任务对应的url是否已经访问过,若是,就无需提交该任务。

for _, r := range result.Requests {
  //检查待爬取的任务的url是否已经执行,若是,则跳过该次循环
    if isDuplicate(r.Url) {
      continue
    }
    ce.Scheduler.Submit(r)
}

2.存储的时候根据id去重
第一条url去重可以解决单次运行情况下的重复问题,但程序停止再运行时,仍然会从零开始获取数据,而这些数据已经在elasticsearch中存在了;
因此由elasticsearch可以根据id存储数据的特点,为每条数据设计唯一的id,避免程序多次运行时的重复存储问题;
设计id比较复杂,而从现有的用户页面的地址中取出id是非常方便的,珍爱网已经为每个用户的详情页分配了一个id:album.zhenai.com/u/1159751830

代码优化

1、只建立一个client连接到elasticsearch,而不是每做一次存储动作都得重新创建客户端:

func ItemSaver() (chan engine.Item, error) {
//先创建一个客户端,之后传递到真正干活的save()函数
    client, err := elastic.NewClient(elastic.SetURL("http://3.226.45.27:9200"), elastic.SetSniff(false))
    if err != nil {
        return nil, err
    }
    out := make(chan engine.Item)
    go func() {
        ......
        for {
          err := save(client, item)
        }
       .......
    }()
    return out, nil
}

2、函数式编程的改造(没怎么懂,详见视频16-8)

func profileParser(name, gender, url string) engine.ParserFunc {
    return func(bytes []byte) engine.ParseResult {
        return ParseProfile(bytes, name, gender, url)
    }
}

3、index名称改为外部配置

之前index的名称是在save()函数的内部写死了的,elasticsearch中的index相当于关系型数据库中的database,应该由外部配置。因此,save()函数需要一个index名称参数,即ItemSaver()函数需要一个index名称参数,这样就可以在启动的时候配置index:

itemChan, err := persist.ItemSaver("dating_profile")

func save(client *elastic.Client, index string, item engine.Item) error {}