前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >爬虫性能分析及优化

爬虫性能分析及优化

作者头像
我的小碗汤
发布2018-08-22 10:50:44
3210
发布2018-08-22 10:50:44
举报
文章被收录于专栏:我的小碗汤我的小碗汤

前两天我们写了单任务版爬虫爬取了珍爱网用户信息,那么它的性能如何呢?

我们可以通过网络利用率看一下,我们用任务管理器中的性能分析窗口可以看到下载速率大概是保持在了200kbps左右,这可以说是相当慢了。

我们针对来通过分析单任务版爬虫的设计来看下:

从上图我们可以看出,engine将request从任务队列取出来,送到Fetcher取获取资源,等待数据返回,然后将返回的数据送到Parser去解析,等待其返回,把返回的request再加到任务队列里,同时把item打印出来。

慢就慢在了没有充分利用网络资源,其实我们可以同时发送多个Fetcher和Pareser,等待其返回的同时,可以去做其他的处理。这一点利用go的并发语法糖很容易实现。

上图中,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: %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以上。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 进击云原生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档