bigywater.tech
Scrapy · 20

RedirectMiddleware:跳转次数与 meta 里的跳转记录

服务器返回 302 以后 Scrapy 会怎么做?POST 还是 POST 吗?跳转几次就放弃?请求头会不会跟着走到别的域名?这篇给每个问题造一个服务器场景,把 RedirectMiddleware 和 MetaRefreshMiddleware 的行为一项项量出来。

21 次
REDIRECT_MAX_TIMES = 20 时无限重定向,服务器收到的请求数(原请求 + 20 次跳转)
POST → GET
301/302/303 跳转后的方法(307/308 保持 POST)
< 100 秒
meta refresh 会被立即跟随的秒数门槛,不会真的等待

方法与请求体

实测 · 本机执行输出
  状态码     原请求     跳转后方法     跳转后请求体        
  301     GET     GET       (空)           
  301     POST    GET       (空)           
  302     GET     GET       (空)           
  302     POST    GET       (空)           
  303     GET     GET       (空)           
  303     POST    GET       (空)           
  307     GET     GET       (空)           
  307     POST    POST      a=1           
  308     GET     GET       (空)           
  308     POST    POST      a=1           

301、302 的 POST 与 303 的非 GET/HEAD 请求会被改成 GET 并去掉请求体和相关头;307、308 保留方法与请求体。

跳转记录

实测 · 本机执行输出
  final_url       = http://127.0.0.1:9960/target
  request_url     = http://127.0.0.1:9960/target#frag
  redirect_urls   = ['http://127.0.0.1:9960/hop1', 'http://127.0.0.1:9960/hop2', 'http://127.0.0.1:9960/hop3#frag']
  redirect_reasons= [302, 301, 307]
  redirect_times  = 3
  redirect_ttl    = 17
  status          = 200
  服务器收到的路径:/hop1 → /hop2 → /hop3 → /target

最终响应的 meta 里有 redirect_urls、redirect_reasons、redirect_times、redirect_ttl;response.url 是最终地址,请求的 URL 可能还带着片段。

次数上限

实测 · 本机执行输出
  REDIRECT_MAX_TIMES=20:服务器收到 21 次请求;errback:IgnoreRequest: max redirections reached
  REDIRECT_MAX_TIMES=3:服务器收到 4 次请求;errback:IgnoreRequest: max redirections reached

REDIRECT_MAX_TIMES(默认 20)用尽后丢弃请求并抛 IgnoreRequest,errback 能收到。

关闭跳转

实测 · 本机执行输出
  {'who': 'errback', 'exc': 'HttpError'}
  {'who': 'callback', 'url': 'http://127.0.0.1:9960/r302?b', 'status': 302, 'location': '/target'}
  服务器收到:['/r302', '/r302?b']

dont_redirect 或 handle_httpstatus_list 让回调直接拿到 3xx 响应。

请求头

实测 · 本机执行输出
  跳到另一个主机名 localhost:目标请求的头 Cookie=(无)、Authorization=(无)、X-Custom=keep、Referer=http://127.0.0.1:9960/xhost
  同主机跳转 /r302:目标请求的头 Cookie=sid=abc、Authorization=Bearer t0k、X-Custom=keep、Referer=http://127.0.0.1:9960/r302
  优先级 +2 的效果(并发 1):服务器收到的顺序 /r302?a /target /plain1 /plain2

跨主机跳转会删除 Cookie 和 Authorization;跳转请求优先级 +2,会插队。

Location 的各种写法

实测 · 本机执行输出
  相对路径 /r302:回调看到 status=200 url=…/target
  协议相对 //localhost:端口/target:回调看到 status=200 url=…/target
  302 没有 Location:回调看到 status=302 url=…/nolocation
  Location 是 ftp://:回调看到 status=302 url=…/ftp

相对路径与协议相对地址会被补全;没有 Location 或目标不是 http(s) 时不跳转。

meta refresh

实测 · 本机执行输出
  content 属性        最终 url          耗时 s  跳转记录
  0; url=/target    target          0.01  ['meta refresh']
  5; url=/target    target          0.01  ['meta refresh']
  99; url=/target   target          0.01  ['meta refresh']
  100; url=/target  meta100         0.01  None
  101; url=/target  meta101         0.01  None

秒数小于 METAREFRESH_MAXDELAY(默认 100)的 meta refresh 会被立即跟随,不会等待那么多秒。

分步演示

重定向
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Scrapy 2.19.0 / Twisted 26.4.0 / lxml 6.1.3 / parsel 1.12.1(临时虚拟环境);所有请求只发给本机自己起的测试服务器(127.0.0.1,端口 9960 以上,不访问任何真实网站);服务器可以按实验需要制造延迟、重定向、压缩、断连等行为,并记录收到的原始请求;每个实验在临时目录里新建 Scrapy 项目或单文件 spider,命令输出来自真实的 scrapy 进程;计时用 time.perf_counter,数字每次运行会有小幅抖动。服务器按路径返回各种 3xx、循环重定向(每次跳到新 URL)、meta refresh 页面,并记录每个请求的方法、请求体和头
    • 没有验证:HTTPS 跳到 HTTP 时 Cookie 与 Authorization 的处理(源码里有 https 例外分支);代理(_scheme_proxy)与重定向的组合;Referer 策略(REFERRER_POLICY)对重定向后 Referer 的影响;HEAD 请求的 meta refresh;重定向与 HTTP 缓存的组合
    京ICP备2026067502号-1京公网安备11010502063487号