爬蟲性能分析及優化

  • 2019 年 10 月 18 日
  • 筆記

前兩天我們寫了單任務版爬蟲爬取了珍愛網用戶資訊,那麼它的性能如何呢?

我們可以通過網路利用率看一下,我們用任務管理器中的性能分析窗口可以看到下載速率大概是保持在了200kbps左右,這可以說是相當慢了。

image

我們針對來通過分析單任務版爬蟲的設計來看下:

image

從上圖我們可以看出,engine將request從任務隊列取出來,送到Fetcher取獲取資源,等待數據返回,然後將返回的數據送到Parser去解析,等待其返回,把返回的request再加到任務隊列里,同時把item列印出來。

慢就慢在了沒有充分利用網路資源,其實我們可以同時發送多個Fetcher和Pareser,等待其返回的同時,可以去做其他的處理。這一點利用go的並發語法糖很容易實現。

image

上圖中,Worker是Fetcher和Parser的合併,Scheduler將很多Request分發到不同的Worker,Worker將Request和Items返回到Engine,Items列印出來,再把Request放到調度器里。

基於此用程式碼實現:

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: %vn", 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:%sn", r.Url)   //爬取數據   body, err := fetcher.Fetch(r.Url)     if err != nil {     log.Printf("fetch url: %s; err: %vn", 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以上。

image

由於程式碼篇幅較長,需要的同學可以關注公眾號回復:go爬蟲 獲取。



本公眾號免費提供csdn下載服務,海量IT學習資源,如果你準備入IT坑,勵志成為優秀的程式猿,那麼這些資源很適合你,包括但不限於java、go、python、springcloud、elk、嵌入式 、大數據、面試資料、前端 等資源。同時我們組建了一個技術交流群,裡面有很多大佬,會不定時分享技術文章,如果你想來一起學習提高,可以公眾號後台回復【2】,免費邀請加技術交流群互相學習提高,會不定期分享編程IT相關資源。


掃碼關注,精彩內容第一時間推給你

image