web-dev-qa-db-de.com

Wie erhalte ich die URLs für die Fehlerbehebung?

Ich bin ein Neuling in Scrapy und es ist ein tolles Crawler-Framework, das ich kenne! 

In meinem Projekt habe ich mehr als 90.000 Anfragen gesendet, aber einige davon sind gescheitert. Ich habe den Log-Level auf INFO gesetzt und sehe nur einige Statistiken, aber keine Details. 

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, detailliertere Berichte zu erhalten? Zeigen Sie beispielsweise diese fehlerhaften URLs an. Vielen Dank!

36
Joe Wu

Ja das ist möglich. 

Ich fügte meiner Spider-Klasse eine failed_urls-Liste hinzu und fügte URLs hinzu, wenn der Antwortstatus 404 war (dies muss erweitert werden, um andere Fehlerstatus abzudecken). 

Dann fügte ich ein Handle hinzu, das die Liste zu einer einzelnen Zeichenfolge zusammenfügt, und fügt es den Statistiken hinzu, wenn die Spinne geschlossen wird.

Aufgrund Ihrer Kommentare können verdrehte Fehler aufgespürt werden.

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)

Ausgabe (die Statistikwerte für downloader/exception_count * werden nur angezeigt, wenn tatsächlich Ausnahmen ausgelöst werden. Ich habe sie simuliert, indem ich versuchte, die Spinne auszuführen, nachdem ich meinen drahtlosen Adapter ausgeschaltet hatte):

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

Ein weiteres Beispiel ist die Behandlung und Erfassung von 404-Fehlern (Überprüfen der Github-Hilfeseiten):

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 aus und sehen Sie sich die Liste der Elemente in der output.json-Datei an.

15
alecxe

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

Wie ich herausgefunden habe, dass hier , werden die Fehler von der stats.py - Middleware verfolgt, die in der DownloaderStats-Klasse 'process_exception-Methode und speziell in der ex_class-Variablen erfasst wird, die jeden Fehlertyp nach Bedarf erhöht, und dann werden die Zähler am Ende des Laufs ausgegeben. 

Um solche Fehler mit Informationen aus dem entsprechenden Anforderungsobjekt abzugleichen, können Sie jeder Anforderung eine eindeutige ID hinzufügen (über request.meta) und sie dann in die process_exception-Methode von stats.py ziehen:

self.stats.set_value('downloader/my_errs/{0}'.format(request.meta), ex_class)

Dadurch wird für jeden Downloader-basierten Fehler eine eindeutige Zeichenfolge generiert, die nicht von einer Antwort begleitet wird. Sie können den geänderten stats.py dann als etwas anderes speichern (z. B. my_stats.py), dem DownloadMiddlewares hinzufügen (mit der richtigen Priorität) und den Bestand stats.py deaktivieren:

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

Die Ausgabe am Ende des Laufs sieht folgendermaßen aus (hier werden Metainformationen verwendet, wobei jede Anforderungs-URL einer group_id und einer member_id zugeordnet wird, die durch einen Schrägstrich wie '0/14' getrennt sind):

{'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'....}

Diese Antwort behandelt nicht auf Downloader basierende Fehler.

12
scharfmn

Scrapy ignoriert 404 standardmäßig und analysiert nicht. Um den 404-Fehler zu behandeln, machen Sie das . Dies ist sehr einfach, wenn Sie als Antwort den Fehlercode 404 erhalten, können Sie damit umgehen, dies ist auf sehr einfache Weise ......__ 

HTTPERROR_ALLOWED_CODES = [404,403]

und behandeln Sie dann den Antwortstatuscode in Ihrer Analysefunktion.

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

in den Einstellungen und erhalten Sie eine Antwort in der Analysefunktion

9
harivans kumar

Ab Scrapy 0.24.6 kann die von alecxe vorgeschlagene Methode keine Fehler bei den Start-URLs abfangen. Um Fehler mit den Start-URLs aufzuzeichnen, müssen Sie parse_start_urls überschreiben. Wenn Sie die Antwort von alexce für diesen Zweck anpassen, erhalten Sie:

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
Louis

Dies ist ein Update zu dieser Frage. Ich hatte ein ähnliches Problem und musste die Scrapy-Signale verwenden, um eine Funktion in meiner Pipeline aufzurufen. Ich habe den Code von @Talvalin bearbeitet, wollte aber nur aus Gründen der Klarheit eine Antwort geben. 

Grundsätzlich sollten Sie self als Argument für handle_spider_closed ..__ hinzufügen. Sie sollten auch den Dispatcher in init aufrufen, damit Sie die Spider-Instanz (self) an die Behandlungsmethode ü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 in Zukunft jedem, der das gleiche Problem hat.

5
Mattias

Zusätzlich zu einigen dieser Antworten möchte ich, wenn Sie verdrehte Fehler verfolgen möchten, den Parameter errback des Anforderungsobjekts verwenden, in dem Sie eine Callback-Funktion einstellen können, die mit der Option Twisted Failure auf a aufgerufen wird request failure ..__ Mit dieser Methode können Sie zusätzlich zur URL die Art des Fehlers verfolgen.

Sie können dann die URLs protokollieren, indem Sie failure.request.url verwenden (wobei failure das Twisted Failure-Objekt ist, das an errback übergeben wird).

# 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-Dokumente geben ein vollständiges Beispiel dafür, wie dies getan werden kann, mit der Ausnahme, dass die Aufrufe des Scrapy-Loggers jetzt abgewertet sind. Daher habe ich mein Beispiel angepasst, um Pythons eingebaute logging ) zu verwenden:

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

1
Michael Yang

Sie können fehlgeschlagene URLs auf zwei Arten erfassen.

  1. Definieren Sie die Scrapeanforderung mit Errback

    class TestSpider(scrapy.Spider):
        def start_requests(self):
            yield scrapy.Request(url, callback=self.parse, errback=self.errback)
    
        def errback(self, failure):
            '''handle failed url (failure.request.url)'''
            pass
    
  2. Verwenden Sie signals.item_dropped

    class TestSpider(scrapy.Spider):
        def __init__(self):
            crawler.signals.connect(self.request_dropped, signal=signals.request_dropped)
    
        def request_dropped(self, request, spider):
            '''handle failed url (request.url)'''
            pass
    

[! Notice] Die Scrapy-Anforderung mit Errback kann einen Fehler bei der automatischen Wiederholung nicht feststellen, z. B. Verbindungsfehler, RETRY_HTTP_CODES in-Einstellungen.

1
jdxin0

Grundsätzlich ignoriert Scrapy 404 Fehler standardmäßig, es wurde in der httperror-Middleware definiert.

Fügen Sie Ihrer Einstellungsdatei also HTTPERROR_ALLOW_ALL = True hinzu.

Danach können Sie auf response.status über Ihre parse-Funktion zugreifen.

Sie können damit umgehen.

def parse(self,response):
    if response.status==404:
        print(response.status)
    else:
        do something
0
Mohan B E