golang爬虫项目Pholcus源码分析(五)

爬取数据的收集。

使用管道收集数据,https://github.com/henrylee2cn/pholcus/blob/master/app/crawler/crawler.go#L57

// 任务执行入口
func (self *crawler) Run() {
	// 预先启动数据收集/输出管道
	self.Pipeline.Start()

	// 运行处理协程
	c := make(chan bool)
	go func() {
		self.run()
		close(c)
	}()

	// 启动任务
	self.Spider.Start()

	<-c // 等待处理协程退出

	// 停止数据收集/输出管道
	self.Pipeline.Stop()
}

https://github.com/henrylee2cn/pholcus/blob/master/app/pipeline/collector/collector.go#L87

// 启动数据收集/输出管道
func (self *Collector) Start() {
	// 启动输出协程
	go func() {
		dataStop := make(chan bool)
		fileStop := make(chan bool)

		go func() {
			defer func() {
				recover()
				// println("DataChanStop$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")
			}()
			for data := range self.DataChan {
				// 缓存分批数据
				self.dataDocker = append(self.dataDocker, data)

				// 未达到设定的分批量时继续收集数据
				if len(self.dataDocker) < cache.Task.DockerCap {
					continue
				}

				// 执行输出
				self.dataBatch++
                // collector实现了pipline接口。
				self.outputData()
			}
			// 将剩余收集到但未输出的数据输出
			self.dataBatch++
			self.outputData()
			close(dataStop)
		}()

		go func() {
			defer func() {
				recover()
				// println("FileChanStop$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")
			}()
			// 只有当收到退出通知并且通道内无数据时,才退出循环
			for file := range self.FileChan {
				atomic.AddUint64(&self.fileBatch, 1)
				self.wait.Add(1)
				go self.outputFile(file)
			}
			close(fileStop)
		}()

		<-dataStop
		<-fileStop
		// println("OutputWaitStopping++++++++++++++++++++++++++++++++")

		// 等待所有输出完成
		self.wait.Wait()
		// println("OutputStopped$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")

		// 返回报告
		self.Report()
	}()
}

收集完成后停止管道

// 停止
func (self *Collector) Stop() {
	go func() {
		defer func() {
			recover()
		}()
		close(self.DataChan)
	}()
	go func() {
		defer func() {
			recover()
		}()
		close(self.FileChan)
	}()
}

collect的结构,包含了几种输出方式

// 结果收集与输出
type Collector struct {
	*spider.Spider                    //绑定的采集规则
	DataChan       chan data.DataCell //文本数据收集通道
	FileChan       chan data.FileCell //文件收集通道
	dataDocker     []data.DataCell    //分批输出结果缓存
	outType        string             //输出方式
	// size     [2]uint64 //数据总输出流量统计[文本,文件],文本暂时未统计
	dataBatch   uint64 //当前文本输出批次
	fileBatch   uint64 //当前文件输出批次
	wait        sync.WaitGroup
	sum         [4]uint64 //收集的数据总数[上次输出后文本总数,本次输出后文本总数,上次输出后文件总数,本次输出后文件总数],非并发安全
	dataSumLock sync.RWMutex
	fileSumLock sync.RWMutex
}

self.outputData()

https://github.com/henrylee2cn/pholcus/blob/master/app/pipeline/collector/output_data.go#L16

// 文本数据输出
func (self *Collector) outputData() {
	defer func() {
		// 回收缓存块
		self.resetDataDocker()
	}()

	// 输出
	dataLen := uint64(len(self.dataDocker))
	if dataLen == 0 {
		return
	}

	defer func() {
		if p := recover(); p != nil {
			logs.Log.Informational(" * ")
			logs.Log.App(" *     Panic  [数据输出:%v | KEYIN:%v | 批次:%v]   数据 %v 条! [ERROR]  %v\n",
				self.Spider.GetName(), self.Spider.GetKeyin(), self.dataBatch, dataLen, p)
		}
	}()

	// 输出统计
	self.addDataSum(dataLen)

	// 根据输出类型执行输出
	err := DataOutput[self.outType](self)

	logs.Log.Informational(" * ")
	if err != nil {
		logs.Log.App(" *     Fail  [数据输出:%v | KEYIN:%v | 批次:%v]   数据 %v 条! [ERROR]  %v\n",
			self.Spider.GetName(), self.Spider.GetKeyin(), self.dataBatch, dataLen, err)
	} else {
		logs.Log.App(" *     [数据输出:%v | KEYIN:%v | 批次:%v]   数据 %v 条!\n",
			self.Spider.GetName(), self.Spider.GetKeyin(), self.dataBatch, dataLen)
		self.Spider.TryFlushSuccess()
	}
}

DataOutPut

如图,每种保存方式下的init方法中都向DataOutPut中添加了相关的处理函数,参数为*Collect,跟据输出类型判断调用哪个函数。具体的保存方法可以查看函数的详细信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值