Explainers

GeeTest v3 挑战-响应工作流程:技术深入探讨

GeeTest(极验)在国内登录、报名、下单页面几乎无处不在,但 GeeTest v3 不是 reCAPTCHA 那种单 token 模式——它靠注册、渲染、三值校验三步走,抓错阶段就拿不到正确参数。下文按顺序拆解全流程,并给出提取 gtchallenge 的三种方法。

GeeTest v3 的核心机制:先注册,再验证

阶段一:服务端注册,拿到 gt 和 challenge

页面加载时,站点后端先向 GeeTest 服务器发起注册请求:

Site Backend → GeeTest Server: "Give me a challenge for this user"
GeeTest Server → Site Backend: { gt, challenge, new_captcha }
Site Backend → Browser: Passes gt and challenge to the page

阶段二:客户端渲染,提交三值校验

浏览器渲染挑战,用户完成后,后端拿三个值去 GeeTest 服务器做最终验证:

Browser: Renders slider/puzzle using gt + challenge
User: Solves the challenge
Browser → Site Backend: { geetest_challenge, geetest_validate, geetest_seccode }
Site Backend → GeeTest Server: Verifies the three values
GeeTest Server → Site Backend: { result: "success" }

阶段三:GeeTest 服务器不可达时的降级校验

生产环境偶尔会遇到 GeeTest 服务器不可达,这时 v3 会自动降级:

  • 在线模式success1):走 GeeTest 服务器完整校验,绝大多数生产站点的实际状态。
  • 离线模式success0):本地生成挑战,校验更简单,强度更低。

抓包时若看到 success0,大概率是 GeeTest 服务端的问题,不必排查自己代码。

完整调用流程:从注册接口到三值校验

第一步:调用注册接口

站点后端请求 GeeTest 的注册端点,直接拿到三个字段:

GET https://api.geetest.com/register.php?gt=GT_ID&json_format=1
{
  "success": 1,
  "gt": "81dc9bdb52d04dc20036dbd8313ed055",
  "challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "new_captcha": true
}
  • gt:标识站点绑定的 GeeTest 账户。
  • challenge:本次会话专属的挑战令牌。
  • new_captcha:是否启用新版验证码格式。

每次页面加载都会生成新的 challenge,不要缓存复用上一次的值。

第二步:渲染验证挑战

浏览器拿到 gtchallenge 后初始化 GeeTest 小部件:

initGeetest({
  gt: "81dc9bdb52d04dc20036dbd8313ed055",
  challenge: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  offline: false,
  new_captcha: true,
  product: "float"
}, function(captchaObj) {
  captchaObj.appendTo('#captcha-container');
  captchaObj.onSuccess(function() {
    var result = captchaObj.getValidate();
    // result contains: geetest_challenge, geetest_validate, geetest_seccode
  });
});

第三步:常见的挑战类型

GeeTest v3 按站点配置和用户风险画像,动态选择以下挑战形式:

  • 滑块:拖动拼图块,补全图像。
  • 图标点击:按提示顺序点击指定图标。
  • 文字点击:按顺序点击图中的汉字。
  • 空间推理:点击/选择,空间判断类挑战。

具体出哪种由 GeeTest 后台决定,前端无法指定。

第四步:拿到三个校验值

用户完成操作后,小部件会生成三个值:

{
  "geetest_challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy",
  "geetest_validate": "abc123def456_validate",
  "geetest_seccode": "abc123def456_validate|jordan"
}
  • geetest_challenge:challenge 加 2 个字符后的新令牌。
  • geetest_validate:校验哈希。
  • geetest_seccode:安全码,即 validate 加 \|jordan 后缀。

第五步:服务端二次校验

站点后端把三个值发给 GeeTest 做最终确认,拿到最终结果:

POST https://api.geetest.com/validate.php

seccode=abc123def456_validate|jordan
&challenge=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy
&sdk=geetest-python-3.0.0
{
  "seccode": "abc123def456_validate",
  "validate": "abc123def456_validate"
}

