分布式爬虫

释放双眼,带上耳机,听听看~!

一 介绍

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

分布式爬虫

所以实现分布式爬取的关键就是,找一台专门的主机上运行一个共享的队列比如Redis,
然后重写Scrapy的Scheduler,让新的Scheduler到共享队列存取Request,并且去除重复的Request请求,所以总结下来,实现分布式的关键就是三点:


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

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

 分布式爬虫


1
2
3
4
5
6
1#安装:
2pip3 install scrapy-redis
3
4#源码:
5D:\python3.6\Lib\site-packages\scrapy_redis
6

二、scrapy-redis组件

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

分布式爬虫

分布式爬虫分布式爬虫


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
1#一、源码:D:\python3.6\Lib\site-packages\scrapy_redis\dupefilter.py
2
3
4
5#二、配置scrapy使用redis提供的共享去重队列
6
7#2.1 在settings.py中配置链接Redis
8REDIS_HOST = 'localhost'                            # 主机名
9REDIS_PORT = 6379                                   # 端口
10REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
11REDIS_PARAMS  = {}                                  # Redis连接参数
12REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # 指定连接Redis的Python模块
13REDIS_ENCODING = "utf-8"                            # redis编码类型  
14# 默认配置:D:\python3.6\Lib\site-packages\scrapy_redis\defaults.py
15
16
17#2.2 让scrapy使用共享的去重队列
18DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
19#使用scrapy-redis提供的去重功能,查看源码会发现是基于Redis的集合实现的
20
21
22#2.3、需要指定Redis中集合的key名,key=存放不重复Request字符串的集合
23DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
24#源码:dupefilter.py内一行代码key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}
25
26
27#2.4、去重规则源码分析dupefilter.py
28def request_seen(self, request):
29    """Returns True if request was already seen.
30
31    Parameters
32    ----------
33    request : scrapy.http.Request
34
35    Returns
36    -------
37    bool
38
39    """
40    fp = self.request_fingerprint(request)
41    # This returns the number of values added, zero if already exists.
42    added = self.server.sadd(self.key, fp)
43    return added == 0
44
45
46#2.5、将request请求转成一串字符后再存入集合
47
48from scrapy.http import Request
49from scrapy.utils.request import request_fingerprint
50
51req = Request(url='http://www.baidu.com')
52result=request_fingerprint(req)
53print(result) #75d6587d87b3f4f3aa574b33dbd69ceeb9eafe7b
54
55
56#2.6、注意:
57    - URL参数位置不同时,计算结果一致;
58    - 默认请求头不在计算范围,include_headers可以设置指定请求头
59    - 示范:
60    from scrapy.utils import request
61    from scrapy.http import Request
62    
63    req = Request(url='http://www.baidu.com?name=8&id=1',callback=lambda x:print(x),cookies={'k1':'vvvvv'})
64    result1 = request.request_fingerprint(req,include_headers=['cookies',])
65    
66    print(result)
67    
68    req = Request(url='http://www.baidu.com?id=1&name=8',callback=lambda x:print(x),cookies={'k1':666})
69    
70    result2 = request.request_fingerprint(req,include_headers=['cookies',])
71    
72    print(result1 == result2) #True
73

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

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

分布式爬虫分布式爬虫


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1#1、源码:D:\python3.6\Lib\site-packages\scrapy_redis\scheduler.py
2
3
4#2、settings.py配置
5
6# Enables scheduling storing requests queue in redis.
7SCHEDULER = "scrapy_redis.scheduler.Scheduler"      
8
9# 调度器将不重复的任务用pickle序列化后放入共享任务队列,默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)              
10SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'          
11
12# 对保存到redis中的request对象进行序列化,默认使用pickle
13SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  
14
15# 调度器中请求任务序列化后存放在redis中的key              
16SCHEDULER_QUEUE_KEY = '%(spider)s:requests'    
17
18# 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空                    
19SCHEDULER_PERSIST = True      
20
21# 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空                                    
22SCHEDULER_FLUSH_ON_START = False    
23
24# 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。如果没有则立刻返回会造成空循环次数过多,cpu占用率飙升                                
25SCHEDULER_IDLE_BEFORE_CLOSE = 10          
26
27# 去重规则,在redis中保存时对应的key                        
28SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'      
29
30# 去重规则对应处理的类,将任务request_fingerprint(request)得到的字符串放入去重队列            
31SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
32

View Code

3、持久化

分布式爬虫分布式爬虫


1
2
3
4
5
6
7
8
1#从目标站点获取并解析出数据后保存成item对象,会由引擎交给pipeline进行持久化/保存到数据库,scrapy-redis提供了一个pipeline组件,可以帮我们把item存到redis中
2    
3#1、将item持久化到redis时,指定key和序列化函数
4REDIS_ITEMS_KEY = '%(spider)s:items'
5REDIS_ITEMS_SERIALIZER = 'json.dumps'
6
7#2、使用列表保存item数据
8

View Code

4、从Redis中获取起始URL

分布式爬虫分布式爬虫


1
2
3
4
5
6
7
8
9
10
11
12
1scrapy程序爬取目标站点,一旦爬取完毕后就结束了,如果目标站点更新内容了,我们想重新爬取,那么只能再重新启动scrapy,非常麻烦
2scrapy-redis提供了一种供,让scrapy从redis中获取起始url,如果没有scrapy则过一段时间再来取而不会关闭
3这样我们就只需要写一个简单的脚本程序,定期往redis队列里放入一个起始url。
4
5#具体配置如下
6
7#1、编写爬虫时,起始URL从redis的Key中获取
8REDIS_START_URLS_KEY = '%(name)s:start_urls'
9    
10#2、获取起始URL时,去集合中获取还是去列表中获取?True,集合;False,列表
11REDIS_START_URLS_AS_SET = False    # 获取起始URL时,如果为True,则使用self.server.spop;如果为False,则使用self.server.lpop
12

View Code

 

给TA打赏
共{{data.count}}人
人已打赏
安全经验

职场中的那些话那些事

2021-9-24 20:41:29

安全经验

elk+redis 搭建nginx日志分析平台

2021-11-28 16:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索