前兩天咱們寫了單任務版爬蟲爬取了珍愛網用戶信息,那麼它的性能如何呢?前端
咱們能夠經過網絡利用率看一下,咱們用任務管理器中的性能分析窗口能夠看到下載速率大概是保持在了200kbps左右,這能夠說是至關慢了。java
咱們針對來經過分析單任務版爬蟲的設計來看下:python
從上圖咱們能夠看出,engine將request從任務隊列取出來,送到Fetcher取獲取資源,等待數據返回,而後將返回的數據送到Parser去解析,等待其返回,把返回的request再加到任務隊列裏,同時把item打印出來。面試
慢就慢在了沒有充分利用網絡資源,其實咱們能夠同時發送多個Fetcher和Pareser,等待其返回的同時,能夠去作其餘的處理。這一點利用go的併發語法糖很容易實現。spring
上圖中,Worker是Fetcher和Parser的合併,Scheduler將不少Request分發到不一樣的Worker,Worker將Request和Items返回到Engine,Items打印出來,再把Request放到調度器裏。編程
基於此用代碼實現:segmentfault
Engine:網絡
package engine import ( "log" ) type ConcurrentEngine struct { Scheduler Scheduler WokerCount int } type Scheduler interface { Submit(Request) ConfigureMasterWorkerChan(chan Request) } func (e *ConcurrentEngine) Run(seeds ...Request) { in := make(chan Request) out := make(chan ParserResult) e.Scheduler.ConfigureMasterWorkerChan(in) //建立Worker for i := 0; i < e.WokerCount; i++ { createWorker(in, out) } //任務分發給Worker for _, r := range seeds { e.Scheduler.Submit(r) } for { //打印out的items result := <- out for _, item := range result.Items { log.Printf("Get Items: %v\n", item) } //將out裏的Request送給Scheduler for _, r := range result.Requests { e.Scheduler.Submit(r) } } } //workerConut goroutine to exec worker for Loop func createWorker(in chan Request, out chan ParserResult) { go func() { for { request := <-in parserResult, err := worker(request) //發生了錯誤繼續下一個 if err != nil { continue } //將parserResult送出 out <- parserResult } }() }
Scheduler:併發
package scheduler import "crawler/engine" //SimpleScheduler one workChan to multi worker type SimpleScheduler struct { workChan chan engine.Request } func (s *SimpleScheduler) ConfigureMasterWorkerChan(r chan engine.Request) { s.workChan = r } func (s *SimpleScheduler) Submit(r engine.Request) { go func() { s.workChan <- r }() }
Worker:函數
func worker(r Request) (ParserResult, error) { log.Printf("fetching url:%s\n", r.Url) //爬取數據 body, err := fetcher.Fetch(r.Url) if err != nil { log.Printf("fetch url: %s; err: %v\n", r.Url, err) //發生錯誤繼續爬取下一個url return ParserResult{}, err } //解析爬取到的結果 return r.ParserFunc(body), nil }
main函數:
package main import ( "crawler/engine" "crawler/zhenai/parser" "crawler/scheduler" ) func main() { e := &engine.ConcurrentEngine{ Scheduler: &scheduler.SimpleScheduler{}, WokerCount :100, } e.Run( engine.Request{ Url: "http://www.zhenai.com/zhenghun", ParserFunc: parser.ParseCityList, }) }
這裏開啓100個Worker,運行後再次查看網絡利用率,變爲3M以上。
因爲代碼篇幅較長,須要的同窗能夠關注公衆號回覆:go爬蟲 獲取。
本公衆號免費提供csdn下載服務,海量IT學習資源,若是你準備入IT坑,勵志成爲優秀的程序猿,那麼這些資源很適合你,包括但不限於java、go、python、springcloud、elk、嵌入式 、大數據、面試資料、前端 等資源。同時咱們組建了一個技術交流羣,裏面有不少大佬,會不定時分享技術文章,若是你想來一塊兒學習提升,能夠公衆號後臺回覆【2】,免費邀請加技術交流羣互相學習提升,會不按期分享編程IT相關資源。
掃碼關注,精彩內容第一時間推給你