tutorial scraping library example español crawling crawler python web-scraping report scrapy

scraping - scrapy python español



¿Cómo obtener las URL de falla de scrapy? (7)

Scrapy ignora 404 de manera predeterminada y no analiza. Para manejar el error 404 haz esto. Esto es muy fácil, si está recibiendo el código de error 404 en respuesta, puede manejar esto de una manera muy fácil ..... en la configuración de escritura

HTTPERROR_ALLOWED_CODES = [404,403]

y luego maneje el código de estado de respuesta en su función de análisis.

def parse(self,response): if response.status == 404: #your action on error

en la configuración y obtener respuesta en la función de análisis

¡Soy un novato de scrapy y es increíble el framework de crawler que he conocido!

En mi proyecto, envié más de 90,000 solicitudes, pero algunas fallaron. Configuré el nivel de registro para que sea INFO, y solo puedo ver algunas estadísticas pero no detalles.

2012-12-05 21:03:04+0800 [pd_spider] INFO: Dumping spider stats: {''downloader/exception_count'': 1, ''downloader/exception_type_count/twisted.internet.error.ConnectionDone'': 1, ''downloader/request_bytes'': 46282582, ''downloader/request_count'': 92383, ''downloader/request_method_count/GET'': 92383, ''downloader/response_bytes'': 123766459, ''downloader/response_count'': 92382, ''downloader/response_status_count/200'': 92382, ''finish_reason'': ''finished'', ''finish_time'': datetime.datetime(2012, 12, 5, 13, 3, 4, 836000), ''item_scraped_count'': 46191, ''request_depth_max'': 1, ''scheduler/memory_enqueued'': 92383, ''start_time'': datetime.datetime(2012, 12, 5, 12, 23, 25, 427000)}

¿Hay alguna manera de obtener un informe más detallado? Por ejemplo, muestre esas URL fallidas. ¡Gracias!


Sí, esto es posible

Agregué una lista de failed_urls a mi clase spider y anexé URLs si el estado de la respuesta era 404 (esto tendrá que extenderse para cubrir otros estados de error).

Luego agregué un identificador que une la lista en una sola cadena y la agrego a las estadísticas cuando la araña está cerrada.

En función de sus comentarios, es posible realizar un seguimiento de los errores de Twisted.

