前言
去重方案
总结:
scrapy实现增量爬取的方法
1.通过开启缓存,将每个请求缓存至本地,下次爬取时,scrapy会优先从本地缓存中获得response,这种模式下,再次请求已爬取的网页不用从网络中获得响应,所以不受带宽影响,对服务器也不会造成额外的压力,但是无法获取网页变化的内容,速度也没有第二种方式快,而且缓存的文件会占用比较大的内存,在setting.py的以下注释用于设置缓存。
2.对item实现去重,通过item来封装
实现方法是在pipelines.py中进行设置,即在持久化数据之前判断数据是否已经存在
3.对url实现去重
速度快,对网站服务器的压力也比较小
scrapy可以自定义下载中间件
scrapy_reids实现增量式爬虫
在settings.py中添加如下代码
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 指定了去重的类
SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 指定了调度器的类
SCHEDULER_PERSIST = True # 调度器的内容是否持久化
REDIS_URL = "redis://127.0.0.1:6379" # redis的url
ITEM_PIPELINES = {'scrapy_redis.pipelines.RedisPipeline': 400, } # 如果数据需要保存到redis中,选配的
生成指纹 RFPDupeFilter.py
def request_seen(self, request):
"""
生成请求指纹,并判断请求在不在指纹集合中,
如果返回True,表示已经放入请求队列中了
返回False,表示该请求还未做过
"""
fp = self.request_fingerprint(request) #生成指纹
# 尝试将指纹放入指纹集合中,如果返回值为0,代表已经存在
added = self.server.sadd(self.key, fp)
return added == 0
def request_fingerprint(request):
"""
对请求生成指纹,利用hashlib的sha1对象,对request的url、method、body进行哈希,会产生一
个40位16进制的字符串,作为request的指纹
"""
fp = hashlib.sha1()
fp.update(to_bytes(request.method))
fp.update(to_bytes(canonicalize_url(request.url)))
fp.update(request.body or b'')
return fp.hexdigest()
进入队列
def enqueue_request(self, request):
if not request.dont_filter and self.df.request_seen(request):
return False
self.queue.push(request)
return True
-
如果请求设置的过滤并且请求的指纹在指纹集合中存在的,不进入队列
-
其他情况会进入队列
-
start_url中由于yield请求时,默认设置了dont_filter为True,不过滤,所以肯定会进入队列
为啥设置start_urls为不过滤?
start_url是起始页,其他请求需要靠start_url对应的响应才能保证抓取
一般正常去重的思路
错误:将所有访问过的URL和其对应的内容保存下来,然后过一段时间重新爬取一次并进行比较,然后决定是否需要覆盖。
原因:消耗很多资源
正确:给URL或者其内容(取决于这个网站采用哪种更新方式)上一个数据指纹标识,所以一般用set和redis
哈希值,根据哈希函数的特性,我们可以为任意内容生成一个独一无二的定长字符串,计算机只要经过简单的计算就可以得到唯一的特征值,这个计算过程的开销基本可以忽略不计
对数据做持久化就用redis ,Redis的集合就是Redis数据库中的集合类型,它具有无序不重复的特点。