如何配置Scrapy:已有Content-Length时不再自动添加并保持请求头顺序
解决Scrapy/Twisted中Content-Length重复及头顺序问题
问题根源
Twisted作为Scrapy的底层网络库,会自动计算并添加Content-Length字段,哪怕你已经在请求头中手动设置了该字段。如果手动设置的长度和实际body长度不匹配,还会导致请求头中出现两个Content-Length;即便长度匹配,自动生成的字段也可能打乱你指定的头顺序。
解决方向1:禁用自动添加,保留自定义Content-Length
通过猴子补丁修改Twisted的逻辑,让它检测到请求头中已有Content-Length时,不再自动生成新的字段。
在爬虫文件开头或项目settings.py中添加以下代码:
from twisted.web._newclient import HTTP11ClientProtocol # 保存原方法 original_calculate = HTTP11ClientProtocol._calculateContentLength def patched_calculate(self, request): # 若请求头已包含Content-Length,直接返回None跳过自动计算 if b'Content-Length' in request.headers: return None return original_calculate(self, request) # 替换原方法 HTTP11ClientProtocol._calculateContentLength = patched_calculate
注意:手动设置的Content-Length必须和实际body的字节长度完全一致,否则会触发服务器错误。比如你的示例中,json.dumps({'foo':'bar'})的utf-8编码字节长度是13,而非14,需要对应修改自定义值。
解决方向2:让自动生成的Content-Length遵循指定顺序
如果希望保留Twisted自动计算长度的功能,同时调整字段位置,可以通过下载中间件重新排序请求头。
- 先从
DEFAULT_REQUEST_HEADERS中移除自定义的Content-Length。 - 创建下载中间件文件(如
middlewares.py),添加以下代码:
from scrapy import signals from scrapy.http.headers import Headers class ReorderHeadersMiddleware: @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): if not request.headers: return # 转换为字典方便操作 headers_dict = dict(request.headers) # 取出自动生成的Content-Length content_length = headers_dict.pop(b'Content-Length', None) if not content_length: return # 按需求定义头顺序 desired_order = [b'aA', b'Bb', b'CC', b'Content-Length', b'dD'] new_headers = Headers() # 按指定顺序添加头 for key in desired_order: if key == b'Content-Length': new_headers[key] = content_length elif key in headers_dict: new_headers[key] = headers_dict[key] # 追加剩余未指定的头 for key in headers_dict: if key not in desired_order: new_headers[key] = headers_dict[key] request.headers = new_headers
- 在
settings.py中启用该中间件:
DOWNLOADER_MIDDLEWARES = { '你的项目名.middlewares.ReorderHeadersMiddleware': 543, }
请求头大小写保留的补充
你当前修改TwistedHeaders._caseMappings的方式是有效的,若网站对Content-Length有特殊大小写要求,只需添加对应的映射即可:
TwistedHeaders._caseMappings[b'content-length'] = b'自定义大小写的Content-Length'
测试时用Fiddler抓包验证,确认头顺序、大小写符合要求,且无重复的Content-Length字段即可。
内容的提问来源于stack exchange,提问作者yeqiuuu
相关产品推荐
相关产品推荐

