Cómo limitar el número de páginas seguidas por sitio en Python Scrapy

Estoy tratando de construir una araña que podría raspar de manera eficiente la información de texto de muchos sitios web. Como soy un usuario de Python, me recomendaron Scrapy. Sin embargo, para evitar raspar grandes sitios web, quiero limitar la araña a rascar no más de 20 páginas de cierta “profundidad” por sitio web . Aquí está mi araña:

class DownloadSpider(CrawlSpider): name = 'downloader' download_path = '/home/MyProjects/crawler' rules = (Rule(SgmlLinkExtractor(), callback='parse_item', follow=True),) def __init__(self, *args, **kwargs): super(DownloadSpider, self).__init__(*args, **kwargs) self.urls_file_path = [kwargs.get('urls_file')] data = open(self.urls_file_path[0], 'r').readlines() self.allowed_domains = [urlparse(i).hostname.strip() for i in data] self.start_urls = ['http://' + domain for domain in self.allowed_domains] def parse_start_url(self, response): return self.parse_item(response) def parse_item(self, response): self.fname = self.download_path + urlparse(response.url).hostname.strip() open(str(self.fname)+ '.txt', 'a').write(response.url) open(str(self.fname)+ '.txt', 'a').write('\n') 

urls_file es una ruta a un archivo de texto con urls. También he establecido la profundidad máxima en el archivo de configuración. Aquí está mi problema: si configuro la excepción CLOSESPIDER_PAGECOUNT , cierra la araña cuando el número total de páginas raspadas (independientemente del sitio) scope el valor de la excepción. Sin embargo, necesito dejar de raspar cuando he raspado, digamos 20 páginas de cada url. También traté de mantener la cuenta con una variable como self.parsed_number + = 1, pero esto tampoco funcionó: parece que scrapy no va url por url sino que los mezcla. Cualquier consejo es muy apreciado!

Haría una variable por clase, la inicializaría con stats = defaultdict(int) e incrementaría self.stats[response.url] (o puede ser que la clave podría ser una tupla como (website, depth) en su caso) en parse_item .

Así es como me imagino esto: debería funcionar en teoría. Déjame saber si necesitas un ejemplo.

Para su información, puede extraer la url base y calcular la profundidad con la ayuda de urlparse.urlparse (ver documentos ).

Para hacer esto, puede crear su propia clase de extractor de enlaces basada en SgmlLinkExtractor. Debería verse algo como esto:

 from scrapy.selector import Selector from scrapy.utils.response import get_base_url from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor class LimitedLinkExtractor(SgmlLinkExtractor): def __init__(self, allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(), tags=('a', 'area'), attrs=('href'), canonicalize=True, unique=True, process_value=None, deny_extensions=None, max_pages=20): self.max_pages=max_pages SgmlLinkExtractor.__init__(self, allow=allow, deny=deny, allow_domains=allow_domains, deny_domains=deny_domains, restrict_xpaths=restrict_xpaths, tags=tags, attrs=attrs, canonicalize=canonicalize, unique=unique, process_value=process_value, deny_extensions=deny_extensions) def extract_links(self, response): base_url = None if self.restrict_xpaths: sel = Selector(response) base_url = get_base_url(response) body = u''.join(f for x in self.restrict_xpaths for f in sel.xpath(x).extract() ).encode(response.encoding, errors='xmlcharrefreplace') else: body = response.body links = self._extract_links(body, response.url, response.encoding, base_url) links = self._process_links(links) links = links[0:self.max_pages] return links 

El código de esta subclase se basa completamente en el código de la clase SgmlLinkExtractor. Acabo de agregar la variable self.max_pages al constructor de la clase y la línea que cortan la lista de enlaces al final del método extract_links . Pero puedes cortar esta lista de una manera más inteligente.