轉載 ------------- scrapy 中判斷重複內容的方法(RFPDupeFilter)

轉載於http://www.leyle.com/archives/scrapy_dupefilter.html

scrapy 中判斷重複內容的方法(RFPDupeFilter)

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 對象是什麼?它的生命週期是如何的?



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章