爬虫工作量由小到大的思维转变---<第六十七章 > Scrapy异常处理中的核心异常类型

时间:2024-04-05 20:32:37

前言:

        Scrapy作为一个强大的爬虫框架,其异常处理机制十分重要。异常表示程序在运行时发生了问题或错误,如果不加以处理,可能导致爬虫直接崩溃。Scrapy通过自定义异常类型实现了非常灵活的异常处理机制。

        Scrapy的异常主要定义在scrapy.exceptions模块中,该模块包含了一些核心异常类,这些异常类继承自ScrapyException基类。当Scrapy程序运行过程中发生异常时,可以通过捕获这些异常来进行相应的处理,保证爬虫能够稳定运行。

        本文将重点介绍scrapy.exceptions模块中的核心异常类型,包括ScrapyException、DropItem、IgnoreRequest等,这些异常类型在Scrapy的爬虫程序中广泛应用。通过对这些异常的理解,可以更好地处理Scrapy爬虫运行时可能发生的各种异常情况,编写出健壮的爬虫程序。

        接下来的内容将首先介绍ScrapyException的特点,然后依次讲解其常见的子类异常,包括各种异常的定义、触发条件、处理方法等,并配合示例代码进行讲解。最后,本文将给出一些Scrapy异常处理的技巧与经验总结。希望本文可以成为Scrapy异常处理的实用指南,帮助大家熟练应对Scrapy爬虫中的各种异常情况。


正文:

1. ScrapyException

        ScrapyException是Scrapy框架中的基本异常类,它是所有Scrapy异常的基类。它提供了一种捕获和处理Scrapy运行时错误的机制。

常见的ScrapyException子类包括:

  1. CloseSpider:当Spider需要提前关闭并停止爬取任务时抛出的异常。
  2. DropItem:用于在Item Pipeline中丢弃某个Item的异常。
  3. IgnoreRequest:用于在中间件中忽略某个Request的异常。
  4. NotConfigured:表示Scrapy的某个组件未正确配置的异常。
  5. NotSupported:表示Scrapy不支持某种功能或操作的异常。

以下是关于这些异常类的示例代码:

触发各种ScrapyException子类异常的示例:

import scrapy

class MySpider(scrapy.Spider):
    name = 'my_spider'

    def start_requests(self):
        # 触发CloseSpider
        raise scrapy.exceptions.CloseSpider('Spider closed')

    def parse(self, response):
        # 触发DropItem
        item = {'name': 'item_name'}
        raise scrapy.exceptions.DropItem(f'Dropping item: {item}')

        # 触发IgnoreRequest
        yield scrapy.Request('http://example.com', callback=self.parse,
                             errback=lambda failure: raise scrapy.exceptions.IgnoreRequest())

class MyMiddleware:
    def process_request(self, request, spider):
        # 触发NotSupported
        if request.url.startswith('http://not_supported'):
            raise scrapy.exceptions.NotSupported(f'Request not supported: {request.url}')

        # 触发NotConfigured
        if not spider.settings.get('MY_SETTING'):
            raise scrapy.exceptions.NotConfigured('MY_SETTING is not configured')

自定义ScrapyException子类的示例:

import scrapy

class MyCustomException(scrapy.exceptions.ScrapyException):
    pass

class MySpider(scrapy.Spider):
    name = 'my_spider'

    def parse(self, response):
        # 触发自定义异常
        raise MyCustomException('Custom exception raised')

异常处理示例:

import scrapy

class MySpider(scrapy.Spider):
    name = 'my_spider'

    def parse(self, response):
        try:
            # 执行某些操作
            # ...
        except scrapy.exceptions.CloseSpider as e:
            # 处理CloseSpider异常
            self.logger.error(f'CloseSpider exception: {e}')
            # 执行相应的中止操作

        except scrapy.exceptions.DropItem as e:
            # 处理DropItem异常
            self.logger.error(f'DropItem exception: {e}')
            # 执行相应的处理操作

        except scrapy.exceptions.IgnoreRequest as e:
            # 处理IgnoreRequest异常
            self.logger.warning(f'IgnoreRequest exception: {e}')
            # 执行相应的处理操作

        except scrapy.exceptions.NotConfigured as e:
            # 处理NotConfigured异常
            self.logger.error(f'NotConfigured exception: {e}')
            # 执行相应的处理操作

        except scrapy.exceptions.NotSupported as e:
            # 处理NotSupported异常
            self.logger.error(f'NotSupported exception: {e}')
            # 执行相应的处理操作

        except Exception as e:
            # 捕获其他异常
            self.logger.error(f'Unhandled exception: {e}')
            # 执行相应的处理操作

        finally:
            # 在异常处理结束后执行清理操作
            # ...

        在异常处理示例中,使用了try-except语句来捕获不同类型的Scrapy异常,并根据需要执行相应的处理操作。异常处理块中包含了对CloseSpider、DropItem、IgnoreRequest、NotConfigured和NotSupported这些异常的处理逻辑。同时,还使用了一个通用的Exception块来捕获其他未处理的异常,并执行相应的处理操作。

