爬取數據的收集。
使用管道收集數據,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,跟據輸出類型判斷調用哪個函數。具體的保存方法可以查看函數的詳細信息。