2.6 Middleware (中间件) 2.6 Middleware (中间件) 在 Scrapy 中,中间件 (Middleware) 扮演着“管道”的角色,它允许你自定义 Scrapy 处理请求 (Request) 和响应 (Response) 的流程。你可以使用中间件来修改请求、过滤响应、处理异常等,从而实现各种强大的功能。Scrapy 中间件分为两种类型: Spider Middlewares (爬虫中间件): 处理 Scrapy 引擎和 Spiders 之间的交互。 Downloader Middlewares (下载器中间件): 处理 Scrapy 引擎和 Downloader 之间的交互。 2.6.
在 Scrapy 中,中间件 (Middleware) 扮演着“管道”的角色,它允许你自定义 Scrapy 处理请求 (Request) 和响应 (Response) 的流程。你可以使用中间件来修改请求、过滤响应、处理异常等,从而实现各种强大的功能。Scrapy 中间件分为两种类型:
Spider Middlewares (爬虫中间件): 处理 Scrapy 引擎和 Spiders 之间的交互。
Downloader Middlewares (下载器中间件): 处理 Scrapy 引擎和 Downloader 之间的交互。
中间件的主要作用包括:
修改请求 (Request): 在请求发送到 Downloader 之前,可以修改请求的 Headers、Cookies、Proxy 等。
过滤响应 (Response): 在响应返回给 Spider 之前,可以根据状态码、内容类型等过滤响应。
处理异常: 捕获并处理 Downloader 或 Spider 抛出的异常,例如重试失败的请求。
实现通用功能: 比如处理重定向、设置 User-Agent、进行身份验证等。
以下是 Downloader Middleware 和 Spider Middleware 在 Scrapy 中的工作流程图:
Downloader Middlewares:
当 Engine 调度一个 Request 时,它会依次通过 Downloader Middlewares 的 process_request 方法。
Downloader 处理完 Request 后,生成一个 Response,Response 会依次通过 Downloader Middlewares 的 process_response 方法。
如果在 Downloader 或 process_request 过程中发生异常,会调用 Downloader Middlewares 的 process_exception 方法。
Spider Middlewares:
当 Downloader 返回 Response 给 Engine 后,Engine 会将 Response 传递给 Spider Middlewares 的 process_spider_input 方法。
Spider 处理完 Response 后,生成 Item 或 Request,这些 Item 或 Request 会依次通过 Spider Middlewares 的 process_spider_output 方法。
如果在 Spider 或 process_spider_input 过程中发生异常,会调用 Spider Middlewares 的 process_spider_exception 方法。
要编写自定义 Middleware,你需要创建一个类,并实现以下一个或多个方法:
Downloader Middleware:
process_request(request, spider): 在请求发送到 Downloader 之前调用。
process_response(request, response, spider): 在 Downloader 返回响应之后调用。
process_exception(request, exception, spider): 当 Downloader 或 process_request 抛出异常时调用。
Spider Middleware:
process_spider_input(response, spider): 在 Response 进入 Spider 之前调用。
process_spider_output(response, result, spider): 在 Spider 处理完 Response 后调用。
process_spider_exception(response, exception, spider): 当 Spider 或 process_spider_input 抛出异常时调用。
process_start_requests(start_requests, spider): 处理 start_requests,必须返回一个可迭代对象。
示例:Downloader Middleware - User-Agent 轮换
import random from scrapy import signals class RandomUserAgentMiddleware: def __init__(self, user_agent_list): self.user_agent_list = user_agent_list @classmethod def from_crawler(cls, crawler): return cls(crawler.settings.getlist('USER_AGENT_LIST')) def process_request(self, request, spider): ua = random.choice(self.user_agent_list) request.headers['User-Agent'] = ua
示例:Downloader Middleware - 代理 IP 轮换
import random class ProxyMiddleware: def __init__(self, proxy_list): self.proxy_list = proxy_list @classmethod def from_crawler(cls, crawler): return cls(crawler.settings.getlist('PROXY_LIST')) def process_request(self, request, spider): proxy = random.choice(self.proxy_list) request.meta['proxy'] = proxy
示例:Spider Middleware - 处理无效 Item
class ValidationMiddleware: def process_spider_output(self, response, result, spider): for item in result: if not isinstance(item, dict) or not item.get('title'): # 假设 title 是必须的 print(f"Invalid item found, skipping: {item}") continue # Skip the item yield item
方法详解:
process_request(request, spider):
request: scrapy.Request 对象,代表即将发送的请求。
spider: 处理该请求的 Spider 对象。
返回值:
None: Scrapy 将继续处理该请求,继续执行其他的downloader middleware直到request到达downloader。
Response 对象: Scrapy 将不会继续处理该请求,直接返回该 Response。
Request 对象: Scrapy 将停止处理该请求,并重新调度该 Request。
IgnoreRequest 异常: Scrapy 将忽略该请求。
process_response(request, response, spider):
request: scrapy.Request 对象,代表生成该响应的请求。
response: scrapy.Response 对象,代表 Downloader 返回的响应。
spider: 处理该响应的 Spider 对象。
返回值:
Response 对象: Scrapy 将继续处理该响应,继续执行其他的downloader middleware直到response到达spider。
Request 对象: Scrapy 将停止处理该响应,并重新调度该 Request。
process_exception(request, exception, spider):
request: scrapy.Request 对象,代表发生异常的请求。
exception: 发生的异常对象。
spider: 处理该请求的 Spider 对象。
返回值:
None: Scrapy 将继续处理该异常,继续执行其他的downloader middleware直到异常被处理。
Response 对象: Scrapy 将使用该 Response 来代替原来的异常。
Request 对象: Scrapy 将重新调度该 Request。
process_spider_input(response, spider):
response: scrapy.Response 对象,代表 Downloader 返回的响应。
spider: 处理该响应的 Spider 对象。
返回值:
None 或者抛出一个异常。如果返回 None,Scrapy 将继续处理该响应,并将它传递给 Spider。如果抛出一个异常,Scrapy 将调用 process_spider_exception 方法。process_spider_output(response, result, spider):
response: scrapy.Response 对象,代表 Spider 处理的响应。
result: 一个可迭代对象,包含 Spider 生成的 Item 或 Request。
spider: 处理该响应的 Spider 对象。
返回值:
process_spider_exception(response, exception, spider):
response: scrapy.Response 对象,代表发生异常的响应。
exception: 发生的异常对象。
spider: 处理该响应的 Spider 对象。
返回值:
None 或者一个可迭代对象,包含 Item 或 Request。如果返回 None,Scrapy 将继续处理该异常,并将它传递给其他的 Spider Middleware。如果返回一个可迭代对象,Scrapy 将停止处理该异常,并将该可迭代对象传递给 Engine。process_start_requests(start_requests, spider):
start_requests: 一个包含 start requests 的可迭代对象。
spider: 启动请求的 Spider 对象。
返回值:
要启用自定义 Middleware,需要在 settings.py 文件中进行配置。 使用 DOWNLOADER_MIDDLEWARES 和 SPIDER_MIDDLEWARES 字典来配置中间件。 键是中间件类的路径,值是中间件的优先级。
示例:
DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.RandomUserAgentMiddleware': 543, 'myproject.middlewares.ProxyMiddleware': 750, } SPIDER_MIDDLEWARES = { 'myproject.middlewares.ValidationMiddleware': 800, } # 设置 User-Agent 列表 (在 settings.py 中) USER_AGENT_LIST = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0', ] # 设置代理 IP 列表 (在 settings.py 中) PROXY_LIST = [ 'http://10.10.1.10:3128', 'http://10.10.1.11:1080', ]
优先级:
数值越小,优先级越高。
Scrapy 默认的 Middleware 优先级范围是 0-1000。
自定义 Middleware 的优先级应该在这个范围内。
1. RetryMiddleware (重试中间件): Scrapy 内置的 RetryMiddleware 可以自动重试失败的请求。你可以在 settings.py 中启用它,并配置重试次数、重试状态码等。
DOWNLOADER_MIDDLEWARES = { 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 90, } RETRY_ENABLED = True RETRY_TIMES = 3 RETRY_HTTP_CODES = [500, 502, 503, 504, 400, 408]
2. HttpProxyMiddleware (代理中间件): Scrapy 内置的 HttpProxyMiddleware 可以使用代理 IP。你可以在 settings.py 中启用它,并配置代理 IP。 也可以使用上面的自定义 ProxyMiddleware 实现更灵活的代理轮换策略。
DOWNLOADER_MIDDLEWARES = { 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110, } # 在 Request 中设置代理 def start_requests(self): yield scrapy.Request(url='http://example.com', meta={'proxy': 'http://some.proxy.com:8080'})
3. CookiesMiddleware (Cookies 中间件): Scrapy 内置的 CookiesMiddleware 可以处理 Cookies。 默认情况下,Scrapy 会自动处理 Cookies。 你可以自定义 CookiesMiddleware 来实现更复杂的 Cookies 管理。
Middleware 的顺序很重要,它决定了 Middleware 的执行顺序。
不要在 Middleware 中执行耗时的操作,这会影响 Scrapy 的性能。
使用 Middleware 时,要仔细测试,确保它不会引入新的问题。
合理使用 Middleware 可以提高 Scrapy 的灵活性和可扩展性。
from_crawler 方法是可选的,但建议使用,它可以让你访问 Scrapy 的设置。
Middleware 是 Scrapy 中一个非常强大的组件,它允许你自定义 Scrapy 的请求和响应处理流程。通过编写自定义 Middleware,你可以实现各种强大的功能,例如 User-Agent 轮换、代理 IP 轮换、Cookies 管理、重试失败的请求等。 掌握 Middleware 的使用,可以让你更好地控制 Scrapy 的行为,并构建更强大的爬虫。