from scrapy.spider import BaseSpider from scrapy.xlib.pydispatch import dispatcher from scrapy import signals class MySpider(BaseSpider): handle_httpstatus_list = [404] name = "myspider" allowed_domains = ["example.com"] start_urls = [ ''http://www.example.com/thisurlexists.html'', ''http://www.example.com/thisurldoesnotexist.html'', ''http://www.example.com/neitherdoesthisone.html'' ] def __init__(self, category=None): self.failed_urls = [] def parse(self, response): if response.status == 404: self.crawler.stats.inc_value(''failed_url_count'') self.failed_urls.append(response.url) def handle_spider_closed(spider, reason): self.crawler.stats.set_value(''failed_urls'', '',''.join(spider.failed_urls)) def process_exception(self, response, exception, spider): ex_class = "%s.%s" % (exception.__class__.__module__, exception.__class__.__name__) self.crawler.stats.inc_value(''downloader/exception_count'', spider=spider) self.crawler.stats.inc_value(''downloader/exception_type_count/%s'' % ex_class, spider=spider) dispatcher.connect(handle_spider_closed, signals.spider_closed)

Salida (las estadísticas downloader / exception_count * solo aparecerán si se lanzan excepciones, las simulé al intentar ejecutar la araña después de que apagué el adaptador inalámbrico):

2012-12-10 11:15:26+0000 [myspider] INFO: Dumping Scrapy stats: {''downloader/exception_count'': 15, ''downloader/exception_type_count/twisted.internet.error.DNSLookupError'': 15, ''downloader/request_bytes'': 717, ''downloader/request_count'': 3, ''downloader/request_method_count/GET'': 3, ''downloader/response_bytes'': 15209, ''downloader/response_count'': 3, ''downloader/response_status_count/200'': 1, ''downloader/response_status_count/404'': 2, ''failed_url_count'': 2, ''failed_urls'': ''http://www.example.com/thisurldoesnotexist.html, http://www.example.com/neitherdoesthisone.html'' ''finish_reason'': ''finished'', ''finish_time'': datetime.datetime(2012, 12, 10, 11, 15, 26, 874000), ''log_count/DEBUG'': 9, ''log_count/ERROR'': 2, ''log_count/INFO'': 4, ''response_received_count'': 3, ''scheduler/dequeued'': 3, ''scheduler/dequeued/memory'': 3, ''scheduler/enqueued'': 3, ''scheduler/enqueued/memory'': 3, ''spider_exceptions/NameError'': 2, ''start_time'': datetime.datetime(2012, 12, 10, 11, 15, 26, 560000)}


Esta es una actualización de esta pregunta. Me encontré con un problema similar y necesitaba usar las señales de scrapy para llamar a una función en mi pipeline. He editado el código de @ Talvalin, pero quería hacer una respuesta para mayor claridad.

Básicamente, debe agregarse a sí mismo como argumento para handle_spider_closed. También debe llamar al despachador en init para que pueda pasar la instancia de spider (self) al método de gestión.

from scrapy.spider import Spider from scrapy.xlib.pydispatch import dispatcher from scrapy import signals class MySpider(Spider): handle_httpstatus_list = [404] name = "myspider" allowed_domains = ["example.com"] start_urls = [ ''http://www.example.com/thisurlexists.html'', ''http://www.example.com/thisurldoesnotexist.html'', ''http://www.example.com/neitherdoesthisone.html'' ] def __init__(self, category=None): self.failed_urls = [] # the dispatcher is now called in init dispatcher.connect(self.handle_spider_closed,signals.spider_closed) def parse(self, response): if response.status == 404: self.crawler.stats.inc_value(''failed_url_count'') self.failed_urls.append(response.url) def handle_spider_closed(self, spider, reason): # added self self.crawler.stats.set_value(''failed_urls'','',''.join(spider.failed_urls)) def process_exception(self, response, exception, spider): ex_class = "%s.%s" % (exception.__class__.__module__, exception.__class__.__name__) self.crawler.stats.inc_value(''downloader/exception_count'', spider=spider) self.crawler.stats.inc_value(''downloader/exception_type_count/%s'' % ex_class, spider=spider)

Espero que esto ayude a cualquiera con el mismo problema en el futuro.


Aquí hay otro ejemplo de cómo manejar y recolectar errores 404 (verificando las páginas de ayuda de github):

from scrapy.selector import HtmlXPathSelector from scrapy.contrib.spiders import CrawlSpider, Rule from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor from scrapy.item import Item, Field class GitHubLinkItem(Item): url = Field() referer = Field() status = Field() class GithubHelpSpider(CrawlSpider): name = "github_help" allowed_domains = ["help.github.com"] start_urls = ["https://help.github.com", ] handle_httpstatus_list = [404] rules = (Rule(SgmlLinkExtractor(), callback=''parse_item'', follow=True),) def parse_item(self, response): if response.status == 404: item = GitHubLinkItem() item[''url''] = response.url item[''referer''] = response.request.headers.get(''Referer'') item[''status''] = response.status return item

Simplemente ejecute scrapy runspider con -o output.json y vea la lista de elementos en el archivo output.json .


A partir de scrapy 0.24.6, el método sugerido por alecxe no detectará errores con las URL de inicio. Para registrar los errores con las URL de inicio, debe anular parse_start_urls . Adaptando la respuesta de alexce para este propósito, obtendrías:

from scrapy.selector import HtmlXPathSelector from scrapy.contrib.spiders import CrawlSpider, Rule from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor from scrapy.item import Item, Field class GitHubLinkItem(Item): url = Field() referer = Field() status = Field() class GithubHelpSpider(CrawlSpider): name = "github_help" allowed_domains = ["help.github.com"] start_urls = ["https://help.github.com", ] handle_httpstatus_list = [404] rules = (Rule(SgmlLinkExtractor(), callback=''parse_item'', follow=True),) def parse_start_url(self, response): return self.handle_response(response) def parse_item(self, response): return self.handle_response(response) def handle_response(self, response): if response.status == 404: item = GitHubLinkItem() item[''url''] = response.url item[''referer''] = response.request.headers.get(''Referer'') item[''status''] = response.status return item


Las respuestas de @Talvalin y @alecxe me ayudaron mucho, pero no parecen capturar los eventos de descarga que no generan un objeto de respuesta (por ejemplo, twisted.internet.error.TimeoutError y twisted.web.http.PotentialDataLoss ) . Estos errores aparecen en el volcado de estadísticas al final de la ejecución, pero sin meta información.

Como descubrí aquí , los errores son rastreados por el middleware Stats.py , capturado en el método process_exception clase DownloaderStats , y específicamente en la variable ex_class , que incrementa cada tipo de error según sea necesario, y luego vuelca los recuentos al final de la carrera.

Para hacer coincidir dichos errores con la información del objeto de solicitud correspondiente, puede agregar metainformación a cada solicitud (a través de request.meta ) y luego colocarla en el método process_exception de Stats.py:

self.stats.set_value(''downloader/my_errs/%s'' % request.meta, ex_class)

Eso generará una cadena única para cada error de este tipo. Puede guardar el Stats.py modificado como Mystats.py , agregarlo al middleware (con la precedencia correcta) y deshabilitar el Stats.py regular:

DOWNLOADER_MIDDLEWARES = { ''myproject.mystats.MyDownloaderStats'': 850, ''scrapy.downloadermiddleware.stats.DownloaderStats'': None, }

La salida al final de la ejecución se ve así (aquí usando metainformación donde las urls / solicitudes están mapeadas a una meta basada en enteros de groupID / memberID, como ''0/14'' ):

{''downloader/exception_count'': 3, ''downloader/exception_type_count/twisted.web.http.PotentialDataLoss'': 3, ''downloader/my_errs/0/1'': ''twisted.web.http.PotentialDataLoss'', ''downloader/my_errs/0/38'': ''twisted.web.http.PotentialDataLoss'', ''downloader/my_errs/0/86'': ''twisted.web.http.PotentialDataLoss'', ''downloader/request_bytes'': 47583, ''downloader/request_count'': 133, ''downloader/request_method_count/GET'': 133, ''downloader/response_bytes'': 3416996, ''downloader/response_count'': 130, ''downloader/response_status_count/200'': 95, ''downloader/response_status_count/301'': 24, ''downloader/response_status_count/302'': 8, ''downloader/response_status_count/500'': 3, ''finish_reason'': ''finished''....}

Esta respuesta se refiere a errores no basados ​​en descarga.


Además de algunas de estas respuestas, si desea realizar un seguimiento de los errores de Twisted, eche un vistazo al uso del parámetro errback del objeto Request, en el que puede establecer que se llame a una función de devolución de llamada con la Falla trenzada en una falla de solicitud. Además de la url, este método puede permitirle rastrear el tipo de falla.

A continuación, puede registrar las URL utilizando: failure.request.url (donde failure el objeto Twisted Failure pasado a errback ).

# these would be in a Spider def start_requests(self): for url in self.start_urls: yield scrapy.Request(url, callback=self.parse, errback=self.handle_error) def handle_error(self, failure): url = failure.request.url logging.error(''Failure type: %s, URL: %s'', failure.type, url)

Los documentos de Scrapy ofrecen un ejemplo completo de cómo se puede hacer esto, excepto que las llamadas al registrador de Scrapy están ahora depreciadas , así que he adaptado mi ejemplo para usar el registro integrado de Python):

https://doc.scrapy.org/en/latest/topics/request-response.html#topics-request-response-ref-errbacks