Scrapy 作为 Python 生态中最强大的爬虫框架之一,其高扩展性很大程度上依赖于中间件(Middleware)------ 中间件介于 Scrapy 引擎和其他组件之间,能够拦截、修改、增强爬虫的请求(Request)和响应(Response),是实现反爬规避、请求优化的核心利器。本文将聚焦两个实战场景:自定义请求头和代理池实现,通过完整的代码示例和步骤讲解,帮助你掌握 Scrapy 中间件的核心用法。
一、前置准备
-
已安装 Scrapy 框架:
pip install scrapy -
新建 Scrapy 项目(用于实战演示): bash
运行
# 1. 创建项目 scrapy startproject middleware_demo # 2. 进入项目目录 cd middleware_demo # 3. 新建爬虫(以爬取测试站点为例) scrapy genspider test_spider httpbin.org -
核心概念铺垫:
- 本文重点使用下载中间件(Downloader Middleware) ,对应项目中的
middlewares.py文件 - 下载中间件的核心方法:
process_request(self, request, spider)(请求发送前拦截处理) - 所有自定义中间件必须在
settings.py中启用才能生效
- 本文重点使用下载中间件(Downloader Middleware) ,对应项目中的
二、实战一:自定义请求头(User-Agent 池)
爬虫请求中,固定的User-Agent极易被目标网站识别并封禁,通过中间件实现User-Agent池随机切换,是最基础的反爬手段之一。
步骤 1:编写自定义请求头中间件
打开项目中的middlewares.py,新增RandomUserAgentMiddleware类,实现User-Agent随机切换:
python
运行
import random
from scrapy import signals
class MiddlewareDemoDownloaderMiddleware:
# 项目默认中间件,保留基础结构即可
@classmethod
def from_crawler(cls, crawler):
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_request(self, request, spider):
return None
def process_response(self, request, response, spider):
return response
def process_exception(self, request, exception, spider):
pass
def spider_opened(self, spider):
spider.logger.info("Spider opened: %s" % spider.name)
# 新增:自定义User-Agent池中间件
class RandomUserAgentMiddleware:
# 1. 定义User-Agent池(涵盖主流浏览器、设备)
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 13; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1"
]
def process_request(self, request, spider):
# 2. 随机选择一个User-Agent
random_ua = random.choice(self.USER_AGENTS)
# 3. 为请求设置自定义请求头(替换默认User-Agent)
request.headers.setdefault('User-Agent', random_ua)
# 可选:添加其他自定义请求头(如Referer、Accept等)
request.headers.setdefault('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')
request.headers.setdefault('Referer', 'https://www.baidu.com/')
spider.logger.info(f"已设置请求头:User-Agent -> {random_ua[:50]}...")
return None
步骤 2:在 settings.py 中启用中间件
打开settings.py,找到DOWNLOADER_MIDDLEWARES配置项(默认被注释),取消注释并添加自定义中间件(优先级数值越小,执行顺序越靠前,通常设置为 100-900 之间):
python
运行
# 下载中间件配置
DOWNLOADER_MIDDLEWARES = {
# 启用自定义User-Agent中间件
"middleware_demo.middlewares.RandomUserAgentMiddleware": 543,
# 禁用Scrapy默认的User-Agent中间件(避免冲突)
"scrapy.downloadermiddlewares.useragent.UserAgentMiddleware": None,
}
步骤 3:测试自定义请求头
修改test_spider.py,爬取httpbin.org/headers(该接口会返回请求头信息,用于验证):
python
运行
import scrapy
class TestSpiderSpider(scrapy.Spider):
name = "test_spider"
allowed_domains = ["httpbin.org"]
start_urls = ["https://httpbin.org/headers"]
def parse(self, response):
# 打印响应结果(查看请求头是否生效)
self.logger.info("响应结果:\n" + response.text)
yield None
步骤 4:运行爬虫验证效果
执行以下命令运行爬虫:
bash
运行
scrapy crawl test_spider
运行成功后,可在日志中看到:
- 爬虫打印出已设置的随机
User-Agent - 响应结果中
headers部分的User-Agent与设置的一致,且每次运行都会随机切换
三、实战二:自定义代理池实现
当爬虫频繁请求目标网站时,固定 IP 极易被封禁,通过中间件实现代理池随机切换,能够有效隐藏真实 IP,规避 IP 封禁风险。
步骤 1:准备代理 IP 池
代理 IP 可通过免费代理站点(如西刺代理、快代理)或付费代理服务获取,格式为http://ip:端口或https://ip:端口(注意:免费代理稳定性较差,生产环境建议使用付费代理)。
步骤 2:编写自定义代理池中间件
继续在middlewares.py中,新增RandomProxyMiddleware类:
python
运行
# 新增:自定义代理池中间件
class RandomProxyMiddleware:
# 1. 定义代理IP池(格式:http/https + ip + 端口,可添加带账号密码的代理:http://user:pass@ip:port)
PROXIES = [
"http://123.124.135.136:8080",
"http://117.121.10.11:9090",
"https://220.225.8.9:8888",
# 更多代理IP...
]
def process_request(self, request, spider):
# 2. 随机选择一个代理IP
random_proxy = random.choice(self.PROXIES)
try:
# 3. 为请求设置代理
request.meta['proxy'] = random_proxy
# 可选:处理带账号密码的代理(如需认证)
# import base64
# proxy_user_pass = "username:password"
# encoded_user_pass = base64.b64encode(proxy_user_pass.encode()).decode()
# request.headers['Proxy-Authorization'] = f'Basic {encoded_user_pass}'
spider.logger.info(f"已设置代理:{random_proxy}")
except Exception as e:
spider.logger.error(f"代理设置失败:{str(e)}")
return None
# 可选:处理代理异常(如代理不可用,重新请求)
def process_exception(self, request, exception, spider):
spider.logger.error(f"代理 {request.meta.get('proxy')} 不可用,触发异常:{str(exception)}")
# 重新生成请求(不添加dont_filter=True会被Scrapy去重过滤)
new_request = request.copy()
new_request.dont_filter = True
return new_request
步骤 3:在 settings.py 中启用代理池中间件
更新DOWNLOADER_MIDDLEWARES配置,添加代理池中间件:
python
运行
DOWNLOADER_MIDDLEWARES = {
# 启用自定义User-Agent中间件
"middleware_demo.middlewares.RandomUserAgentMiddleware": 543,
# 启用自定义代理池中间件(优先级略低于User-Agent中间件)
"middleware_demo.middlewares.RandomProxyMiddleware": 600,
# 禁用Scrapy默认的User-Agent中间件
"scrapy.downloadermiddlewares.useragent.UserAgentMiddleware": None,
}
步骤 4:优化代理池(可选,生产环境必备)
上述代理池为固定静态列表,实际使用中存在代理失效、数量不足等问题,可进行以下优化:
-
动态获取代理:编写接口从代理服务提供商获取实时有效代理(如通过 API 接口拉取)
-
代理有效性检测:在中间件中添加代理连通性检测,剔除失效代理
-
代理权重分配:根据代理的响应速度、成功率分配权重,优先使用优质代理
-
添加代理超时配置 :在
settings.py中设置代理请求超时时间python
运行
# 下载超时时间(单位:秒) DOWNLOAD_TIMEOUT = 10
步骤 5:测试代理池效果
重新运行爬虫:
bash
运行
scrapy crawl test_spider
在日志中可看到随机切换的代理 IP,同时爬取httpbin.org/ip(修改start_urls为["https://httpbin.org/ip"])可查看当前出口 IP,验证代理是否生效。
四、核心注意事项与最佳实践
-
中间件优先级 :
DOWNLOADER_MIDDLEWARES中的优先级数值决定执行顺序,建议遵循 Scrapy 默认规范:请求头设置(500 左右)→ 代理设置(600 左右)→ 其他处理(700+) -
禁用冲突中间件:启用自定义请求头 / 代理中间件时,需禁用 Scrapy 默认的对应中间件,避免配置冲突
-
日志记录:在中间件中添加详细日志,便于排查请求失败、代理失效等问题
-
代理合法性:使用代理时需遵守目标网站的 robots 协议和相关法律法规,禁止用于恶意爬取
-
请求去重 :处理代理异常重新请求时,需设置
dont_filter=True,避免被 Scrapy 的去重机制过滤 -
生产环境优化 :
- 请求头和代理池建议存储在配置文件(如
config.json)中,便于维护 - 添加代理池自动更新机制,定期剔除失效代理
- 结合
DOWNLOAD_DELAY设置请求延迟,降低反爬风险
python
运行
# settings.py中设置请求延迟(单位:秒) DOWNLOAD_DELAY = 2 - 请求头和代理池建议存储在配置文件(如
五、总结
- Scrapy 下载中间件是实现请求定制化的核心,通过
process_request方法可拦截并修改请求头、代理等关键配置 - 自定义请求头(
User-Agent池)和代理池是规避反爬的基础手段,核心步骤为「编写中间件→启用中间件→测试验证」 - 静态池(请求头 / 代理)适合快速验证,生产环境需实现动态更新、有效性检测等高级功能,提升爬虫的稳定性和可用性
通过本文的实战操作,你不仅掌握了两种核心中间件的实现,更理解了 Scrapy 中间件的工作逻辑,后续可基于此扩展更多功能(如 Cookie 池、请求重试、反反爬解密等),打造更加强大的爬虫系统。