# 13.6 Spider Middleware 的用法

Spider Middleware 是介入到 Scrapy 的 Spider 处理机制的钩子框架。我们首先来看看它的架构，如图 13-1 所示。

当 Downloader 生成 Response 之后，Response 会被发送给 Spider，在发送给 Spider 之前，Response 会首先经过 Spider Middleware 处理，当 Spider 处理生成 Item 和 Request 之后，Item 和 Request 还会经过 Spider Middleware 的处理。

Spider Middleware 有如下三个作用。

* 我们可以在 Downloader 生成的 Response 发送给 Spider 之前，也就是在 Response 发送给 Spider 之前对 Response 进行处理。

* 我们可以在 Spider 生成的 Request 发送给 Scheduler 之前，也就是在 Request 发送给 Scheduler 之前对 Request 进行处理。

* 我们可以在 Spider 生成的 Item 发送给 Item Pipeline 之前，也就是在 Item 发送给 Item Pipeline 之前对 Item 进行处理。

### 1. 使用说明

需要说明的是，Scrapy 其实已经提供了许多 Spider Middleware，它们被 SPIDER_MIDDLEWARES_BASE 这个变量所定义。

SPIDER_MIDDLEWARES_BASE 变量的内容如下：

```python
{
    'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,
    'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,
    'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,
    'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,
}
```

和 Downloader Middleware 一样，Spider Middleware 首先加入到 SPIDER_MIDDLEWARES 设置中，该设置会和 Scrapy 中 SPIDER_MIDDLEWARES_BASE 定义的 Spider Middleware 合并。然后根据键值的数字优先级排序，得到一个有序列表。第一个 Middleware 是最靠近引擎的，最后一个 Middleware 是最靠近 Spider 的。

### 2. 核心方法

Scrapy 内置的 Spider Middleware 为 Scrapy 提供了基础的功能。如果我们想要扩展其功能，只需要实现某几个方法即可。

每个 Spider Middleware 都定义了以下一个或多个方法的类，核心方法有如下 4 个。

* process_spider_input(response, spider)
* process_spider_output(response, result, spider)
* process_spider_exception(response, exception, spider)
* process_start_requests(start_requests, spider)

只需要实现其中一个方法就可以定义一个 Spider Middleware。下面我们来看看这 4 个方法的详细用法。

#### process_spider_input(response, spider)

当 Response 通过 Spider Middleware 时，该方法被调用，处理该 Response。

方法的参数有两个：
* response，即 Response 对象，即被处理的 Response
* spider，即 Spider 对象，即该 response 对应的 Spider

process_spider_input() 应该返回 None 或者抛出一个异常。

* 如果其返回 None ，Scrapy 将会继续处理该 Response，调用所有其他的 Spider Middleware 直到 Spider 处理该 Response。

* 如果其抛出一个异常，Scrapy 将不会调用任何其他 Spider Middlewar e 的 process_spider_input() 方法，并调用 Request 的 errback() 方法。 errback 的输出将会以另一个方向被重新输入到中间件中，使用 process_spider_output() 方法来处理，当其抛出异常时则调用 process_spider_exception() 来处理。

#### process_spider_output(response, result, spider)

当 Spider 处理 Response 返回结果时，该方法被调用。

方法的参数有三个：
* response，即 Response 对象，即生成该输出的 Response
* result，包含 Request 或 Item 对象的可迭代对象，即 Spider 返回的结果
* spider，即 Spider 对象，即其结果对应的 Spider

process_spider_output() 必须返回包含 Request 或 Item 对象的可迭代对象。

#### process_spider_exception(response, exception, spider)

当 Spider 或 Spider Middleware 的 process_spider_input() 方法抛出异常时， 该方法被调用。

方法的参数有三个：

* response，即 Response 对象，即异常被抛出时被处理的 Response
* exception，即 Exception 对象，被抛出的异常
* spider，即 Spider 对象，即抛出该异常的 Spider

process_spider_exception() 必须要么返回 None ， 要么返回一个包含 Response 或 Item 对象的可迭代对象。

* 如果其返回 None ，Scrapy 将继续处理该异常，调用其他 Spider Middleware 中的 process_spider_exception() 方法，直到所有 Spider Middleware 都被调用。
* 如果其返回一个可迭代对象，则其他 Spider Middleware 的 process_spider_output() 方法被调用， 其他的 process_spider_exception() 将不会被调用。

#### process_start_requests(start_requests, spider)

该方法以 Spider 启动的 Request 为参数被调用，执行的过程类似于 process_spider_output() ，只不过其没有相关联的 Response 并且必须返回 Request。

方法的参数有两个：
* start_requests，即包含 Request 的可迭代对象，即 Start Requests
* spider，即 Spider 对象，即 Start Requests 所属的 Spider

其必须返回另一个包含 Request 对象的可迭代对象。

### 3. 结语

本节介绍了 Spider Middleware 的基本原理和自定义 Spider Middleware 的方法。Spider Middleware 使用的频率不如 Downloader Middleware 的高，在必要的情况下它可以用来方便数据的处理。