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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章