python分布式爬虫去重_python分布式爬虫 Cool·

一 介绍

原来scrapy的Scheduler维护的是本机的任务队列(存放Request对象及其回调函数等信息)+本机的去重队列(存放访问过的url地址)

155678a1b5e4665b4e2398d358af2577.png

所以实现分布式爬取的关键就是,找一台专门的主机上运行一个共享的队列比如Redis,

然后重写Scrapy的Scheduler,让新的Scheduler到共享队列存取Request,并且去除重复的Request请求,所以总结下来,实现分布式的关键就是三点:

#1、共享队列#2、重写Scheduler,让其无论是去重还是任务都去访问共享队列#3、为Scheduler定制去重规则(利用redis的集合类型)

以上三点便是scrapy-redis组件的核心功能

887760f5211f40d268cf61654988b060.png

#安装:

pip3 install scrapy-redis

#源码:

D:python3.6Libsite-packagesscrapy_redis

二、scrapy-redis组件

1、只使用scrapy-redis的去重功能

cf311673fd1f626f6990555892e26c58.png

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#一、源码:D:python3.6Libsite-packagesscrapy_redisdupefilter.py

#二、配置scrapy使用redis提供的共享去重队列

#2.1 在settings.py中配置链接Redis

REDIS_HOST = 'localhost' #主机名

REDIS_PORT = 6379 #端口

REDIS_URL = 'redis://user:pass@hostname:9001' #连接URL(优先于以上配置)

REDIS_PARAMS = {} #Redis连接参数

REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' #指定连接Redis的Python模块

REDIS_ENCODING = "utf-8" #redis编码类型 #默认配置:D:python3.6Libsite-packagesscrapy_redisdefaults.py

#2.2 让scrapy使用共享的去重队列

DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

#使用scrapy-redis提供的去重功能,查看源码会发现是基于Redis的集合实现的

#2.3、需要指定Redis中集合的key名,key=存放不重复Request字符串的集合

DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'

#源码:dupefilter.py内一行代码key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}

#2.4、去重规则源码分析dupefilter.py

defrequest_seen(self, request):

"""Returns True if request was already seen.

Parameters

----------

request : scrapy.http.Request

Returns

-------

bool

"""fp =self.request_fingerprint(request)

#This returns the number of values added, zero if already exists.

added =self.server.sadd(self.key, fp)

return added ==0

#2.5、将request请求转成一串字符后再存入集合

from scrapy.http importRequest

from scrapy.utils.request importrequest_fingerprint

req = Request(url='http://www.baidu.com')

result=request_fingerprint(req)

print(result) #75d6587d87b3f4f3aa574b33dbd69ceeb9eafe7b

#2.6、注意:

-URL参数位置不同时,计算结果一致;

-默认请求头不在计算范围,include_headers可以设置指定请求头

-示范:

from scrapy.utils importrequest

from scrapy.http importRequest

req = Request(url='http://www.baidu.com?name=8&id=1',callback=lambda x:print(x),cookies={'k1':'vvvvv'})

result1 = request.request_fingerprint(req,include_headers=['cookies',])

print(result)

req = Request(url='http://www.baidu.com?id=1&name=8',callback=lambda x:print(x),cookies={'k1':666})

result2 = request.request_fingerprint(req,include_headers=['cookies',])

print(result1 == result2) #True

使用共享去重队列+源码分析

2、使用scrapy-redis的去重+调度实现分布式爬取

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#1、源码:D:python3.6Libsite-packagesscrapy_redisscheduler.py

#2、settings.py配置

#Enables scheduling storing requests queue in redis.

SCHEDULER = "scrapy_redis.scheduler.Scheduler"

#调度器将不重复的任务用pickle序列化后放入共享任务队列,默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)

SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'

#对保存到redis中的request对象进行序列化,默认使用pickle

SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"

#调度器中请求任务序列化后存放在redis中的key

SCHEDULER_QUEUE_KEY = '%(spider)s:requests'

#是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空

SCHEDULER_PERSIST =True

#是否在开始之前清空 调度器和去重记录,True=清空,False=不清空

SCHEDULER_FLUSH_ON_START =False

#去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。如果没有则立刻返回会造成空循环次数过多,cpu占用率飙升

SCHEDULER_IDLE_BEFORE_CLOSE = 10

#去重规则,在redis中保存时对应的key

SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'

#去重规则对应处理的类,将任务request_fingerprint(request)得到的字符串放入去重队列

SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'

View Code

3、持久化

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#从目标站点获取并解析出数据后保存成item对象,会由引擎交给pipeline进行持久化/保存到数据库,scrapy-redis提供了一个pipeline组件,可以帮我们把item存到redis中

#1、将item持久化到redis时,指定key和序列化函数

REDIS_ITEMS_KEY = '%(spider)s:items'REDIS_ITEMS_SERIALIZER = 'json.dumps'

#2、使用列表保存item数据

View Code

4、从Redis中获取起始URL

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

scrapy程序爬取目标站点,一旦爬取完毕后就结束了,如果目标站点更新内容了,我们想重新爬取,那么只能再重新启动scrapy,非常麻烦

scrapy-redis提供了一种供,让scrapy从redis中获取起始url,如果没有scrapy则过一段时间再来取而不会关闭

这样我们就只需要写一个简单的脚本程序,定期往redis队列里放入一个起始url。

#具体配置如下

#1、编写爬虫时,起始URL从redis的Key中获取

REDIS_START_URLS_KEY = '%(name)s:start_urls'

#2、获取起始URL时,去集合中获取还是去列表中获取?True,集合;False,列表

REDIS_START_URLS_AS_SET = False #获取起始URL时,如果为True,则使用self.server.spop;如果为False,则使用self.server.lpop

View Code

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值