爬虫性能分析及优化

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

我们可以通过网络利用率看一下,我们用任务管理器中的性能分析窗口可以看到下载速率大概是保持在了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以上。

原文发布于微信公众号 - 我的小碗汤(mysmallsoup)

原文发表时间:2018-06-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

IntentService简介

在manifest中声明服务 和activity、content provider一样,服务也必须要在AndroidManifest文件中进行声明是<app...

208100
来自专栏Kubernetes

解析Kubernetes 1.8中的

Author: xidianwangtao@gmail.com Kubernetes 1.8中对scheduler的更新 【Alpha】支持定义Prior...

31180
来自专栏图像识别与深度学习

Bluetooth4_3运行流程(连接发射器SN00000009)

31260
来自专栏大数据挖掘DT机器学习

Scrapy爬虫轻松抓取网站数据(以bbs为例,提供源码)

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。   其最初是为了页面抓取...

47590
来自专栏玄魂工作室

Scrapy爬虫入门

快两周了,还没缓过来劲,python 黑帽的系列教程今天才开始捡起来。不过工作又要忙了,晚上照顾玄小魂,白天敲代码,抽时间写文章,真的有点心力交瘁。不过没关系,...

33370
来自专栏向治洪

保证service存活

Android开发的过程中,每次调用startService(Intent)的时候,都会调用该Service对象的onStartCommand(Intent,i...

20170
来自专栏hbbliyong

WPF刷新界面之坎坷路

   项目需要一个硬件检测功能,需要用到界面刷新,刚开始想用个定时器,对检测过的硬设定时添加后刷新界面。 但是很遗憾,定时器并不能进行刷新。后台检测List数据...

40470
来自专栏Java帮帮-微信公众号-技术文章全总结

Web-第二十三天 Web商城实战三【悟空教程】

<a href="${pageContext.request.contextPath}/OrderServlet?method=findByUid">我的订单<...

19010
来自专栏小怪聊职场

爬虫课堂(二十)|编写Spider之使用Item Pipeline处理数据

29150
来自专栏CodingBlock

Android插件化-RePlugin项目集成与使用

前言:前一段时间新开源了一种全面插件化的方案-- RePlugin,之前一种都在关注 DroidPlugin 并且很早也在项目中试用了,但最终没有投入到真正的生...

49000

扫码关注云+社区

领取腾讯云代金券