先说结论:IP 换得越勤不一定越好。 从页面加载到 token 提交必须是同一个出口 IP,否则 token 多半被判无效——排查半天的那个 403,根源常在这里。真正要调的只有两件事:何时换 IP,以及换的粒度。
先选策略:三种轮换粒度怎么挑
判断只有一条:流程里是否存在"上一页拿到的东西要在下一页用"? 有就会话保持,没有按请求轮换;多站则按域固定。
1. 按请求轮换(默认行为)
import requests
proxies = {
"http": "http://user:[email protected]:7777",
"https": "http://user:[email protected]:7777",
}
# Each request gets a new IP
for url in urls:
resp = requests.get(url, proxies=proxies, timeout=30)
# New IP after each request
适合: 跨站点抓大量页面。风险: 无法维持会话。
2. 会话保持轮换
import random
import string
def get_sticky_session(duration_min=10):
session_id = "".join(random.choices(string.ascii_lowercase, k=8))
return {
"http": f"http://user-session-{session_id}:[email protected]:7777",
"https": f"http://user-session-{session_id}:[email protected]:7777",
}
# Same IP for the whole CAPTCHA workflow
session_proxy = get_sticky_session(duration_min=10)
# Step 1: Load page
resp = requests.get("https://staging.example.com/qa-form", proxies=session_proxy)
# Step 2: Solve CAPTCHA (same IP)
token = solve_captcha("https://staging.example.com/qa-form", sitekey)
# Step 3: Submit (same IP — required for token validity)
resp = requests.post("https://target.com/submit", proxies=session_proxy, data={
"g-recaptcha-response": token,
})
适合: 带验证码的表单和多步流程。风险: 很低。
注意第 2、3 步:识别期间不能换 IP。服务端会比对签发与提交的来源,中途换出口等于作废。
3. 按域固定
domain_sessions = {}
def get_domain_proxy(domain):
"""Same IP per domain, different IP per domain."""
if domain not in domain_sessions:
session_id = "".join(random.choices(string.ascii_lowercase, k=8))
domain_sessions[domain] = get_sticky_session()
return domain_sessions[domain]
# site-a.com always uses the same IP
proxy_a = get_domain_proxy("site-a.com")
# site-b.com gets a different IP
proxy_b = get_domain_proxy("site-b.com")
IP 来源为什么影响验证码频率
| 出口类型 | IP 归属 | 验证码出现率 |
|---|---|---|
| 数据中心 | 云服务商 / 主机商 | 高(30–70%) |
| 家庭宽带 | 真实家庭 ISP | 低(5–15%) |
| 蜂窝网络 | 移动运营商 | 很低(1–5%) |
以上区间基于公开资料与内部观测样本,仅供参考,请在自有环境测量。
原因在于 reCAPTCHA 会查 IP 信誉库:真实 ISP 地址有正常历史流量,信任度更高;机房段被大量自动化用过,命中率高。
降低验证码频率的五个做法
1. 出口地区和目标站点对得上
# Target site is US-based? Use US residential IP
us_proxy = "http://user-country-us:[email protected]:7777"
# Target is Germany? Use DE residential IP
de_proxy = "http://user-country-de:[email protected]:7777"
地区错配最容易被忽略:用海外出口访问只服务本地用户的站点,评分会直接掉。
2. 请求节奏要像人
import time
import random
def human_pace_requests(urls, proxy):
results = []
for url in urls:
resp = requests.get(url, proxies=proxy, timeout=30)
results.append(resp)
# Random delay between requests
delay = random.uniform(2, 8)
time.sleep(delay)
return results
3. 请求头要完整
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"DNT": "1",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
}
resp = requests.get(url, proxies=proxy, headers=headers)
Accept-Language 要和出口地区一致——北美出口却发 zh-CN 优先很显眼。
4. 处理封禁要有退避
def fetch_with_retry(url, max_retries=3):
for attempt in range(max_retries):
# New proxy for each retry
proxy = get_sticky_session()
try:
resp = requests.get(url, proxies=proxy, timeout=30)
if resp.status_code == 403:
print(f"IP banned, rotating... (attempt {attempt + 1})")
time.sleep(5)
continue
if resp.status_code == 429:
print(f"Rate limited, backing off...")
time.sleep(30)
continue
return resp
except requests.exceptions.ProxyError:
print(f"Proxy failed, rotating...")
continue
raise Exception(f"Failed after {max_retries} retries")
403 换 IP、429 退避:429 是限流信号,换 IP 重来只会让整个池子被限。
5. 新 IP 先预热
def warm_up_proxy(proxy):
"""Visit neutral sites before target to build session trust."""
warm_urls = [
"https://www.google.com",
"https://www.wikipedia.org",
]
for url in warm_urls:
try:
requests.get(url, proxies=proxy, timeout=15)
time.sleep(random.uniform(2, 5))
except Exception:
pass
和 CaptchaAI 接口衔接
IP 策略降低的是验证码出现次数,识别是另一回事。链路:抓页面 → 提取 sitekey → 提交任务 → 轮询 res.php → 同一会话提交 token。
import requests
import time
import re
import random
import string
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
def solve_recaptcha(site_url, sitekey):
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
"key": CAPTCHAAI_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": site_url,
"json": 1,
})
task_id = resp.json()["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":
return data["request"]
raise TimeoutError("Timeout")
def scrape_url(url, proxy_config):
"""Complete scrape with proxy + CAPTCHA solving."""
session_id = "".join(random.choices(string.ascii_lowercase, k=8))
proxy = {
"http": f"http://user-session-{session_id}:{proxy_config['pass']}@{proxy_config['host']}",
"https": f"http://user-session-{session_id}:{proxy_config['pass']}@{proxy_config['host']}",
}
resp = requests.get(url, proxies=proxy, timeout=30)
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if match:
token = solve_recaptcha(url, match.group(1))
resp = requests.post(
url.replace("/form", "/submit"),
proxies=proxy,
data={"g-recaptcha-response": token},
)
return resp.text
CaptchaAI 按并发线程计价而非按次:BASIC($15/月,5 线程)、STANDARD($30/月,15 线程)、ADVANCE($90/月,50 线程),每线程识别次数不限,所以要估的是并发峰值。
一个实际的取舍场景
跨境比价采集是个典型例子:国内源多为 极验(GeeTest)滑块或图片验证码,海外源多为 reCAPTCHA 与 Cloudflare Turnstile。CaptchaAI 支持 GeeTest v3 与图片 / 九宫格,v4 只能说"即将支持"。只有登录态和表单页需要会话保持。另外 reCAPTCHA 依赖 Google 域名下的脚本,国内加载不稳定,调试时容易误判成"识别失败";采集范围请对照 网络安全法 与 robots 协议。
成本与排障速查
成本上最有效的三件事:只对验证码密集的站点用高信誉出口(可省 50–70%)、先抓完无验证码页面、淘汰差池子。
| 现象 | 原因 | 处理方式 |
|---|---|---|
| 每页都弹验证码 | 出口池质量差 | 换服务商或切换地区 |
| token 被拒绝 | 识别与提交之间换了 IP | 改用会话保持 |
| 407 错误 | 认证格式写错 | 核对用户名格式 |
| 中途会话失效 | TTL 太短 | 申请更长的会话时长 |
常见问题
会话保持时长设多久合适?
覆盖完整流程再留一倍余量,表单类通常 5–10 分钟够用。TTL 过长反而容易被限流。
抓国内站点这套策略还适用吗?
思路一致,但类型不同。国内常见 极验(GeeTest)、网易易盾、腾讯防水墙;CaptchaAI 覆盖 GeeTest v3 与图片 / 九宫格。
线程数怎么估?
按峰值并发算,不按日总量:同时 10 个验证码在识别中就需要 10 个线程。BASIC($15/月)5 线程,STANDARD($30/月)15 线程。
相关阅读
先把 IP 策略调对,剩下的交给接口——获取 CaptchaAI API Key,几分钟跑通第一个识别任务。