极验(GeeTest)滑块验证码卡住自动化流程,十有八九是 gt 或 challenge 传错了——本文按真实识别流程逐个拆解这两个字段,帮你少走弯路。
国内电商、教育和政务类网站的注册、登录页常见极验滑块,reCAPTCHA 则更多出现在面向海外用户的产品里。QA 脚本要同时覆盖国内外站点时,往往得兼容两套参数体系——本文只讲极验(GeeTest v3)。
GeeTest v3 滑块验证码需要哪些参数
| 参数 | 是否必填 | 说明 |
|---|---|---|
gt |
是 | 站点的 GeeTest 账户 ID(32 位十六进制字符串),可在页面源码或接口响应中找到 |
challenge |
是 | 特定于本次会话的挑战字符串,每次识别都必须重新获取 |
pageurl |
是 | 展示验证码的页面完整 URL |
api_server |
否 | 自定义 GeeTest API 服务器子域,仅部分站点需要 |
gt 和 challenge 的关系很好记: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
拿到 gt、challenge、pageurl 之后,把三者一起 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 返回的结果里包含 challenge、validate、seccode 三个值,缺一个都会被目标站点拒绝:
# 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 缺失 |
确认三个字段(challenge、validate、seccode)都提交了 |
页面源码里找不到 gt |
gt 是通过 JavaScript 异步加载的 |
改用 Selenium 渲染页面,或直接抓注册接口的 XHR 响应 |
常见问题
极验滑块验证码和图片验证码、九宫格验证码有什么区别?
图片验证码、九宫格验证码提交的是坐标或文字识别结果,极验滑块提交的是 gt、challenge 加上 CaptchaAI 返回的 validate、seccode。参数结构不同,提交逻辑不能混用。
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 开始。