go爬虫设计(单机版)

爬虫总体架构:

爬虫实现方案:

 

单机版

单机版架构

主要模块有:

  • 处理引擎(Engine)
  • 解析器(Parser)
  • 下载器(Fetcher)

流程:

请求处理模型=请求URL + URL对应的Parse

  • 0: 先向引擎发起一个种子请求处理模型(URL+Parse)
  • 1: 引擎将请求放入任务队列中
  • 2: 引擎从任务队列中取出请求处理模型(如果还有请求的话)
  • 3. 将请求的URL发送给下载器
  • 4. 下载器根据URL返回对应的html内容(utf-8编码的文本)给引擎
  • 5. 引擎将html内容转发给请求处理模型对应的解析器(Parse)
  • 6. 解析器返回处理后的得到的新的请求处理模型及Item信息(URL+Parse,Item)
  • 7. 重复1-6

单机版总体算法:

目标:爬取每个城市第一页的所有用户

URL : http://www.zhenai.com/zhenghun

所以需要三个解析器:

  • 城市列表解析器
  • 城市解析器
  • 用户解析器

解析器的

  • 输入:utf8编码的文本
  • 输出:Request{URL,对应Parse}列表 ,Item列表

输出一定是一个Request列表,因为一个城市的输入,会返回多个用户的输出。

Item是在返回Request列表时,里面的每个Request对应的一些附加信息

代码结构

重点在于Request请求模型的设计

type Request struct {
	Url 	    string
	ParserFunc  func([]byte) ParseResult  // 参数[]byte就是下载器根据这个URL返回的内容
}

type ParseResult struct {
	Requests 	[]Request
	Items 		[]interface{}
}
  • 就是说一个请求处理模型包括URL和其对应的Parse
  • 解析器的返回模型包括请求处理模型列表和Item列表

当然有时候,解析器之间是需要参数传递的,比如城市解析器传递参数给用户解析器

有两种处理方式:

1. ParseFunc加参数

type Request struct {
	Url 	    string
	ParserFunc  func([]byte, string) ParseResult
}

2. 使用函数式编程

一般因为ParseFunc的结构,定义的解析器如下

func ParseProfile(contents []byte) engine.ParseResult{
  ...
}

但是如果城市解析器,需要传递用户名称给用户解析器,而使用第一种方法对其它解析器影响较大的话,就可以使用如下手段:

result.Requests = append(
  result.Requests, engine.Request{
    Url: 		string(m[1]),
    ParserFunc: func(c []byte) engine.ParseResult {
      return ParseProfile(c ,userName)
   },
})

然后定义用户解析器的时候

func ParseProfile(contents []byte, name string) engine.ParseResult{
  ...
}

引擎处理:

func Run(seeds ...Request){
	var requests  []Request

	for _, r := range seeds{
		requests = append(requests, r)
	}


	for len(requests) > 0 {
		r := requests[0]           //获取任务列表中的第一个请求处理模型
		requests = requests[1:]

		log.Printf("Fetching %s",r.Url)

		body, err := fetcher.Fetch(r.Url) //将请求处理模型的URL发送给下载器,得到下载的内容
		if err != nil {
			log.Printf("Fetching error,  url %s, %s", r.Url, err)
			continue
		}
		parseResult := r.ParserFunc(body)  //将下载器返回的内容交给请求处理模型的Parse处理
		requests = append(requests, parseResult.Requests...)  //将返回的新的请求处理模型列表加入任务列表中

		//在一次请求处理结束后
		for _, item := range parseResult.Items {
			log.Printf("Got item %v", item)
		}
	}
	
}

猜你喜欢

转载自blog.csdn.net/zengchen73/article/details/81109581