API Tutorials

GeeTest Slide 验证码参数及API指南

极验(GeeTest)滑块验证码卡住自动化流程,十有八九是 gtchallenge 传错了——本文按真实识别流程逐个拆解这两个字段,帮你少走弯路。

国内电商、教育和政务类网站的注册、登录页常见极验滑块,reCAPTCHA 则更多出现在面向海外用户的产品里。QA 脚本要同时覆盖国内外站点时,往往得兼容两套参数体系——本文只讲极验(GeeTest v3)。


GeeTest v3 滑块验证码需要哪些参数

参数 是否必填 说明
gt 站点的 GeeTest 账户 ID(32 位十六进制字符串),可在页面源码或接口响应中找到
challenge 特定于本次会话的挑战字符串,每次识别都必须重新获取
pageurl 展示验证码的页面完整 URL
api_server 自定义 GeeTest API 服务器子域,仅部分站点需要

gtchallenge 的关系很好记:gt 是站点身份,长期不变;challenge 是本次会话的一次性凭证,用一次就作废。


第一步:从页面提取 gt 和 challenge

先把两个参数从目标页面拿到手,gt 通常直接写在 HTML 里,challenge 则要靠请求 GeeTest 的注册接口才能拿到:

# extract_geetest_params.py
import requests
import re
import json


def extract_geetest_v3(page_url, session=None):
    """Extract GeeTest v3 gt and challenge from a page."""
    if session is None:
        session = requests.Session()
        session.headers["User-Agent"] = (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36"
        )

    resp = session.get(page_url, timeout=15)
    html = resp.text

    # Method 1: Extract gt from HTML
    gt_match = re.search(r'gt["\']?\s*[:=]\s*["\']([a-f0-9]{32})', html)
    gt = gt_match.group(1) if gt_match else None

    # Method 2: Find API endpoint that returns challenge
    api_match = re.search(r'(https?://[^"\']+register-slide[^"\']*)', html)

    challenge = None
    if api_match:
        api_url = api_match.group(1)
        api_resp = session.get(api_url, timeout=10)
        try:
            data = api_resp.json()
            challenge = data.get("challenge")
            gt = gt or data.get("gt")
        except json.JSONDecodeError:
            pass

    if not challenge:
        # Try embedded challenge
        ch_match = re.search(r'challenge["\']?\s*[:=]\s*["\']([a-f0-9]+)', html)
        challenge = ch_match.group(1) if ch_match else None

    return {"gt": gt, "challenge": challenge, "pageurl": page_url}


# Usage
params = extract_geetest_v3("https://staging.example.com/qa-login")
print(f"gt: {params['gt']}")
print(f"challenge: {params['challenge']}")

如果本地装 requests 比较慢,可以先切换到国内镜像:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests


第二步:把参数提交给 CaptchaAI

拿到 gtchallengepageurl 之后,把三者一起 POST 到 CaptchaAI 的 in.php,再轮询 res.php 拿结果:

# solve_geetest.py
import requests
import time
import os


def solve_geetest(gt, challenge, pageurl, api_server=None):
    """Solve GeeTest v3 slide CAPTCHA via CaptchaAI."""
    api_key = os.environ["CAPTCHAAI_API_KEY"]

    payload = {
        "key": api_key,
        "method": "geetest",
        "gt": gt,
        "challenge": challenge,
        "pageurl": pageurl,
        "json": 1,
    }

    if api_server:
        payload["api_server"] = api_server

    # Submit
    resp = requests.post(
        "https://ocr.captchaai.com/in.php",
        data=payload,
        timeout=30,
    )
    result = resp.json()

    if result.get("status") != 1:
        raise RuntimeError(f"Submit failed: {result.get('request')}")

    task_id = result["request"]

    # Poll — GeeTest typically solves in 10-20 seconds
    time.sleep(10)
    for _ in range(30):
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=15)
        data = resp.json()

        if data.get("status") == 1:
            return data["request"]  # Returns challenge, validate, seccode
        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("GeeTest solve timeout")

极验通常 10—20 秒内出结果,所以脚本先睡 10 秒再开始轮询,避免打空请求。


第三步:把解决方案交回目标页面

CaptchaAI 返回的结果里包含 challengevalidateseccode 三个值,缺一个都会被目标站点拒绝:

# submit_solution.py
import json


def submit_geetest_solution(session, validation_url, solution, original_challenge):
    """Submit GeeTest solution to the target site."""
    # Parse solution if string
    if isinstance(solution, str):
        solution = json.loads(solution)

    payload = {
        "geetest_challenge": solution.get("challenge", original_challenge),
        "geetest_validate": solution.get("validate", ""),
        "geetest_seccode": solution.get("seccode", ""),
    }

    resp = session.post(validation_url, data=payload, timeout=30)
    return resp


