requests.Session():对验证码的POST请求返回403,下一次请求仍然返回验证码页面

移动开发 2026-07-11

我在调试一个与requests.Session()相关的特定HTTP/会话问题。

验证码答案由人工输入,我的代码提交该值。我并非在尝试自动化captcha解决。

Environment:

  • Python 3.11.9
  • requests 2.32.5
  • beautifulsoup4 4.14.3
  • 一个 requests.Session() 在验证码页面GET与验证码提交POST之间复用

Instrumented failing flow from the same runtime profile/session:

GET #1:
200 https://hh.ru/account/captcha?state=
<redacted>&captchaState=<redacted>
redirects: []

captcha form:
method=POST
action=https://hh.ru/account/captcha?state=
<redacted>&captchaState=<redacted>
payload keys=['_xsrf', 'captchaText', 'isBot']

cookies before submit (keys):
['_ddg10', '_ddg1', '_ddg8', '_ddg9', '_xsrf', 'crypted_hhuid', 'hhrole', 'hhtoken', 'hhuid', 'regions', 'session_hhuid']

POST submit attempt:
status=403
url=https://hh.ru/account/captcha?state=
<redacted>&captchaState=<redacted>
redirects=[]
headers: Location='', Set-Cookie present, Content-Type='application/json'
body prefix: {"hhcaptcha":{"isBot":true,...

GET #2 in same Session:
status=200
url=https://hh.ru/account/captcha?state=
<redacted>&_xsrf=<redacted>&captchaText=<redacted>&isBot=false
redirects=[]
headers: Location='', Set-Cookie present, Content-Type='text/html; charset=utf-8'
body prefix: <!DOCTYPE html><html class="light-page-root">...

Additional request/response details captured from PreparedRequest.headers:

POST submit request:
method: POST
url: https://hh.ru/account/captcha?state=
<redacted>
User-Agent: ru.hh.android/7.121.11146, Device: 23053RN02Y, Android OS: 11 (...)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: https://hh.ru/account/captcha?state=
<redacted>
Origin: https://hh.ru

Content-Type: application/x-www-form-urlencoded
Cookie names: _ddg8, _ddg10, _ddg9, _ddg1, crypted_hhuid, session_hhuid, _xsrf, hhtoken, hhuid, hhrole, regions

Preceding GET request:
method: GET
url: https://hh.ru/account/captcha?state=
<redacted>
User-Agent: ru.hh.android/7.121.11146, Device: 23053RN02Y, Android OS: 11 (...)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Referer: https://hh.ru/

Cookie names: _ddg8, _ddg10, _ddg9, _ddg1

POST response:
status: 403
Content-Type: application/json
Server: ddos-guard
redirects: []
body prefix: {"hhcaptcha":{"isBot":true,...

Fallback GET response:
status: 200
Content-Type: text/html; charset=utf-8
Server: ddos-guard
redirects: []
body prefix: <!DOCTYPE html><html class="light-page-root">...

Possible causes I am considering:

  • 解析后的表单提交方式为POST,但回退时会重试GET
  • POST提交可能在验证码状态记入会话前被ddos-guard拒绝
  • 浏览器与requests的请求头可能不匹配

验证码答案由人工输入。验证码文本本身是正确的(也是手动输入)。

Minimal reproduction shape:

session = requests.Session()

r1 = session.get(captcha_url, headers=get_headers, allow_redirects=True, timeout=20)

payload = {
"_xsrf": "...",
"captchaText": user_input,
"isBot": "false",
}

s = session.post(form_action, data=payload, headers=post_headers, allow_redirects=True, timeout=20)

current fallback path then retries with GET and sends the same payload as query params

r2 = session.get(form_action, params=payload, headers=get_headers, allow_redirects=True, timeout=20)

Expected: - 验证码提交成功后,同一个Session的下一次请求不应再被解析为验证码页面。

Actual: - POST尝试返回来自ddos-guard的 403 - 重试的GET仍然显示为验证码页面。

在这种情况下,你会首先检查什么:

  1. 浏览器与requests之间的请求头差异
  2. POST表单之后的错误GET回退
  3. 验证码GET与 POST之间的会话/ cookie状态不匹配

Or is there some other common mistake when submitting such forms with requests.Session()?

解决方案

requests.Session() 可能不是这里的问题。一个 Session 确实能在请求之间持久化cookies,而逐请求的参数覆盖会话中的值但不会回写到会话中,因此在同一会话中先GET再在同一会话中POST的基本流程是有效的。

首先要检查的是POST本身,而不是后续的GET。403Server: ddos-guard、和 {"hhcaptcha":{"isBot":true... 的响应意味着提交在正常应用流程接受验证码之前就被拒绝。在那种情况下,验证码状态永远不会记入会话,因此下一次请求仍会落到验证码页面。

另外,你的回退策略是错的:如果表单显示 method=POST,用相同的载荷再用 GET 重试并不等同。应完全移除那条路径。

我先会检查以下几项:

  • 不要自己设置 Cookie 头。让 requests.Session() 来管理cookies。这就是Session jar的作用,手动重放cookies很容易让你们不同步。
  • 确保 _xsrf 以及其他隐藏输入字段来自当前验证码页面。如果页面重新生成它们而你提交了旧值,即使验证码文本正确,POST也可能失败。
  • 尽量让你的POST与浏览器提交尽可能接近:

  • 相同的隐藏字段

  • 相同的表单编码
  • 浏览器是直接提交表单还是通过JS/XHR提交
  • 在加载页面到提交表单之间是否出现额外的Cookies

  • 你的 Accept 头看起来有些可疑:

text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

那最后一段可能该是:

*/*;q=0.8

这可能不是主要问题,但值得修正。 - 如果你在任何地方手动准备请求,请使用 Session.prepare_request(),而不是 Request.prepare()。否则会话状态(尤其是Cookies)可能不会按你预期的方式生效。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章