重定向看起来是客户端库替你处理的小事,但它决定了 POST 请求体会不会丢、一个死循环会让服务器多挨多少下、你的令牌会不会被带到别的主机。这篇在本机用 301、302、303、307、308 逐个试。
POST /r301 → 目标 /final:方法 GET,请求体 0 字节,Content-Type=None,Content-Length=None;服务器共收到 2 个请求;history=[301] POST /r302 → 目标 /final:方法 GET,请求体 0 字节,Content-Type=None,Content-Length=None;服务器共收到 2 个请求;history=[302] POST /r303 → 目标 /final:方法 GET,请求体 0 字节,Content-Type=None,Content-Length=None;服务器共收到 2 个请求;history=[303] POST /r307 → 目标 /final:方法 POST,请求体 7 字节,Content-Type=None,Content-Length=7;服务器共收到 2 个请求;history=[307] POST /r308 → 目标 /final:方法 POST,请求体 7 字节,Content-Type=None,Content-Length=7;服务器共收到 2 个请求;history=[308]
301、302、303 之后 requests 把 POST 改成 GET 并丢掉请求体;307、308 保持方法和请求体。服务器想让客户端重发表单,要用 307/308,否则 POST 的数据会悄悄消失,目标地址只会收到一个没有 Content-Length 的 GET。
allow_redirects=False:状态 302,Location=/final,is_redirect=True,r.next.url=http://127.0.0.1:9961/final /chain/3 逐跳:[(302, '/chain/3'), (302, '/chain/2'), (302, '/chain/1')] → 最终 200 /chain/0 /rel 回 301 Location: sub/page(相对路径)→ 最终 URL /sub/page,状态 200
allow_redirects=False 可以拿到原始的 302 响应和 Location(r.next 是拼好的下一个请求)。默认跟随时,r.history 按顺序保存每一跳的响应;相对路径的 Location 会被解析成完整 URL。爬虫要记录“最终 URL”时看 r.url,要记录来路看 r.history。
Session.max_redirects 默认值 = 30 /loop(一直 302 到自己):TooManyRedirects: Exceeded 30 redirects.;服务器收到 31 个请求 max_redirects=5:TooManyRedirects;服务器收到 6 个请求 链长 29 跳:成功,history 29 跳;服务器收到 30 个请求 链长 30 跳:成功,history 30 跳;服务器收到 31 个请求 链长 31 跳:TooManyRedirects;服务器收到 31 个请求
max_redirects 默认 30。服务器在客户端放弃前会收到 31 个请求(第一次加 30 次跳转);链长 30 跳的重定向还能成功,31 跳抛 TooManyRedirects。爬虫遇到重定向环时,上限就是它给目标服务器多添的请求数,可以调小。
headers= 手写 Authorization、X-Token、Cookie:A 收到的头 ['Host', 'User-Agent', 'Accept-Encoding', 'Accept', 'Connection', 'Authorization', 'X-Token', 'Cookie']
headers= 手写 Authorization、X-Token、Cookie:B 收到的头 ['Host', 'User-Agent', 'Accept-Encoding', 'Accept', 'Connection', 'X-Token']
auth=(user, pass):A 收到的头 ['Host', 'User-Agent', 'Accept-Encoding', 'Accept', 'Connection', 'Authorization']
auth=(user, pass):B 收到的头 ['Host', 'User-Agent', 'Accept-Encoding', 'Accept', 'Connection']
requests 的 should_strip_auth(决定跳转时要不要去掉 Authorization)对几组 URL 的判断:
http://example.test/a → http://example.test/b:去掉 Authorization = False
http://example.test:8000/a → http://example.test:9000/a:去掉 Authorization = True
http://example.test/a → https://example.test/a:去掉 Authorization = False
http://example.test:8080/a → https://example.test:8443/a:去掉 Authorization = True
https://example.test/a → http://example.test/a:去掉 Authorization = True
http://example.test/a → http://other.test/a:去掉 Authorization = True跳到另一个主机(这里是不同端口)时,requests 会去掉 Authorization 和手写的 Cookie 头,但自定义的 X-Token 之类的头原样转发。如果你的令牌放在自定义头里,跳转到第三方主机就会一起带过去——这是实测结果,需要自己在跳转前处理(例如 allow_redirects=False 手动判断)。