Подтвердить что ты не робот

Как получить URL-адрес неудачных попыток?

Я новичок в терапии, и это потрясающая рама для гусениц, которую я знаю!

В моем проекте я отправил более 90 000 запросов, но некоторые из них потерпели неудачу. Я установил уровень журнала 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-адреса. Спасибо!

4b9b3361

Ответ 1

Да, это возможно.

Я добавил список failed_urls в мой класс паука и добавленные URL-адреса, если статус ответа был 404 (это нужно будет расширить, чтобы охватить другие статусы ошибок).

Затем я добавил дескриптор, который объединяет список в одну строку и добавляет его в статистику при закрытии паука.

На основе ваших комментариев можно отслеживать ошибки 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)

Результат (статистика загрузчика /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)}

Ответ 2

Вот еще один пример, как обрабатывать и собирать 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.

Ответ 3

Ответы от @Talvalin и @alecxe мне очень помогли, но они, похоже, не захватывают события загрузчика, которые не генерируют объект ответа (например, twisted.internet.error.TimeoutError и twisted.web.http.PotentialDataLoss), Эти ошибки отображаются в дампе статистики в конце прогона, но без метаданных.

Как я узнал здесь, ошибки отслеживаются промежуточным программным обеспечением stats.py, захваченным в методе process_exception класса DownloaderStats и, в частности, в переменной ex_class, которая при необходимости увеличивает каждый тип ошибки и затем сбрасывает счетчики в конце бег.

Чтобы сопоставить такие ошибки с информацией из соответствующего объекта запроса, вы можете добавить уникальный идентификатор к каждому запросу (через request.meta), а затем потянуть его в метод process_exception для stats.py:

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

Это создаст уникальную строку для каждой ошибки на основе загрузчика, не сопровождаемой ответом. Затем вы можете сохранить измененный stats.py как что-то еще (например, my_stats.py), добавить его в downloadermiddlewares (с правильным приоритетом) и отключить stats.py.

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

Результат в конце прогона выглядит так (здесь используется метаинформация, где каждый URL-адрес запроса сопоставляется с group_id и member_id, разделенным косой чертой, например '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'....}

Этот ответ касается ошибок, не связанных с загрузкой.

Ответ 4

Scrapy игнорирует 404 по умолчанию и не анализирует его. Если в ответ вы получаете код ошибки 404, вы можете справиться с этим очень простым способом.

В settings.py напишите:

HTTPERROR_ALLOWED_CODES = [404,403]

А затем обработайте код состояния ответа в вашей функции разбора:

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

Ответ 5

Как и в случае с scrapy 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

Ответ 6

Это обновление по этому вопросу. Я столкнулся с подобной проблемой и мне нужно было использовать сигналы scrapy для вызова функции в моем конвейере. Я редактировал код @Talvalin, но хотел дать ответ только для некоторой ясности.

В принципе, вы должны добавить себя как аргумент для handle_spider_closed. Вы также должны вызвать диспетчера в init, чтобы вы могли передать экземпляр spider (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)

Я надеюсь, что это поможет любому, у кого есть такая же проблема в будущем.

Ответ 7

В дополнение к некоторым из этих ответов, если вы хотите отслеживать ошибки Twisted, я бы посмотрел на использование параметра Request object errback, на котором вы можете установить функцию обратного вызова, вызываемую с помощью Скрученная ошибка при ошибке запроса. В дополнение к URL-адресу этот метод позволяет отслеживать тип сбоя.

Затем вы можете зарегистрировать URL-адреса, используя: failure.request.url (где failure - объект Twisted failure, переданный в 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 теперь обесценились, поэтому Я адаптировал свой пример для использования Python, встроенного в logging):

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

Ответ 8

Вы можете захватывать неудачные URL-адреса двумя способами.

  1. Определить запрос на лечение с ошибкой

    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. Использовать сигналы.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] Запрос Scrapy с errback не может поймать ошибку автоматического повтора попыток, например ошибку соединения, RETRY_HTTP_CODES в настройках.

Ответ 9

В основном Scrapy игнорирует ошибку 404 по умолчанию, она была определена в промежуточном программном обеспечении httperror.

Итак, добавьте HTTPERROR_ALLOW_ALL = True в ваш файл настроек.

После этого вы можете получить доступ к response.status через свою функцию синтаксического анализа.

Вы можете справиться с этим так.

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