web-dev-qa-db-ja.com

スクレイピーエラーURLを取得する方法は?

私はスクレイピーの初心者であり、私が知っている驚くべきクローラーフレームワークです!

私のプロジェクトでは、9万件以上のリクエストを送信しましたが、失敗したものもあります。ログレベルをINFOに設定しましたが、一部の統計情報しか表示できませんが、詳細は表示できません。

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

より詳細なレポートを取得する方法はありますか?たとえば、失敗したURLを表示します。ありがとう!

39
Joe Wu

はい、これは可能です。

Failed_urlsリストをスパイダークラスに追加し、応答のステータスが404の場合はURLを追加しました(他のエラーステータスをカバーするには拡張する必要があります)。

次に、リストを単一の文字列に結合するハンドルを追加し、スパイダーが閉じられたときにそれを統計に追加します。

コメントに基づいて、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)

出力(downloader/exception_count *統計は、例外が実際にスローされた場合にのみ表示されます-ワイヤレスアダプターをオフにした後、スパイダーを実行しようとすることでそれらをシミュレートしました):

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

404エラーを処理および収集する別の例を次に示します(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

scrapy runspiderを指定して-o output.jsonを実行すると、output.jsonファイル内のアイテムのリストが表示されます。

15
alecxe

@Talvalinと@alecxeからの回答は大いに役立ちましたが、応答オブジェクト(たとえば、twisted.internet.error.TimeoutErrortwisted.web.http.PotentialDataLoss)を生成しないダウンローダーイベントをキャプチャーしていないようです。これらのエラーは、実行の終了時に統計ダンプに表示されますが、メタ情報はありません。

here を見つけたので、エラーは stats.py ミドルウェアによって追跡され、DownloaderStatsクラスのprocess_exceptionメソッドでキャプチャされ、具体的にはex_class変数で、必要に応じて各エラータイプをインクリメントし、実行の終了時にカウントをダンプします。

このようなエラーを対応するリクエストオブジェクトからの情報と一致させるには、各リクエストに一意のIDを追加し(request.meta経由)、process_exceptionstats.pyメソッドにプルします。

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

これにより、応答を伴わないダウンローダーベースのエラーごとに一意の文字列が生成されます。次に、変更したstats.pyを別のものとして保存し(例:my_stats.py)、ダウンローダーミドルウェアに追加し(正しい優先順位で)、ストックstats.pyを無効にします。

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

実行終了時の出力は次のようになります(ここでは、各リクエストURLが'0/14'などのスラッシュで区切られたgroup_idとmember_idにマッピングされるメタ情報を使用しています):

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

この回答 非ダウンローダーベースのエラーを処理します。

13
scharfmn

Scrapyはデフォルトで404を無視し、解析しません。応答でエラーコード404を受け取っている場合は、非常に簡単な方法でこれを処理できます。

settings.pyで、次のように書きます:

HTTPERROR_ALLOWED_CODES = [404,403]

次に、解析関数で応答ステータスコードを処理します。

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

スクレイピー0.24.6の時点で、 alecxe で提案されているメソッドは、開始URLでエラーをキャッチしません。開始URLでエラーを記録するには、parse_start_urlsをオーバーライドする必要があります。この目的にアレックスの答えを適応させると、次のようになります。

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

これはこの質問の更新です。同様の問題に遭遇し、スクレイピー信号を使用してパイプラインの関数を呼び出す必要がありました。 @Talvalinのコードを編集しましたが、わかりやすくするために答えを出したいと思いました。

基本的に、selfをhandle_spider_closedの引数として追加する必要があります。また、initでディスパッチャーを呼び出して、スパイダーインスタンス(self)を処理メソッドに渡すこともできます。

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)

これが将来同じ問題を抱えている人に役立つことを願っています。

5
Mattias

失敗したURLは2つの方法でキャプチャできます。

  1. 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. 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]errbackを使用したスクレイピーリクエストは、接続エラーなどの一部の自動再試行の失敗をキャッチできません。設定で RETRY_HTTP_CODES

2
jdxin0

これらの回答の一部に加えて、Twistedエラーを追跡する場合は、Requestオブジェクトのerrbackパラメーターの使用を検討します。このパラメーターでは、コールバック関数を Twisted Failure リクエスト失敗時。 URLに加えて、このメソッドを使用すると、失敗のタイプを追跡できます。

その後、次を使用してURLを記録できます。failure.request.url(ここで、failureFailureに渡されるTwisted 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)

Scrapyのドキュメントでは、これを行う方法の完全な例を示していますが、Scrapyロガーへの呼び出しが depreciated になっていることを除いて、 Pythonの組み込み logging )を使用する例:

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

1
Michael Yang

基本的に、Scrapyはデフォルトで404エラーを無視します。httperrorミドルウェアで定義されています。

そのため、設定ファイルにHTTPERROR_ALLOW_ALL = Trueを追加します。

この後、parse関数を介してresponse.statusにアクセスできます。

このように処理できます。

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