怎么把 gt 和 challenge 交给 CaptchaAI

GeeTest v3 只需要 gtchallenge 两个值即可求解,抓取方式有三种:

方法一:拦截注册请求的响应

直接监听浏览器发出的注册请求,读取 GeeTest 服务器返回的原始数据:

from playwright.sync_api import sync_playwright
import json

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    geetest_params = {}

    def handle_response(response):
        if "register" in response.url and "geetest" in response.url:
            data = response.json()
            geetest_params["gt"] = data.get("gt")
            geetest_params["challenge"] = data.get("challenge")

    page.on("response", handle_response)
    page.goto("https://staging.example.com/qa-login")

    # Wait for GeeTest to load
    page.wait_for_selector(".geetest_holder")
    print(f"gt: {geetest_params.get('gt')}")
    print(f"challenge: {geetest_params.get('challenge')}")

方法二:直接读取页面 DOM 属性

如果参数写进了 data 属性,可直接用选择器取值:

gt = page.evaluate("() => document.querySelector('[data-gt]')?.dataset.gt")
challenge = page.evaluate("() => document.querySelector('[data-challenge]')?.dataset.challenge")

方法三:正则匹配 initGeetest 调用

部分站点把参数硬编码在内联脚本里,搜索源码里的 initGeetest 调用即可:

import re
source = page.content()
gt_match = re.search(r"gt['\"]?\s*[:=]\s*['\"]([a-f0-9]{32})['\"]", source)
challenge_match = re.search(r"challenge['\"]?\s*[:=]\s*['\"]([a-f0-9]{32})['\"]", source)

提交给 CaptchaAI,拿回三值结果

参数到手后提交给 CaptchaAI 的识别接口,拿到任务 ID 后轮询结果即可:

POST https://ocr.captchaai.com/in.php

key=YOUR_API_KEY
&method=geetest
&gt=81dc9bdb52d04dc20036dbd8313ed055
&challenge=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
&pageurl=https://staging.example.com/qa-login
&json=1
GET https://ocr.captchaai.com/res.php?key=YOUR_API_KEY&action=get&id=TASK_ID&json=1
{
  "status": 1,
  "request": {
    "geetest_challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy",
    "geetest_validate": "abc123def456_validate",
    "geetest_seccode": "abc123def456_validate|jordan"
  }
}

按第五步格式提交给站点后端即可。

故障排查:这几类问题最常见

  • challenge 值为空——注册请求失败,检查页面是否正常加载 GeeTest 小部件。
  • 提交后被拒绝——challenge 已过期,重新抓取新的 challenge 并立刻提交。
  • gt 值不对——页面上有多个 GeeTest 实例,确认取自目标那个小部件的 gt
  • 三个值都拿到了但表单没提交出去——表单字段没对上,把三个值注入对应的输入框。

常见问题

GeeTest v3 和极验 v4 是一回事吗?CaptchaAI 支持 v4 吗?

不是,两者协议和参数格式不同。CaptchaAI 只支持 GeeTest v3,v4 即将支持,暂不能识别。

为什么每次请求都要同时带 gt 和 challenge,只传一个不行吗?

gt 是站点账号标识,长期不变;challenge 是防重放的会话一次性令牌,缺一个都无法完成校验。

GeeTest 弹出的是滑块还是点击图标,能提前判断吗?

不能。挑战类型由 GeeTest 按站点配置和用户风险画像动态决定,前端只能拿到渲染好的结果,抓取逻辑需覆盖多种类型。

challenge 提交失败或过期了,能直接重试同一个值吗?

不能。challenge 通常 1-2 分钟内过期且只能校验一次,需重新调用注册接口拿新值。

相关文章

下一步

把 GeeTest v3 挑战接入你的识别流程:获取 CaptchaAI API Key,对接三值响应即可。

该文章已禁用评论。