文章目录
- Scrapy框架
- 1.增量爬虫
- 2.分布式爬虫
Scrapy框架
1.增量爬虫
- 实现思路
利用redis集合数据类型
1.获取到url后进行判断 是否重复???
2.第一次爬取到数据,爬取完成写入该记录...
(两个点: 必须要没有爬过的数据 在这个基础之上做
(1)数据准备入到管道之前
(2)数据入库之前
)
优点:
Redis查询速度快 相比: Mysql 文件 Mongdb ...
- 信号代码实现 配置打开关闭
@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
s = cls()
s._set_crawler(crawler) # -> 参考父类中的写法
# 定义两个信号
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
crawler.signals.connect(s.spider_closed, signal=signals.spider_closed)
return s
def spider_opened(self, spider):
self.conn = redis.Redis(host="127.0.0.1", port=6379, db=3, password="123456", decode_responses=True)
def spider_closed(self, spider):
self.conn.save() # 存储到硬盘
self.conn.close() # 关闭连接
- 集合去重逻辑
if self.conn.sismerber(key, href?):
continue
成功请求获取数据
添加该记录 url相当于就是主键 or 管道pipelines设置 process_item()
open_spider/ close_spider 两个hook方法 上下文管理
self.conn.sadd(key, url)
yield {
xxx: yyy
}
- 定时任务 while + 进程(scrapy会帮我们自动杀死process)
import time
from scrapy.cmdline import execute
from multiprocessing import Process
from datetime import datetime
def task():
# sys.exit(0)
execute("scrapy crawl fuck".split())
if __name__ == '__main__':
# 定时任务 time.seep()
while 1:
process = Process(target=task)
process.start()
print(f"正在等待执行中..... 当前时间=====> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
time.sleep(60 * 2) # 两分钟一次
process.join()
2.分布式爬虫
- 核心思想
利用多个scrapy程序对同一个任务进行分解,模拟出多个worker干同一任务的场景。直到完成所有的任务。
1.单个scrapy实现去重逻辑: set() + 指纹sha1 创建request的时候设置的是否去重?
2.多个scrapy实现去重逻辑: 共享一个Redis 集合 + 队列(列表lpush brpop) 消息队列
针对处理我们的耗时任务 各个worker之间是独立的不会产生影响!
核心: 依赖Redis MQ其实也可以 Kafka Celery 处理异步任务的框架
(1)去重集合
(2)请求队列
-
简单配置Redis分布式爬虫
# settings.py # 调度器类 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 过滤器类. DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # save操作 SCHEDULER_PERSIST = True # 管道 数据存储在Redis里面 ITEM_PIPELINES = { # 配置redis的pipeline 可用可不用 'scrapy_redis.pipelines.RedisPipeline': 301 } # Redis server REDIS_HOST = "localhost" REDIS_PORT = 6379 REDIS_DB = 4 REDIS_PARAMS = { "password": "" }
# spider文件 爬虫核心文件... pip install scrapy-redis 1.更换继承关系 2. 干掉这个start_urls 3. 更换为redis_key 起始url,相当于就是任务id ... [注意] 给Redislpsuh的时候注意推url 该url 请求后的逻辑会返回resp 数据处理的逻辑必须一致!!!
from scrapy_redis.spiders import RedisSpider class XxxSpider(RedisSpider): name = "nb_cqie" redis_key = "cqie_pro:item"
[注意]
-
调度器源码分析
-
初始化调度器
-
核心代码执行流程
重复过滤器对象 几个队列类… -
open函数
-
close
入队逻辑
指纹运算 代码分析: 参数 request 请求对象: url method body 等等进行签名
请求对象里面一般携带了哪些参数????
request = Request(url, headers, callback, dont_filter,cookies,method, meta,)
入队逻辑: 根据dont_filter判断 短路逻辑 xx and xxx
if not request.dont_filter and self.df.request_seen(request):
return False
代码解读:
(1) dont_filter False => 去重
not request.dont_filter => True 去重就判断是否是否重复???
执行: self.df.request_seen(request) 过滤器判断
1.如果指纹存在 True 条件: True and True : return False 跳过
2.如果指纹不存在 None/False 条件: True and False : return True 继续请求
request_seen() 校验该请求是否已经请求过了 :返回True/Fasle
指纹:存储在内存里面 集合数据类型自动去重: 算法:sha1 .....
伪代码模型:
if 是否去重 and 判断是否存在(根据指纹,set()): True 执行判断逻辑
return Fasle 不玩了...
继续干...
将该请求添加到请求队列 给引擎 然后下载器 网络请求....
-
Pycharm配置并行程序
- 清空运行栈
-
重新配置运行
-
模板允许并行执行
-
并行 多个进程同时执行
-
推送任务
lpush key cqie_pro:item https://www.cqie.edu.cn/html/3/tzgg/Index.shtml
程序执行结果:
Redis存储数据:
Redis分布式爬虫效果:
(1)你只需要往里面put任务基于消息队列 scrapy程序监听到消息后 直接执行 各个节点独立。
(2)支持断点续爬 和增量爬虫本质一样,利用Redis set数据结构 自动过滤掉重复请求
(3) 共享调度器 => Redis
(4) 同步 -> 异步
-
版本
pip install scrapy == 2.5.1 pip install scrapy-redis==0.7.2
布隆过滤器:
平时, 我们如果需要对数据进行去重操作可以有以下方案:
1. 直接用set集合来存储url. (最low的方案)
2. 用set集合存储hash过的url. scrapy默认
3. 用redis来存储hash过的请求, scrapy-redis默认就是这样做的. 如果请求非常非常多. redis压力是很大的.
4. 用布隆过滤器.
布隆过滤器的原理: 其实它里面就是一个改良版的bitmap. 何为bitmap,.
假设我提前准备好一个数组, 然后把源数据经过hash计算. 会计算出一个数字. 我们按照下标来找到该下标对应的位置. 然后设置成1.
a = 李嘉诚
b = 张翠山
....
[0],[0],[0],[0],[0],[0],[0],[0],[0] 10个长度数组
hash(a) => 3
hash(b) => 4
[0],[0],[0],[1],[1],[0],[0],[0],[0]
# 我想找'张三'
hash('张三') => 6
# 去数组中找6位置的数字。 是0,则不存在'张三'
# 找的时候依然执行该hash算法. 然后直接去找对应下标的位置看看是不是1. 是1就有, 不是1就没有
这样有个不好的现象. 容易误判. 如果hash算法选的不够好. 很容易搞错. 那怎么办. 多选几个hash算法
a = 李嘉诚
b = 张翠山
[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]
hash1(a) = 3
hash2(a) = 4
hash1(b) = 2
hash2(b) = 5
[0],[0],[1],[1],[1],[1],[0],[0],[0],[0]
# 找的时候, 重新按照这个hash的顺序, 在重新执行一遍. 依然会得到2个值. 分别去这两个位置看是否是1. 如果全是1, 就有, 如果有一个是0, 就没有.
在scrapy-redis中想要使用布隆过滤器是非常简单的. 你可以自己去写这个布隆过滤器的逻辑. 不过我建议直接用第三方的就可以了
# 安装布隆过滤器
pip install scrapy_redis_bloomfilter
# 配置即可...
# 去重类,要使用 BloomFilter 请替换 DUPEFILTER_CLASS
DUPEFILTER_CLASS = "scrapy_redis_bloomfilter.dupefilter.RFPDupeFilter"
# 哈希函数的个数,默认为 6,可以自行修改
BLOOMFILTER_HASH_NUMBER = 6
# BloomFilter 的 bit 参数,默认 30,占用 128MB 空间,去重量级 1 亿
BLOOMFILTER_BIT = 30
```python
# 安装布隆过滤器
pip install scrapy_redis_bloomfilter
# 配置即可...
# 去重类,要使用 BloomFilter 请替换 DUPEFILTER_CLASS
DUPEFILTER_CLASS = "scrapy_redis_bloomfilter.dupefilter.RFPDupeFilter"
# 哈希函数的个数,默认为 6,可以自行修改
BLOOMFILTER_HASH_NUMBER = 6
# BloomFilter 的 bit 参数,默认 30,占用 128MB 空间,去重量级 1 亿
BLOOMFILTER_BIT = 30