ps: 这些示例只是帮助理解Scrapy框架中异常的使用场景及异常处理的方式。对于不同的异常类型,你可以根据实际需求编写相应的处理代码,以实现更灵活和可靠的异常处理机制。

2. DropItem异常

        在Spider中引发DropItem异常是一种常见的数据过滤机制,用于在解析函数中删除不需要的项。DropItem异常允许我们根据自定义规则过滤item,并且在pipeline中进行处理。

示例情景:

你正在编写一个爬虫来收集在线商店的产品信息。假设你的网站要求您仅保留价格低于100美元的产品。为了实现此过滤功能,可以使用DropItem异常。

首先,让我们创建一个名为ProductSpider的Spider,并定义解析函数parse_product来处理爬取到的产品页面。

import scrapy
from scrapy.exceptions import DropItem

class ProductSpider(scrapy.Spider):
    name = "products"
    start_urls = ['https://example.com/products']

    def parse(self, response):
        for product in response.css('div.product'):
            yield self.parse_product(product)

    def parse_product(self, product):
        name = product.css('div.name::text').get()
        price = product.css('div.price::text').get()
        if price is None or not price.startswith('$'):
            raise DropItem("无效价格: %s" % price)
        
        price_value = float(price.strip('$'))
        if price_value > 100:
            raise DropItem("价格超过100美元: %s" % price)

        yield {
            'name': name,
            'price': price
        }

在以上示例中,我们使用DropItem异常来根据自定义规则过滤产品项。首先,我们检查产品的价格是否有效。如果价格为空或不以美元符号开头,我们抛出DropItem异常并提供相应的错误信息。

接下来,我们将价格值转换为浮点数,并与100进行比较。如果价格超过100美元,我们再次引发DropItem异常。

通过这种方式,在解析函数中过滤item,并通过抛出DropItem异常来删除不需要的产品项,我们可以确保最终输出的数据满足我们的条件。

接下来,让我们继续探讨

如何在Item Pipeline中使用DropItem异常来清洗数据。

我们可以创建一个名为PriceValidationPipeline的Item Pipeline,它负责验证产品价格是否有效,并根据需要丢弃无效的item。

from scrapy.exceptions import DropItem

class PriceValidationPipeline:
    def process_item(self, item, spider):
        price = item.get('price')
        if price is None or not price.startswith('$'):
            raise DropItem("无效价格: %s" % price)
        
        price_value = float(price.strip('$'))
        if price_value > 100:
            raise DropItem("价格超过100美元: %s" % price)

        return item

在以上示例中,process_item方法用于处理每个item。我们使用相同的规则进行价格验证,并通过抛出DropItem异常来丢弃不需要的item。

要启用这个Item Pipeline,需要在Scrapy项目的配置文件(如settings.py)中添加以下行:

ITEM_PIPELINES = {
    'myproject.pipelines.PriceValidationPipeline': 100,
}

通过这种方式,可以在item pipeline中引发DropItem异常来删除不需要的item,并确保输入到下一个pipeline或最终输出的数据满足您的条件。

小总结:

DropItem异常是在Spider和Item Pipeline中过滤item的强大工具。您可以根据自定义规则使用这个异常来删除无效的item,从而清洗数据并获得符合要求的最终结果。

3. IgnoreRequest异常

        IgnoreRequest异常是Scrapy框架中常用的异常之一,用于在Spider中间件和下载器中间件中过滤请求。在本节中,我将为您提供一个详细的中文案例,展示如何使用IgnoreRequest异常根据自定义规则过滤请求和根据响应状态过滤请求。

首先,让我们来看看

如何在Spider中间件中引发IgnoreRequest异常以根据自定义规则过滤请求。

假设你正在编写一个爬虫来收集电影信息,并且有一个规则要求电影的评分必须高于7分才能进行爬取。为了实现这个过滤功能,可以在Spider中间件中使用IgnoreRequest异常。

首先,我们创建一个名为MovieSpiderMiddleware的Spider中间件,并在其process_spider_input方法中实现请求过滤规则。

from scrapy.exceptions import IgnoreRequest

class MovieSpiderMiddleware:
    def process_spider_input(self, response, spider):
        rating = response.meta.get('rating')
        if rating is not None and rating < 7:
            raise IgnoreRequest("电影评分过低: %s" % rating)

        return None

在以上示例中,我们使用Spider中间件来处理从下载器返回的响应。我们通过使用response.meta.get('rating')来获取请求中的电影评分,并进行评分过滤。如果评分低于7分,我们引发IgnoreRequest异常来丢弃该请求。

下面让我们看看在下载器中间件中

如何引发IgnoreRequest异常以根据响应状态过滤请求。

