You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置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自动计算长度的功能,同时调整字段位置,可以通过下载中间件重新排序请求头。

  1. 先从DEFAULT_REQUEST_HEADERS中移除自定义的Content-Length。
  2. 创建下载中间件文件(如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
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 05:07:42