美文网首页
使用scrapy_redis进行kuku漫画全站爬取

使用scrapy_redis进行kuku漫画全站爬取

作者: 小温侯 | 来源:发表于2018-07-21 23:07 被阅读160次

思路

虽然scrapy支持多线程,但是单机scrapy也是有性能瓶颈的。使用scrapy-redis可以将scrapy改造成分布式的爬虫架构。

改造的原理是什么?

对于原版的scrapy,scrapy-redis修改了四个部分:调度器(Scheduler)、去重(Dupefilter)、管道(Pipeline)、抓取器(Spider)。其核心是使用了redis代替了原本的queue,因为redis可以作为一个消息队列,这样多个爬虫实例就可以通过共享消息队列来实现分布式了。

要实现分布式主要要解决的问题无非就是几个:

  • 任务分发,让每个实例知道自己要做什么。
  • 共享消息队列,不能出现重复爬取的情况。

这里Scheduler就是用来分发任务的;如果配置了去重,Dupefilter会负责维护一个集合,包含抓取过的url,避免重复抓取;Pipeline可以是自己写的,也可以使用RedisPipeline将结果存储在redis中;最后Spider中需要实现具体爬虫功能的部分,也是写代码的地方。

如果一切顺利,你可以看到Redis中会出现三个序列:

scrapy_redis_db11.jpg

Redis中的key都是按照spidername:word的形式来命名的,其中Dupefiler用来去重,需要合适的配置项。Requests包含了所有爬取的序列,是所有各个爬虫节点的任务来源;同时所有在爬取过程中yield出的新request都会被放入这个序列。最后如果你在配置项中使用了RedisPipeline,Spider返回的结果会被存储在Items中。

除了这三个序列,有时候会有一个名为start_urls序列。如果你的爬虫只写了处理一个URL时的代码,但是没有其实URL,也就是说爬虫不知道从哪个url开始入手(有了第一个URL之后就会像蛛网一样衍生出更多的URL)。这时候你会需要用到redis-cli(redis的客户端程序)来手动向redis中添加一条url:

redis-cli -h redis_addr -p redis_port -n redis_db# lpush spidername:start_urls CustomURL

这个项目里我把start_urls直接写在代码里了。

怎么部署这个分布式爬虫?

scrapy本身支持并发和异步IO,通过修改其配置项CONCURRENT_REQUESTS来实现,默认为16。因为GIL的存在,如果配置合理,此时的爬虫会占满你一个CPU核心;通过手动执行N个爬虫实例(scrapy crawl comicCrawler),N一般是你的CPU核心数,你可以充分发挥出一台机器的性能。同样,在其他机器上,你可以执行相同的操作,所有的爬虫实例都共享一个redis数据库。通过这种多进程+多线程的模式,理论上可以最大限度地发挥机器的能力。

具体怎么做?

这里只包含了从scrapy转scrapy-redis需要修改的地方。

修改Setting.py

在官网上提供了一个scrapy-redis的完整配置文件,可以按需修改,一般来说,有几个是必须的,你需要在原本的Setting.py的基础上,增加:

SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER_PERSIST = True
REDIS_HOST = '34.229.250.31'
REDIS_PORT = 6379
REDIS_PARAMS = {
    'db': 11
}
LOG_LEVEL = 'DEBUG'

几个比较重要参数是:

  • SCHEDULER`是核心部分,scrapy-redis重写了调度器用于管理分布式架构。
  • DUPEFILTER_CLASS用用于去重。
  • SCHEDULER_PERSIST允许配置是否可以暂停\继续爬虫,互有利弊。
  • SCHEDULER_QUEUE_CLASS是队列的类型,有三种:PriorityQueue(默认), FifoQueueLifoQueue
  • REDIS系列:Redis有几种配置方法,可以使用REDIS_HOST+REDIS_PORT组合,也可使用REDIS_URL,也可以通过REDIS_PARAMS字典对具体的某个参数做调整,比如说,例子里,我选择使用redis数据库中的第11个实例,避免与其他爬虫冲突。
  • 其他的一般保持默认即可。

除了新增的几个配置项,如果你选择将最终的结果存入redis,你需要向ITEM_PIPELINES中添加scrapy_redis.pipelines.RedisPipeline管道。

ITEM_PIPELINES = {
    'comicspider.pipelines.ComicspiderPipeline': 300,
    'scrapy_redis.pipelines.RedisPipeline': 400
}

修改spider文件

所有涉及到的spider文件都需要修改,scarpy的spider类一般继承于scrapy.Spider,这里要调整为scrapy_redis.spiders。然后,没了。

这里没了是我这个例子中,不需要修改其他代码了。但是每个人可能写的spider的结构不一样,因此调整起来可能还有一点出入。

比如说:

  • 在我的例子中,我没有使用scrapy的Rules部件,因此在多页面爬取的时候,我需要为每一类请求都指定好合适的callback。你可以通过配置好Rules,它可以通过匹配请求的URL,然后给每一类URL绑定相应的回调函数。
  • 如果你没有在代码中配置start_urls,你需要指定一个redis的key值用来之后向其中推送请求,redis_key='comicCrawler:start_urls'

代码和效果

代码我放在GitHub上了,我的笔记本是八核的,然后再加一个云主机,我一共起了9个实例,每个实例的并发数是16。最后跑出来,大概一个小时可以抓到27W结果。

相关文章

网友评论

      本文标题:使用scrapy_redis进行kuku漫画全站爬取

      本文链接:https://www.haomeiwen.com/subject/ohbkmftx.html