2012-12-05 12 views
33

Ich bin ein Neuling von Scrapy und es ist erstaunlich Crawler Framework, die ich kenne!Wie bekomme ich die Scrapy-Fehler-URLs?

In meinem Projekt habe ich mehr als 90 000 Anfragen gesendet, aber einige von ihnen sind fehlgeschlagen. Ich habe den Protokolllevel auf INFO gesetzt, und ich kann nur einige Statistiken, aber keine Details sehen.

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)} 

Gibt es eine Möglichkeit, detaillierter Bericht zu erhalten? Zeigen Sie beispielsweise diese fehlgeschlagenen URLs an. Vielen Dank!

Antwort

42

Ja, das ist möglich.

Ich habe meiner Spider-Klasse eine failed_urls-Liste hinzugefügt und URLs an sie angehängt, wenn der Status der Antwort 404 war (dies muss erweitert werden, um andere Fehlerstatus abzudecken).

Dann habe ich ein Handle hinzugefügt, die die Liste in einer einzigen Zeichenfolge verbindet und es den Statistiken hinzufügen, wenn die Spinne geschlossen ist.

Basierend auf Ihren Kommentaren ist es möglich, Twisted-Fehler zu verfolgen.

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) 

Ausgang (der Downloader/exception_count * Statistiken erscheinen nur, wenn Ausnahmen tatsächlich geworfen werden - ich simulierte sie, indem Sie versuchen, die Spinne nachdem ich ausgeschaltet meinen Wireless-Adapter laufen):

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)} 
+0

Dies funktioniert nicht mehr. 'exceptions.NameError: Globaler Name 'self' ist nicht definiert 'Fehler tritt auf. 'BaseSpider' ist jetzt nur' Spider' http://doc.scrapy.org/en/0.24/news.html?highlight = basespider # id2 https://github.com/scrapy/dirbot/blob/master/dirbot/spiders/dmoz.py, aber ich kann den Fix nicht finden, um Ihren Code @Talvalin zu erhalten. – Mikeumus

15

Hier ist ein weiteres Beispiel dafür, wie 404-Fehler (Überprüfung github Hilfeseiten) zu handhaben und sammeln:

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 

führen Sie einfach scrapy runspider mit -o output.json und sehen Liste der Artikel in der Datei output.json.

10

Die Antworten von @Talvalin und @alecxe haben mir sehr geholfen, aber sie scheinen keine Downloader-Ereignisse zu erfassen, die kein Antwortobjekt erzeugen (zum Beispiel twisted.internet.error.TimeoutError und twisted.web.http.PotentialDataLoss). Diese Fehler werden am Ende des Laufs im Statistik-Dump angezeigt, jedoch ohne Meta-Informationen.

Wie ich here herausgefunden, werden die Fehler durch die Stats.py Middleware, eingefangen in der DownloaderStats Klasse process_exception Verfahren, und speziell in dem ex_class Variable verfolgt, die jeweils Fehler nach Bedarf inkrementiert eingeben und dann Dumps die Zählungen an dem Ende des Laufs.

Um solche Fehler mit Informationen aus dem entsprechenden Request-Objekt übereinstimmen, kann auf jede Anforderung Meta info (via request.meta) hinzufügen, dann in die process_exception Methode des Stats.py ziehen:

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

Das wird erzeugen eine eindeutige Zeichenfolge für jeden Fehler dieser Art.Kann die veränderte Stats.py als Mystats.py speichern, um es in die Middleware (mit dem Recht Vorrang) hinzufügen, und deaktivieren Sie die regelmäßige Stats.py: sieht

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

Der Ausgang wird am Ende des Laufs wie folgt aus (hier Meta-Informationen mit dem Urls/Anfragen werden abgebildet auf ganzzahlige-basierte Meta str von groupID/memberID, wie '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'....} 

This answer beschäftigt sich mit nicht-Downloader-basierten Fehler.

+0

Genau das, was ich suche. Ich denke, Scrapy sollte diese Funktion hinzufügen, um bequemen Zugriff auf Fehlerinformationen wie URL zu bieten. – wlnirvana

+1

Verwenden Sie 'scrapy.downloadermiddlewares.stats' statt veraltet am neuesten (1.0.5) Version' scrapy.contrib.downloadermiddleware.stats' –

+0

@ElRuso danke - haben die Antwort – bahmait

6

Ab scrapy 0.24.6, die durch alecxe vorgeschlagene Methode wird nicht Fehler mit dem Start-URLs fängt. Um Fehler mit den Start-URLs aufzuzeichnen, müssen Sie parse_start_urls überschreiben. Die Antwort von Alexce für diesen Zweck, würde erhalten:

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 
5

Dies ist ein Update zu dieser Frage. Ich stieß auf ein ähnliches Problem und musste die Scrapy-Signale verwenden, um eine Funktion in meiner Pipeline aufzurufen. Ich habe den @ Talvalin-Code bearbeitet, wollte aber eine Antwort nur für etwas mehr Klarheit geben.

Grundsätzlich sollten Sie in sich selbst als Argument für handle_spider_closed hinzufügen. Sie sollten den Dispatcher auch in Init aufrufen, damit Sie die Spider-Instanz (self) an die Handle-Methode übergeben können.

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) 

Ich hoffe, das hilft jedem mit dem gleichen Problem in der Zukunft.

8

Scrapy ignorieren 404 standardmäßig aktiviert und Sie analysieren nicht. Um 404 Fehler zu behandeln, tun Sie dies. Dies ist sehr einfach, wenn Sie Fehlercode 404 als Antwort bekommen, können Sie damit umgehen mit sehr einfachen Art und Weise ist ..... in Einstellungen

HTTPERROR_ALLOWED_CODES = [404,403] 

schreiben und dann die Antwort-Statuscode behandeln in Ihrem Parse-Funktion.

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

in Einstellungen und erhält Antwort in Parse-Funktion

0

Neben einigen dieser Antworten, wenn Sie Twisted-Fehler verfolgen mögen, ich errback Parameter einen Blick auf mit dem Request-Objekt übernehmen würde, auf der Sie können eine Rückruffunktion festlegen, die bei einem Fehler bei der Anforderung mit Twisted Failure aufgerufen wird. Zusätzlich zur URL können Sie mit dieser Methode den Fehlertyp verfolgen.

Anschließend können Sie die URLs anmelden, indem Sie mit: failure.request.url (wo failure das Failure Objekt in errback geben Verdreht ist).

# 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) 

Die Scrapy docs geben ein vollständiges Beispiel dafür, wie diese Ausnahme gemacht werden kann, dass die Anrufe an die Scrapy Logger jetzt depreciated sind, also habe ich mein Beispiel angepasst Pythons in logging gebaut verwenden):

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