先说结论:Smartproxy 决定请求从哪个出口 IP 发出,CaptchaAI 负责把 reCAPTCHA v2 或 Cloudflare Turnstile 换成可提交的 token。配错基本都出在把两件事混在一起。守住一条即可:加载页面和提交 token 走同一出口 IP。
安全范围: 本指南仅适用于你自有或经授权的 QA、staging 与预发布环境。内容覆盖针对你自己 CAPTCHA 集成的诊断、测试与可观测性模式 — 不涉及第三方站点或未授权流程。
先理清数据流:哪一步走网关
- 取页面:请求经网关发出,从 HTML 拿到
sitekey。 - 拿 token:
sitekey与pageurl提交给in.php,再轮询res.php,这一步不走网关。 - 回传表单:带 token 提交回页面,出口 IP 与第 1 步一致。
第 2 步最容易配错:识别请求挂上网关只是多花流量,成功率不变——在意 IP 一致性的是目标站点。
Python 配置:取页面与验证码识别
依赖只有 requests,国内装包走镜像更稳:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests。下面这段把请求挂到网关,提交后每 5 秒轮询一次,最多 60 次。
import requests
import time
SMARTPROXY_USER = "spuser"
SMARTPROXY_PASS = "sppassword"
SMARTPROXY_HOST = "gate.smartproxy.com"
SMARTPROXY_PORT = 10001
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
proxies = {
"http": f"http://{SMARTPROXY_USER}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}",
"https": f"http://{SMARTPROXY_USER}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}",
}
def fetch_page(url):
return requests.get(url, proxies=proxies, timeout=30)
def solve_captcha(site_url, sitekey, captcha_type="recaptcha_v2"):
submit_data = {
"key": CAPTCHAAI_KEY,
"pageurl": site_url,
"json": 1,
}
if captcha_type == "turnstile":
submit_data["method"] = "turnstile"
submit_data["sitekey"] = sitekey
else:
submit_data["method"] = "userrecaptcha"
submit_data["googlekey"] = sitekey
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=submit_data)
data = resp.json()
if data["status"] != 1:
raise Exception(f"Submit failed: {data['request']}")
task_id = data["request"]
for _ in range(60):
time.sleep(5)
resp = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY,
"action": "get",
"id": task_id,
"json": 1,
})
data = resp.json()
if data["request"] == "CAPCHA_NOT_READY":
continue
if data["status"] == 1:
return data["request"]
raise Exception(f"Solve: {data['request']}")
raise TimeoutError("Timeout")
两个坑:googlekey 只用于 reCAPTCHA v2,Turnstile 用 sitekey;CAPCHA_NOT_READY 少一个 T 是历史写法,不是笔误。
粘性会话:一次流程锁定同一个 IP
中途换 IP,站点通常直接判定 token 无效。Smartproxy 把会话写进用户名:-session-<id> 指定会话,-sessionduration-<分钟> 定时长。
import random
import string
def get_sticky_proxy(session_duration_minutes=10):
"""Create a sticky session proxy (same IP for duration)."""
session_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
proxy_url = (
f"http://{SMARTPROXY_USER}"
f"-session-{session_id}"
f"-sessionduration-{session_duration_minutes}"
f":{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
)
return {"http": proxy_url, "https": proxy_url}
# Use same IP for entire CAPTCHA workflow
sticky = get_sticky_proxy(session_duration_minutes=10)
# Page load
resp = requests.get("https://staging.example.com/qa-form", proxies=sticky)
# Solve CAPTCHA
token = solve_captcha("https://staging.example.com/qa-form", "SITEKEY_HERE")
# Submit with same IP
resp = requests.post(
"https://target.com/submit",
data={"g-recaptcha-response": token},
proxies=sticky,
)
10 分钟够覆盖“加载 → 识别 → 提交”,多步表单调到 30 分钟;会话 ID 按任务生成。
按国家/地区选择出口
同一页面在不同出口下常常不是同一份 HTML:货币、语种、是否弹验证码都会变。比如面向东南亚做价格监控 QA,要在新加坡和马来西亚出口下各验证一遍货币与 sitekey,把 country 设成 sg 与 my 各跑一遍。
# Smartproxy country targeting via username
def get_country_proxy(country_code):
proxy_url = (
f"http://{SMARTPROXY_USER}"
f"-country-{country_code}"
f":{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
)
return {"http": proxy_url, "https": proxy_url}
# US proxy
us_proxy = get_country_proxy("us")
# UK proxy
uk_proxy = get_country_proxy("gb")
# Germany proxy
de_proxy = get_country_proxy("de")
另外,reCAPTCHA 加载的是 Google 托管脚本,内地网络访问不稳定,调试时先换出口再怀疑代码。
Selenium 接入
页面要跑 JavaScript 才渲染验证码时,浏览器也走同一出口。
from selenium import webdriver
from selenium.webdriver.common.by import By
def create_smartproxy_driver(country=None, sticky_session=None):
proxy_user = SMARTPROXY_USER
if country:
proxy_user += f"-country-{country}"
if sticky_session:
proxy_user += f"-session-{sticky_session}"
proxy_url = f"{proxy_user}:{SMARTPROXY_PASS}@{SMARTPROXY_HOST}:{SMARTPROXY_PORT}"
options = webdriver.ChromeOptions()
options.add_argument(f"--proxy-server=http://{SMARTPROXY_HOST}:{SMARTPROXY_PORT}")
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1920,1080")
# For authenticated proxies, use seleniumwire or extension
return webdriver.Chrome(options=options)
def scrape_with_captcha(url, country="us"):
session_id = "".join(random.choices(string.ascii_lowercase, k=8))
driver = create_smartproxy_driver(country=country, sticky_session=session_id)
try:
driver.get(url)
time.sleep(3)
sitekey = driver.execute_script(
"return document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')"
)
if sitekey:
token = solve_captcha(url, sitekey)
driver.execute_script(f"""
document.querySelector('#g-recaptcha-response').value = '{token}';
""")
driver.find_element(By.CSS_SELECTOR, "form").submit()
time.sleep(3)
return driver.page_source
finally:
driver.quit()
Chrome 的 --proxy-server 不接受用户名密码:带认证的网关用 Selenium Wire 配置,或把凭据打包成浏览器扩展。
Node.js 接入
写法对应:用 https-proxy-agent 把出口挂到 axios,识别请求直连,不带 agent。
const axios = require("axios");
const HttpsProxyAgent = require("https-proxy-agent");
const CAPTCHAAI_KEY = "YOUR_API_KEY";
function getSmartproxyAgent(options = {}) {
let user = "spuser";
if (options.country) user += `-country-${options.country}`;
if (options.session) user += `-session-${options.session}`;
return new HttpsProxyAgent(
`http://${user}:[email protected]:10001`
);
}
async function scrapeWithCaptcha(url, sitekey) {
const agent = getSmartproxyAgent({
country: "us",
session: `sess-${Date.now()}`,
});
// Fetch page through proxy
const pageResp = await axios.get(url, { httpsAgent: agent });
// Solve CAPTCHA via CaptchaAI (no proxy needed)
const submitResp = await axios.post(
"https://ocr.captchaai.com/in.php",
null,
{
params: {
key: CAPTCHAAI_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: url,
json: 1,
},
}
);
const taskId = submitResp.data.request;
// Poll for result
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: {
key: CAPTCHAAI_KEY,
action: "get",
id: taskId,
json: 1,
},
});
if (result.data.request === "CAPCHA_NOT_READY") continue;
if (result.data.status === 1) return result.data.request;
}
throw new Error("Timeout");
}
批量并发:max_workers 该设多少
并发上限由 CaptchaAI 的线程数决定,不是网关。它按并发线程计费、套餐内识别次数不限:BASIC($15/月,5 线程)、ADVANCE($90/月,50 线程),最高到 VIP-3($7,500/月,5,000 线程)。max_workers 别超过线程数,示例里的 5 正好对应 BASIC。
from concurrent.futures import ThreadPoolExecutor, as_completed
def process_url(url):
session_id = "".join(random.choices(string.ascii_lowercase, k=8))
proxy = get_sticky_proxy(10)
try:
resp = requests.get(url, proxies=proxy, timeout=30)
# Check if CAPTCHA is present (simplified detection)
if "data-sitekey" in resp.text:
import re
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if match:
sitekey = match.group(1)
token = solve_captcha(url, sitekey)
return {"url": url, "status": "solved", "token": token[:30]}
return {"url": url, "status": "no_captcha"}
except Exception as e:
return {"url": url, "status": "error", "error": str(e)}
urls = [
"https://site1.com/page",
"https://site2.com/page",
"https://site3.com/page",
]
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(process_url, u): u for u in urls}
for future in as_completed(futures):
result = future.result()
print(f"[{result['status']}] {result['url']}")
Smartproxy 出口类型怎么选
挑选只看两点:验证码出现频率,会话能否保持。
| 出口类型 | IP 规模 | 适用场景 | 验证码出现频率 |
|---|---|---|---|
| 家庭宽带出口 | 55M+ IP | 常规采集 | 低 |
| 数据中心出口 | 100K+ IP | 高速批量 | 中到高 |
| 移动网络出口 | 10M+ IP | 移动端页面 | 很低 |
| ISP 静态出口 | 静态 IP | 长会话流程 | 低 |
规模数字取自 Smartproxy 官方资料,实际表现请自行实测。
排错清单
| 现象 | 原因 | 处理方式 |
|---|---|---|
| 407 认证失败 | 用户名密码格式写错 | 到控制台核对凭据 |
| 中途 IP 变了 | 没启用粘性会话 | 用户名加 -session-ID |
| 每个请求都弹验证码 | 用了数据中心出口 | 换家庭宽带 |
| 连接变慢 | 该地区出口拥挤 | 换国家或城市 |
| token 被目标站拒绝 | 两步之间换了 IP | 延长会话时长 |
常见问题
可以把 Smartproxy 的出口 IP 传给 CaptchaAI 吗?
可以,提交接口支持 proxy 参数。多数场景用不上,只有站点把识别与页面 IP 绑定校验时才需要。
CaptchaAI 支持哪些验证码类型?
reCAPTCHA v2/v3(含 Enterprise 与隐形 reCAPTCHA)、Turnstile 与 Cloudflare Challenge、GeeTest v3、图片/OCR 与九宫格、BLS;CaptchaFox、Friendly Captcha、Lemin 为测试版。hCaptcha 与 FunCaptcha 暂不支持,GeeTest v4 即将支持。
国内常见的极验滑块适用这套流程吗?
部分适用。国内页面多用 GeeTest(极验)、网易易盾、腾讯防水墙,CaptchaAI 只覆盖 GeeTest v3,参数为 gt 和 challenge。
采集数据时的合规底线是什么?
只采集你有权访问的数据,对齐《网络安全法》《数据安全法》《个人信息保护法》与 robots 协议。
相关阅读
网关负责出口,CaptchaAI 负责 token——领取 API Key。