Scrapy结合Splash时返回Splash服务端响应头而非目标站点头如何解决
Scrapy + Splash 获取目标站点原始响应头方案
问题原因
Splash是独立的渲染代理服务,你向Splash服务发起请求时,response.headers默认返回的是Splash服务本身的HTTP响应头,目标站点的原始响应头存储在Splash返回的结构化元数据中,不会自动覆盖到Scrapy的响应头字段。
解决方案
方案1:手动从HAR数据中提取
Splash支持返回HAR(HTTP归档)格式的请求记录,里面包含了渲染过程中所有网络请求的完整信息,包括目标站点的响应头,提取步骤如下:
- 构造
SplashRequest时开启HAR记录参数:
from scrapy_splash import SplashRequest yield SplashRequest( url="目标站点URL", callback=self.parse, args={ "har": 1, # 开启HAR记录 "html": 1, # 同时返回渲染后的HTML,不影响正常页面解析 } )
- 回调函数中提取目标响应头:
def parse(self, response): # 读取HAR数据 har_data = response.data["har"] # 默认第一条entry为目标站点主页面请求,有重定向场景可调整为最后一条 main_request_entry = har_data["log"]["entries"][0] # 提取原始响应头 target_headers = { header["name"]: header["value"] for header in main_request_entry["response"]["headers"] } # 测试打印 print(target_headers) # 后续可正常解析页面内容
方案2:自定义中间件自动替换响应头
如果需要全局使用目标站点的响应头,可自定义下载中间件自动替换Splash的服务响应头:
- 新建自定义中间件:
class TargetHeaderReplaceMiddleware: def process_response(self, request, response, spider): # 仅处理带HAR数据的Splash响应 if hasattr(response, "data") and "har" in response.data: try: entry = response.data["har"]["log"]["entries"][0] # 转换为Scrapy响应头的bytes格式 response.headers = { h["name"].encode(): [h["value"].encode()] for h in entry["response"]["headers"] } except (KeyError, IndexError): # 提取失败时保留原有响应头,不影响正常请求 pass return response
- 在
settings.py中注册中间件,优先级需高于默认的压缩中间件、低于Splash核心中间件:
DOWNLOADER_MIDDLEWARES = { 'scrapy_splash.SplashCookiesMiddleware': 723, 'scrapy_splash.SplashMiddleware': 725, # 替换为你自己项目的中间件路径 '你的项目名称.middlewares.TargetHeaderReplaceMiddleware': 726, 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810, }
配置完成后,直接调用response.headers即可获取目标站点的原始响应头。
注意事项
- 如果目标站点存在301/302重定向,需调整entry的取值,取HAR记录中最后一个对应目标域名的请求条目即可
- 需保证Splash服务版本在2.0以上,低版本存在HAR字段返回不全的问题
内容的提问来源于stack exchange,提问作者sleepless_in_seattle
相关产品推荐
相关产品推荐

