用redis實現scrapy的url去重與增量爬取

2021-08-21 10:36:21 字數 4013 閱讀 4229

scrapy 自帶了去重方案,通過rfpdupefilter類完成去重,檢視原始碼。

def request_seen(self, request):

fp = self.request_fingerprint(request)

if fp in self.fingerprints:

return true

self.fingerprints.add(fp)

if self.file:

self.file.write(fp + os.linesep)

def request_fingerprint(self, request):

return request_fingerprint(request)

def request_fingerprint(request, include_headers=none):

if include_headers:

include_headers = tuple(to_bytes(h.lower())

for h in sorted(include_headers))

cache = _fingerprint_cache.setdefault(request, {})

if include_headers not in cache:

fp = hashlib.sha1()

fp.update(to_bytes(request.method))

fp.update(to_bytes(canonicalize_url(request.url)))

fp.update(request.body or b'')

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]

rfpdupefilter定義了request_seen()方法,將request的指紋資訊sha1(method+url+body+header)整體寫入set()進行去重。

這種方式去重的比例較小。

下面我們定製過濾器,僅根據request的url進行去重。

from scrapy.dupefilters import rfpdupefilter

class urlfilter(rfpdupefilter):

""" 只根據url去重"""

def __init__(self, path=none, debug=false):

self.urls_seen = set()

rfpdupefilter.__init__(self, path)

def request_seen(self, request):

if request.url in self.urls_seen:

return true

else:

self.urls_seen.add(request.url)

配置setting.py

dupefilter_class = '專案名.檔名.urlfilter'
這種去重方式,儲存著set中的資訊在爬蟲執行結束就會消失。下次排程爬蟲的時候還是會繼續爬去此次爬過的url。

為了實現增量爬蟲,可以利用redis的set()快取爬過的url資料。

1.自定義過濾器,在爬資料之前,校驗該url是否爬取過

from scrapy.dupefilters import rfpdupefilter

class urlredisfilter(rfpdupefilter):

""" 只根據url去重"""

def __init__(self, path=none, debug=false):

rfpdupefilter.__init__(self, path)

self.dupefilter = urlfilterandadd()

def request_seen(self, request):

# 校驗,新增2行**

if self.dupefilter.check_url(request.url):

return true

#保留中間頁面的去重規則不變,不然爬蟲在執行過程中容易出現死迴圈

fp = self.request_fingerprint(request)

if fp in self.fingerprints:

return true

self.fingerprints.add(fp)

if self.file:

self.file.write(fp + os.linesep)

class urlfilterandadd(object):

def __init__(self):

redis_config =

pool = connectionpool(**redis_config)

self.pool = pool

self.redis = strictredis(connection_pool=pool)

self.key = "spider_redis_key"

def url_sha1(self, url):

fp = hashlib.sha1()

fp.update(canonicalize_url(url).encode("utf-8"))

url_sha1 = fp.hexdigest()

return url_sha1

def check_url(self, url):

sha1 = self.url_sha1(url)

#此處只判斷url是否在set中,並不新增url資訊,

#防止將起始url 、中間url(比如列表頁的url位址)寫入快取中,

i***ist = self.redis.sismember(self.key, sha1)

return i***ist

def add_url(self, url):

sha1 = self.url_sha1(url)

added = self.redis.sadd(self.key, sha1)

return added

注意:urlfilter中只校驗爬蟲是否存在,不快取url資料
2.修改pipeline 在資料爬完後將url放入redis中去重,避免將中間鏈結也快取了

class myspiderpipeline(object):

def __init__(self):

self.dupefilter = urlfilterandadd()

def process_item(self, item, spider):

# ,在資料爬完後將url放入redis去重

print("add>>url:", item['crawl_url'])

self.dupefilter.add_url(item['crawl_url'])

return itemprint("add>>url:", item['crawl_url'])

self.dupefilter.add_url(item['crawl_url'])

return item

在setting中配置:

item_pipelines = 

dupefilter_class = '專案名.檔名.urlredisfilter'

按照目前的方式,redis中的資料會一直膨脹,後續在優化。

用redis實現秒殺

今日在研究秒殺系統,用資料庫的樂觀鎖可以實現,但是在高併發下可能並不好,所以就想到了快取系統redis,因為redis本身也有鎖機制,廢話不多說,直接上 請大神指點不足的地方。class a public class myrunnable implements runnable override p...

用Redis實現Session功能,實現單點登入

redis是乙個開源的使用ansi c語言編寫 支援網路 可基於記憶體亦可持久化的日誌型 key value資料庫,並提供多種語言的api 維基百科 一般開發中使用者狀態使用session或者cookie,兩種方式各種利弊。session 在inproc模式下容易丟失,並且引起併發問題。如果使用sq...

用redis實現的小遊戲設計

前段時間接了乙個h5遊戲的後端開發任務,需求比較簡單,就是在大會場裡,幾百 上千人分成若干組,在一段時間裡同時搖手機,實時顯示當前排名,最後看哪個組搖的最快,哪個人搖的最快。由於是所有使用者同時搖手機,而且一秒鐘之內要搖5 10下,假設一千人同時搖,可能在一秒鐘內會有5000至10000次的寫入請求...