# Complete flow
def full_geetest_flow(page_url, validation_url):
    import requests
    from extract_geetest_params import extract_geetest_v3

    session = requests.Session()
    session.headers["User-Agent"] = (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36"
    )

    # Step 1: Extract parameters
    params = extract_geetest_v3(page_url, session)
    print(f"gt: {params['gt']}, challenge: {params['challenge'][:16]}...")

    # Step 2: Solve
    solution = solve_geetest(
        params["gt"], params["challenge"], params["pageurl"],
    )
    print("Solved!")

    # Step 3: Submit
    resp = submit_geetest_solution(
        session, validation_url, solution, params["challenge"],
    )
    print(f"Validation response: {resp.status_code}")
    return resp

三个函数串起来就是完整链路:提取参数 → 提交给 CaptchaAI → 把返回值交回目标站点的验证接口。


challenge 为什么会失效,怎么保持新鲜

challenge 是特定于本次会话的一次性值,过期很快——这也是新手最容易踩的坑:提取时是对的,提交时已经过期了。

# fresh_challenge.py
import time


def get_fresh_challenge(session, register_url):
    """Always fetch a fresh challenge before solving."""
    resp = session.get(register_url, timeout=10)
    data = resp.json()

    challenge = data.get("challenge")
    if not challenge:
        raise ValueError("No challenge returned")

    return challenge


def solve_with_fresh_challenge(session, gt, register_url, pageurl):
    """Ensure challenge is fresh before submitting to CaptchaAI."""
    challenge = get_fresh_challenge(session, register_url)

    # Submit immediately — don't let it expire
    solution = solve_geetest(gt, challenge, pageurl)
    return solution

关键规则: 提取 challenge 后立刻提交给 CaptchaAI,中间不要插入其他耗时逻辑。陈旧的 challenge 提交必定失败,重试也没用,只能重新获取。


遇到自定义 API 服务器怎么办

部分站点没有用极验的默认接口,而是接了自己的 GeeTest 子域,这时需要显式传 api_server

# The api_server parameter specifies a custom GeeTest backend
# Default: api.geetest.com
# Custom examples: api-na.geetest.com, api.geetest.com/ajax-custom

solution = solve_geetest(
    gt="abc123...",
    challenge="def456...",
    pageurl="https://staging.example.com/qa-login",
    api_server="api-na.geetest.com",  # North America endpoint
)

判断要不要传这个参数很简单:打开浏览器开发者工具的网络面板,看请求域名是不是 api.geetest.com。不是的话,把那个子域填进 api_server


常见报错和排查思路

报错/现象 原因 处理方式
ERROR_CAPTCHA_UNSOLVABLE challenge 已过期 提交前立即重新获取一次 challenge
validate 返回为空 API 版本用错 极验 v4 站点要额外传 version=4
站点拒收解决方案 seccode 缺失 确认三个字段(challengevalidateseccode)都提交了
页面源码里找不到 gt gt 是通过 JavaScript 异步加载的 改用 Selenium 渲染页面,或直接抓注册接口的 XHR 响应

常见问题

极验滑块验证码和图片验证码、九宫格验证码有什么区别?

图片验证码、九宫格验证码提交的是坐标或文字识别结果,极验滑块提交的是 gtchallenge 加上 CaptchaAI 返回的 validateseccode。参数结构不同,提交逻辑不能混用。

gt 和 challenge 是同一个参数吗?

不是。gt 是站点的 GeeTest 账户 ID,基本固定不变;challenge 是每次会话单独生成的一次性值,必须每次重新提取。

challenge 的有效期大概是多久?

通常在 60—120 秒之间,具体因站点配置而异。稳妥的做法是提取后立刻提交,不要缓存复用。

GeeTest v4 现在能用 CaptchaAI 识别吗?

目前不支持,官方标注为「即将支持」。本文涉及的 gt/challenge 参数体系针对的是 GeeTest v3;v4 的握手机制不同,参数不能照搬。

为什么有的页面在开发者工具里也找不到 gt 参数?

部分站点把 gt 放进了打包后的 JavaScript 变量或异步接口响应里,HTML 源码里看不到。这种情况改用 Selenium 渲染完整页面再提取,或直接监听网络请求,找返回 gt/challenge 的那个接口。


相关指南


极验滑块的参数搞懂了,识别流程就不难——从 CaptchaAI 开始

该文章已禁用评论。