Py007-02-13分布式爬虫简介

分布式爬虫概念

  1. 多台机器可以执行同一个爬虫程序,实现网站数据的分布爬取
  2. 原生的scrapy是不可以实现分布式爬虫的
    • 调度器无法在多台机器共享
    • 管道无法共享
  3. scrapy-redis组件:专门为scrapy开发的一套组件。
    • 该组件可以让scrapy实现分布式

scrapy-redis组件

1
2
# 下载
pip install scrapy-redis

分布式爬取的流程

1. redis配置(事先安装好redis)

1
2
3
4
5
6
我的是 5.0版本
切换到 redis5.0.0 的文件目录
找到redis.conf

- 注释掉 bind 127.0.0.1 ::1 ==> # bind 127.0.0.1 ::1
- 关闭保护模式 protected-mode yes ==> protected-mode no

2. redis服务器的开启 基于配置文件进行开启

1
./redis-server  ../redis.conf

再开一个客户端

1
./redis-cli

3. 创建scrapy工程

1
2
3
scrapy startproject redisPro

cd redisPro

注意: 此时要创建一个基于 crawlSpider 的应用

1
2
# 我们现在就爬取  糗百 糗图的数据
scrapy genspider -t crawl qiubai www.qiushibaike.com/pic

4. 导入RedisCrawlSpider 类 ,然后将爬虫文件修改成基于该类的源文件

spiders/qiubai.py

1
2
3
4
5
6
7
...

from scrapy_redis.spiders import RedisCrawlSpider
# class QiubaiSpider(CrawlSpider):
# 修改原先继承的 CrawlSpider 为 RedisCrawlSpider
class QiubaiSpider(RedisCrawlSpider):
...

5. 将 start_urls 修改成 redis_key = ‘调度器队列的名称’

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
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from scrapy_redis.spiders import RedisCrawlSpider
class QiubaiSpider(CrawlSpider):
name = 'qiubai'

# 此时要注释掉 allowed_domains 和 start_urls
# allowed_domains = ['https://www.qiushibaike.com/pic']
# start_urls = ['http://https://www.qiushibaike.com/pic/']

# redis_key 它代表调度器队列的名称
redis_key = 'qiubaispider' # 该行代码和 start_urls 一样
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)

def parse_item(self, response):
i = {}
#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
#i['name'] = response.xpath('//div[@id="name"]').extract()
#i['description'] = response.xpath('//div[@id="description"]').extract()
return i

6. 将项目的管道和调度器配置成基于 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
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from scrapy_redis.spiders import RedisCrawlSpider
class QiubaiSpider(CrawlSpider):
name = 'qiubai'

# 此时要注释掉 allowed_domains 和 start_urls
# allowed_domains = ['https://www.qiushibaike.com/pic']
# start_urls = ['http://https://www.qiushibaike.com/pic/']

# redis_key 它代表调度器队列的名称
redis_key = 'qiubaispider' # 该行代码和 start_urls 一样

# 糗百糗图 底部分页器
'''
<a href="/pic/page/2?s=5145873" rel="nofollow">
解析规则
/pic/page/2/?s=5145873
为啥有个s=5145873 刷新下页面后 发现 s会变化 所以可以把s参数忽略
/pic/page/\d+
'''
link = LinkExtractor(allow=r'/pic/page/\d+')

rules = (
Rule(link, callback='parse_item', follow=True),
)

def parse_item(self, response):
div_list = response.xpath('//div[@id="content-left"]/div')

for div in div_list:
img_url = 'https:'+ div.xpath('.//div[@class="thumb"]/a/img/@src').extract_first()

pass

定义items

items.py

1
2
3
4
5
6
7
8
# -*- coding: utf-8 -*-

import scrapy

class RedisproItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
img_url = scrapy.Field()

导入items类

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
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from scrapy_redis.spiders import RedisCrawlSpider
from redisPro.items import RedisproItem

class QiubaiSpider(RedisCrawlSpider):
name = 'qiubai'

# 此时要注释掉 allowed_domains 和 start_urls
# allowed_domains = ['https://www.qiushibaike.com/pic']
# start_urls = ['http://https://www.qiushibaike.com/pic/']

# redis_key 它代表调度器队列的名称
redis_key = 'qiubaispider' # 该行代码和 start_urls 一样

# 糗百糗图 底部分页器
'''
<a href="/pic/page/2?s=5145873" rel="nofollow">
解析规则
/pic/page/2/?s=5145873
为啥有个s=5145873 刷新下页面后 发现 s会变化 所以可以把s参数忽略
/pic/page/\d+
'''
link = LinkExtractor(allow=r'/pic/page/\d+')

rules = (
Rule(link, callback='parse_item', follow=True),
)

def parse_item(self, response):
div_list = response.xpath('//div[@id="content-left"]/div')

for div in div_list:
img_url = 'https:'+ div.xpath('.//div[@class="thumb"]/a/img/@src').extract_first()
item = RedisproItem()
item['img_url'] = img_url

# 现在是 scrapy的管道, 此时要使用 scrapy_redis 提供的管道 去settings.py 里修改配置
yield item

使用scrapy_redis 提供的管道 RedisPipeline

  • 修改settings.py
1
2
3
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,
}

同时对 RedisPipeline 的调度器进行配置

1
2
3
4
5
6
7
8
9
10
11
# 使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否允许暂停
SCHEDULER_PERSIST = True

'''
SCHEDULER_PERSIST = True 代表 如果分布的一台机器宕机了,
当机器重启后 继续爬虫时, 从上次失败的地方开始爬取,而不是从头爬取
'''

7. REDIS_HOST 配置

爬虫程序为分布式的所以不仅部署在你的机子上

1
2
REDIS_HOST = 'redis服务的ip地址'
REDIS_PORT = 6379

8.执行爬虫文件

1
2
3
4
5
6
# 此时要定位到 爬虫文件的目录 
cd redisPro/spiders
scrapy runspider qiubai.py

# 最后你发现有一句
linstening at xxxx 代表监听在一个端口

还记得刚刚开启的redis-cli吗?

此时你要告诉redis-spider 起始url

还记得刚刚起名的 redis_key 吗?

1
redis_key = 'qiubaispider'

糗百-糗图的起始url

1
https://www.qiushibaike.com/pic/

在redis-cli里 输入如下信息

1
2
3
4
5
lpush 队列名称  起始url

# 我们当前应用为

lpush qiubaispider https://www.qiushibaike.com/pic/
1
2
3
4
5
6
7
8
9
[scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'finish_reason': 'finished',
'finish_time': datetime.datetime(2018, 12, 2, 15, 26, 8, 106343),
'log_count/DEBUG': 1,
'log_count/INFO': 7,
'memusage/max': 49971200,
'memusage/startup': 49971200,
'start_time': datetime.datetime(2018, 12, 2, 15, 26, 8, 96989)}
2018-12-02 23:26:08 [scrapy.core.engine] INFO: Spider closed (finished)

出现这个就去看看 继承的类是否是 RedisCrawlSpider

出现这个就去看看 继承的类是否是 RedisCrawlSpider

出现这个就去看看 继承的类是否是 RedisCrawlSpider

1
class QiubaiSpider(RedisCrawlSpider)