私はスクレイピーの初心者であり、私が知っている驚くべきクローラーフレームワークです!
私のプロジェクトでは、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を表示します。ありがとう!
はい、これは可能です。
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)}
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
ファイル内のアイテムのリストが表示されます。
@Talvalinと@alecxeからの回答は大いに役立ちましたが、応答オブジェクト(たとえば、twisted.internet.error.TimeoutError
やtwisted.web.http.PotentialDataLoss
)を生成しないダウンローダーイベントをキャプチャーしていないようです。これらのエラーは、実行の終了時に統計ダンプに表示されますが、メタ情報はありません。
here を見つけたので、エラーは stats.py ミドルウェアによって追跡され、DownloaderStats
クラスのprocess_exception
メソッドでキャプチャされ、具体的にはex_class
変数で、必要に応じて各エラータイプをインクリメントし、実行の終了時にカウントをダンプします。
このようなエラーを対応するリクエストオブジェクトからの情報と一致させるには、各リクエストに一意のIDを追加し(request.meta
経由)、process_exception
のstats.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'....}
この回答 非ダウンローダーベースのエラーを処理します。
Scrapyはデフォルトで404を無視し、解析しません。応答でエラーコード404を受け取っている場合は、非常に簡単な方法でこれを処理できます。
settings.pyで、次のように書きます:
HTTPERROR_ALLOWED_CODES = [404,403]
次に、解析関数で応答ステータスコードを処理します。
def parse(self,response):
if response.status == 404:
#your action on error
スクレイピー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
これはこの質問の更新です。同様の問題に遭遇し、スクレイピー信号を使用してパイプラインの関数を呼び出す必要がありました。 @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)
これが将来同じ問題を抱えている人に役立つことを願っています。
失敗したURLは2つの方法でキャプチャできます。
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
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 。
これらの回答の一部に加えて、Twistedエラーを追跡する場合は、Requestオブジェクトのerrback
パラメーターの使用を検討します。このパラメーターでは、コールバック関数を Twisted Failure リクエスト失敗時。 URLに加えて、このメソッドを使用すると、失敗のタイプを追跡できます。
その後、次を使用してURLを記録できます。failure.request.url
(ここで、failure
はFailure
に渡される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
基本的に、Scrapyはデフォルトで404エラーを無視します。httperrorミドルウェアで定義されています。
そのため、設定ファイルにHTTPERROR_ALLOW_ALL = Trueを追加します。
この後、parse関数を介してresponse.statusにアクセスできます。
このように処理できます。
def parse(self,response):
if response.status==404:
print(response.status)
else:
do something