爬虫项目-数据存储
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 {}