验证码识别任务大部分时间都花在等第三方 API 返回结果上,真正占用 CPU 的时间很短——这正是 AWS Lambda 的强项。把 CaptchaAI 求解逻辑放进 Lambda 函数,你不用为空闲时段付费,也不用维护常驻进程:请求来了才启动,处理完立刻释放,还能直接接入 API Gateway、SQS 或 Step Functions。
编写 Lambda 处理函数
下面的处理函数接收一次求解请求,从环境变量读取 API Key,调用 in.php 提交任务,再轮询 res.php 直到拿到 token 或超时,逻辑和普通脚本一致,只是入口换成了 event/context:
# lambda_function.py
import json
import os
import time
import urllib.request
import urllib.parse
def lambda_handler(event, context):
"""AWS Lambda handler for CaptchaAI solving."""
api_key = os.environ["CAPTCHAAI_KEY"]
# Parse input
body = json.loads(event.get("body", "{}")) if isinstance(event.get("body"), str) else event
method = body.get("method", "userrecaptcha")
params = body.get("params", {})
try:
token = solve_captcha(api_key, method, params)
return {
"statusCode": 200,
"body": json.dumps({"token": token}),
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps({"error": str(e)}),
}
def solve_captcha(api_key, method, params, timeout=90):
"""Solve CAPTCHA using CaptchaAI API."""
# Submit task
submit_data = urllib.parse.urlencode({
"key": api_key,
"method": method,
"json": 1,
**params,
}).encode()
req = urllib.request.Request(
"https://ocr.captchaai.com/in.php",
data=submit_data,
)
with urllib.request.urlopen(req, timeout=30) as resp:
result = json.loads(resp.read())
if result.get("status") != 1:
raise RuntimeError(f"Submit error: {result.get('request')}")
task_id = result["request"]
# Poll for result
start = time.time()
while time.time() - start < timeout:
time.sleep(5)
poll_url = (
f"https://ocr.captchaai.com/res.php"
f"?key={api_key}&action=get&id={task_id}&json=1"
)
with urllib.request.urlopen(poll_url, timeout=15) as resp:
data = json.loads(resp.read())
if data["request"] != "CAPCHA_NOT_READY":
if data.get("status") == 1:
return data["request"]
raise RuntimeError(f"Solve error: {data['request']}")
raise TimeoutError("Solve timeout")
用 Secrets Manager 管理 API Key
API Key 写死在环境变量里能跑起来,但生产环境更推荐用 Secrets Manager 集中管理,配合 IAM 策略控制读取权限:
import json
import boto3
def get_api_key():
"""Retrieve CaptchaAI key from AWS Secrets Manager."""
client = boto3.client("secretsmanager")
response = client.get_secret_value(SecretId="captchaai/api-key")
secret = json.loads(response["SecretString"])
return secret["api_key"]
先把密钥存进 Secrets Manager:
aws secretsmanager create-secret \
--name captchaai/api-key \
--secret-string '{"api_key":"YOUR_API_KEY"}'
用 SAM 模板做基础设施即代码
接下来用 AWS SAM 把部署声明成代码——Lambda 函数、API Gateway 路由、IAM 权限一次定义清楚,回滚也更可控:
# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 120
MemorySize: 256
Runtime: python3.11
Resources:
CaptchaSolverFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda_function.lambda_handler
Environment:
Variables:
CAPTCHAAI_KEY: !Sub "{{resolve:secretsmanager:captchaai/api-key:SecretString:api_key}}"
Events:
SolveApi:
Type: Api
Properties:
Path: /solve
Method: post
Policies:
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:captchaai/api-key-*"
Outputs:
SolveApiUrl:
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/solve"
构建与部署
两条命令就能上线,再用 curl 验证接口是否正常返回 token:
# Build and deploy
sam build
sam deploy --guided
# Test
curl -X POST https://YOUR_API_ID.execute-api.us-east-1.amazonaws.com/Prod/solve \
-H "Content-Type: application/json" \
-d '{
"method": "userrecaptcha",
"params": {
"googlekey": "SITE_KEY",
"pageurl": "https://example.com"
}
}'
结合 SQS 实现批量验证码处理
验证码任务批量产生时——比如一次抓取要处理几百个页面——用 SQS 触发 Lambda 比同步调用更稳:队列做缓冲,Lambda 按并发消费,单个任务失败不会拖垮整批:
import json
import os
import time
import urllib.request
import urllib.parse
def sqs_handler(event, context):
"""Process CAPTCHA tasks from SQS queue."""
api_key = os.environ["CAPTCHAAI_KEY"]
results = []
for record in event["Records"]:
task = json.loads(record["body"])
try:
token = solve_captcha(
api_key,
task["method"],
task["params"],
)
results.append({
"task_id": task.get("id"),
"status": "success",
"token": token[:50],
})
except Exception as e:
results.append({
"task_id": task.get("id"),
"status": "error",
"error": str(e),
})
return {"results": results}
Lambda 参数怎么选:超时、内存、并发
这几个参数基本不用反复调整:
| 参数 | 建议值 |
|---|---|
| 最大超时 | 15 分钟上限,大多数验证码场景设 2 分钟够用 |
| 内存 | 256 MB 足够,函数本身不做重计算 |
| 并发 | 默认 1000 并发,量大时提前申请提额 |
| 冷启动 | Python 约 500 毫秒,相对轮询等待可以忽略 |
| 成本 | 每次调用约 $0.0001(仅计算部分) |
| 依赖 | 用内置的 urllib,省去打包 Lambda 层的麻烦 |
在 AWS 中国区部署要注意什么
Lambda 若部署在 AWS 中国区(cn-north-1、cn-northwest-1),出公网访问 ocr.captchaai.com 这类境外地址的延迟通常不如 ap-southeast-1(新加坡)稳定,批量高并发时更容易出现偶发超时。用户主要在国内的团队,选 ap-southeast-1 往往更省心,上线前先跑一轮真实延迟测试,再对齐轮询超时时间。
常见故障排查
| 问题 | 原因 | 处理方式 |
|---|---|---|
| 函数执行超时 | Lambda 超时时间短于求解耗时 | 把超时设为 120 秒以上 |
| 读取密钥报权限拒绝 | 缺少 IAM 策略 | 给函数角色加上 Secrets Manager 读取权限 |
| 冷启动拖慢首次响应 | 调用频率低 | 开启预置并发(provisioned concurrency) |
| 导入 requests 报错 | Lambda 未打包该依赖 | 改用内置的 urllib.request,或额外挂一个 Layer |
常见问题
Lambda 处理验证码划算吗?
划算。一次 256 MB、60 秒的调用大约 $0.0001,在 CaptchaAI 的 API 费用之上几乎可以忽略。CaptchaAI 按并发线程数收费,不是按次数——比如 BASIC 套餐 $15/月含 5 个线程,线程内解题次数不限——真正影响成本的是套餐线程数,不是 Lambda 本身。
Lambda 超时应该设多久?
大多数验证码在 10-60 秒内出结果,把超时设为 120 秒足够覆盖轮询等待。任务里包含 reCAPTCHA Enterprise 这类更复杂的类型时,建议留到 180 秒。
能不能不用每次都部署,先在本地测试处理函数?
可以。sam local invoke 在本地用 Docker 模拟 Lambda 运行时,传入测试用的 event.json 就能跑通提交、轮询、返回 token 的完整逻辑,确认无误再 sam deploy。
如何把轮询产生的 Lambda 计费时长降到最低?
把首次轮询间隔从 5 秒起步改成逐步递增(3 秒、5 秒、8 秒……),多数简单验证码在前几次轮询就能拿到结果,明显减少空转;耗时长的类型可以拆到 SQS 异步流程里,用 Step Functions 编排轮询节奏。
SQS 批量处理里失败的任务需要重试吗?
需要。给队列配置死信队列(DLQ),设置合理的 maxReceiveCount(比如 3 次),超过重试次数的任务会自动转入 DLQ,方便你判断是验证码识别失败,还是网络或 API Key 配置问题。
相关指南
从服务器运维中解脱出来——立即获取你的 CaptchaAI API Key。