爬取数据的收集。
使用管道收集数据,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,跟据输出类型判断调用哪个函数。具体的保存方法可以查看函数的详细信息。