假设你的爬虫目标是收集一些特定网站的新闻文章。然而,有时候这些文章的响应状态可能是404(页面未找到)。为了避免爬取到无效的文章,你可以在下载器中间件中使用IgnoreRequest异常来过滤这些请求。

首先,我们创建一个名为HttpStatusMiddleware的下载器中间件,并在其process_response方法中实现请求过滤规则。

from scrapy.exceptions import IgnoreRequest

class HttpStatusMiddleware:
    def process_response(self, request, response, spider):
        if response.status == 404:
            raise IgnoreRequest("获取文章失败: 404 Not Found")

        return response

在以上示例中,我们使用下载器中间件来处理从下载器返回的响应。我们检查响应的状态是否为404,如果是,我们引发IgnoreRequest异常来丢弃该请求。

通过这种方式,在下载器中间件中引发IgnoreRequest异常可以根据响应状态过滤请求,并确保通过的请求都是有效的。

小总结:

IgnoreRequest异常是一个非常有用的工具,用于在Spider中间件和下载器中间件中过滤请求。通过在中间件中引发这个异常,我们可以根据自定义规则或响应状态来丢弃不需要的请求,从而提高爬虫的效率并获得符合需求的有效数据。

4. NotConfigured

        NotConfigured异常是在Scrapy框架中常见的异常之一,它指示某个组件未正确配置或缺少必要的参数。

NotConfigured异常的基本概念。

        NotConfigured异常表示某个组件或扩展没有正确配置或缺少必要的参数,从而无法正常工作。这个异常通常是Scrapy中的一个协议,用于提醒开发人员在配置或初始化过程中出现了问题。

        NotConfigured异常的常见原因之一是缺少设置必要的组件参数。在Scrapy中,许多组件或扩展都有一些必须的参数,用于配置其行为和功能。如果这些参数没有正确设置,就会触发NotConfigured异常。

案例:

        假设你希望编写一个爬虫来收集在线商店的产品信息。使用一个自定义的Item Pipeline来处理和保存数据。为了实现这个目标,需要在配置文件中设置必要的参数。

首先,让我们创建一个名为CustomPipeline的自定义Item Pipeline,并定义一个参数database_uri来指定保存数据的数据库连接 URI。

from scrapy.exceptions import NotConfigured

class CustomPipeline:
    def __init__(self, database_uri=None):
        if database_uri is None:
            raise NotConfigured("缺少数据库连接参数")
        self.database_uri = database_uri

    @classmethod
    def from_crawler(cls, crawler):
        database_uri = crawler.settings.get('DATABASE_URI')
        if not database_uri:
            raise NotConfigured("缺少数据库连接参数")

        return cls(database_uri=database_uri)

    def process_item(self, item, spider):
        # 处理和保存数据的代码
        pass

在以上示例中,我们首先在__init__方法中检查database_uri参数是否为None。如果是None,我们引发NotConfigured异常。

接下来,我们使用from_crawler方法来从Scrapy的配置中获取DATABASE_URI参数,并进行同样的检查。如果缺少这个参数,我们同样引发NotConfigured异常。

通过这种方式,我们确保在设置自定义Item Pipeline时必须提供database_uri参数。如果参数缺失,NotConfigured异常将被引发。

在Scrapy的配置文件(如settings.py)中,您需要添加以下行来启用这个自定义Item Pipeline:

ITEM_PIPELINES = {
    'myproject.pipelines.CustomPipeline': 200,
}
DATABASE_URI = 'your_database_uri'

通过这种方式,可以设置必要的参数,并正确启用自定义的Item Pipeline。

小总结:

NotConfigured异常在Scrapy框架中用于指示组件或扩展缺少必要的参数或未正确配置。通过捕获和处理这个异常,我们可以及时发现并修复配置问题,确保组件正常工作并顺利运行。

总结:

        Scrapy是一个强大的爬虫框架,对异常处理机制的重视至关重要。Scrapy通过定义自定义异常类型来实现灵活的异常处理,保证爬虫稳定运行。

        本文重点介绍了scrapy.exceptions模块中的核心异常类型,包括ScrapyException、DropItem、IgnoreRequest等。这些异常类型在Scrapy的爬虫程序中广泛应用。通过对这些异常的理解,可以更好地处理Scrapy爬虫运行时可能发生的各种异常情况,编写出健壮的爬虫程序。

        ScrapyException是Scrapy框架中的基本异常类,是所有Scrapy异常的基类。其常见的子类异常有CloseSpider、DropItem、IgnoreRequest、NotConfigured和NotSupported等。这些异常类型分别用于处理Spider的关闭、丢弃Item、忽略Request、配置错误和不支持的操作。

        总结来说,Scrapy的异常处理机制非常重要,能够帮助我们处理各种异常情况,保证爬虫的稳定运行。通过学习和理解Scrapy中的异常类型及其处理方式,可以编写出健壮的爬虫程序,提高爬取效率和数据质量。