scrapy 中判断重复内容的方法(RFPDupeFilter)
爬虫抓取数据时,重复肯定是存在的,scrapy 是如何来筛选这些重复的 url 的呢?
这个处理的代码是编写在 dupefilter.py 文件中的,其中定义了处理重复 url 的方法。
在 scrapy 启动时,如果配置了重复 url 写入文件(requests.seen
),那么就会以追加的方式打开这个文件,并且从这个文件中载入以前的数据到内存 set() 中保存,当遇到一个新来的 url 时,通过指纹计算,在已抓取 url 集合中查找,如果不存在,就添加进去,如果需要写入文件,就写入文件;如果已经存在了,告诉上层调用 url 已经抓取过了。
具体可以参考 class RFPDupeFilter(BaseDupeFilter)
类。
那么在 scrapy 中是如何来使用这个类的方法的呢?什么时候使用,这个流程是怎样的呢?
这个可以追溯到 scrapy.core.scheduler 中定义的 Scheduler 类来决定。
现在就来看看 Scheduler 类中和过滤重复 url 有关的内容。
在 Scheduler 类中,在调度时,采用了 memory queue 和 disk queue 的存储方法,所以,有一个入队的方法,在入队前,就要对 request
进行检查,检查是否是重复,如果已经重复了,就不入队了。
1
|
if
not
request.dont_filter
and
self
.df.request_seen(request)
|
这里两个条件控制,首先是配置中 dont_filter,如果它是 True,就说明是不筛选的,如果是 False,才是要筛选的。
后面的 request_seen() 在默认内置的筛选方法中,就是 RFPDupeFilter() 中的方法,检查 request 是否已经存在。
只有要筛选且没有见过这个 request,才会去筛选 url。
所以这里已经很清晰了,调度器收到了 enqueue_request()
调用时,会检查这个 url 重复的判断开关,如果要筛选,就要检查这个 request 是否已经存在了;这里的检查 if 如果成立,就直接返回了,只有不成立时,才会有后续的存储操作,也就是入队。
下面来看看 scrapy 中是如何判断两个 url 重复的。
关键的函数是 request_fingerprint
,这个是判断是否重复的关键实现方法。(scrapy.utils.request.request_fingerprint()
)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def
request_fingerprint(request, include_headers
=
None
):
if
include_headers:
include_headers
=
tuple
([h.lower()
for
h
in
sorted
(include_headers)])
cache
=
_fingerprint_cache.setdefault(request, {})
if
include_headers
not
in
cache:
fp
=
hashlib.sha1()
fp.update(request.method)
fp.update(canonicalize_url(request.url))
fp.update(request.body
or
'')
if
include_headers:
for
hdr
in
include_headers:
if
hdr
in
request.headers:
fp.update(hdr)
for
v
in
request.headers.getlist(hdr):
fp.update(v)
cache[include_headers]
=
fp.hexdigest()
return
cache[include_headers]
|
默认的调用情况下,计算的内容包括 method、格式化后的 url、请求正文,还有就是 http headers 是可选的。
和通常情况下不一样的是,这里的计算指纹,不是单纯的比较了 url 是否一致。计算的结果是一串 hash 16 进制数字。
这里自然产生了一个疑问,如果说计算指纹不是单纯的比较 url,那么 request 对象是个什么东西?当调用 request_fingerprint() 时, request 经过了哪些计算,是不是 request 传递到这里的时候,url 已经被下载过了?还是说没有下载?如果说已经下载过了,就出现了重复下载的问题,那去重的意义就很小很小了;如果没有下载过,method、header、body 的内容又是如何得知的呢?
request 对象是什么?它的生命周期是如何的?