Ich crawle 6 verschiedene allowed_domains und möchte die Tiefe von 1 Domain begrenzen. Wie würde ich die Tiefe dieser 1 Domäne in scrapy begrenzen? Oder wäre es möglich, nur eine Tiefe einer Offsite-Domain zu crawlen?Scrapy Set Tiefenlimit pro erlaubte_Domänen
5
A
Antwort
8
Scrapy bietet nichts dergleichen. Sie können set the DEPTH_LIMIT
per-spider, aber nicht per-Domäne.
Was können wir tun? Read the code, Kaffee trinken und lösen (Bestellung ist wichtig).
Die Idee ist, Scrapy eingebaute DepthMiddleware
und provide our custom one stattdessen zu deaktivieren.
Lassen Sie uns zunächst Einstellungen definieren:
DOMAIN_DEPTHS
ein Wörterbuch mit Tiefengrenzen pro DomainDEPTH_LIMIT
Einstellung sein würden wir als Standard ein, falls verlassen werde eine Domäne nicht konfiguriert ist
Beispiel Einstellungen:
DOMAIN_DEPTHS = {'amazon.com': 1, 'homedepot.com': 4}
DEPTH_LIMIT = 3
Okay, jetzt die benutzerdefinierte Middleware (basierend auf DepthMiddleware
):
from scrapy import log
from scrapy.http import Request
import tldextract
class DomainDepthMiddleware(object):
def __init__(self, domain_depths, default_depth):
self.domain_depths = domain_depths
self.default_depth = default_depth
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
domain_depths = settings.getdict('DOMAIN_DEPTHS', default={})
default_depth = settings.getint('DEPTH_LIMIT', 1)
return cls(domain_depths, default_depth)
def process_spider_output(self, response, result, spider):
def _filter(request):
if isinstance(request, Request):
# get max depth per domain
domain = tldextract.extract(request.url).registered_domain
maxdepth = self.domain_depths.get(domain, self.default_depth)
depth = response.meta.get('depth', 0) + 1
request.meta['depth'] = depth
if maxdepth and depth > maxdepth:
log.msg(format="Ignoring link (depth > %(maxdepth)d): %(requrl)s ",
level=log.DEBUG, spider=spider,
maxdepth=maxdepth, requrl=request.url)
return False
return True
return (r for r in result or() if _filter(r))
Beachten Sie, dass es tldextract
Modul (verwendet, um einen Domain-Namen von URL zum Extrahieren) installiert werden benötigt:
>>> import tldextract
>>> url = 'http://stackoverflow.com/questions/27805952/scrapy-set-depth-limit-per-allowed-domains'
>>> tldextract.extract(url).registered_domain
'stackoverflow.com'
Jetzt brauchen wir um die Standard-Middleware auszuschalten und die von uns implementierte zu verwenden:
SPIDER_MIDDLEWARES = {
'myproject.middlewares.DomainDepthMiddleware': 900,
'scrapy.contrib.spidermiddleware.depth.DepthMiddleware': None
}
Wie unterscheidet sich tldextract von dem bekannteren Python-Paket 'tld'? –
@FredericBazin Ich bin mir nicht wirklich sicher, habe sie nicht verglichen. Ich denke hier ist es nicht sehr wichtig. Sie können jedes von ihnen verwenden oder es sogar mit 'urlparse()' lösen, das eine Gewohnheit 'extract_domain()' Funktion hat. Vielen Dank. – alecxe
Vielen Dank für eine großartige Erklärung! Sollte 'response.meta ['depth'] = 0 'über' depth = response.meta [' depth '] + 1' Else, ist' response.meta ['depth'] 'leer und würde einen Keyerror geben –