签证预约总约不到号,卡住流程的往往不是验证码,而是识别和提交之间的时间差太紧。BLS 是使领馆签证、证件门户常用的验证码方案,几乎每步都会弹一次。CaptchaAI 识别 BLS 通常 1 秒内完成,真正决定成败的是会话保持和提交时机——国内签证代办、留学团队常盯紧名额,一有空位就处理。下面按实际顺序拆解整个流程。
用 CaptchaAI 识别 BLS 验证码:三步流程
分三步:抓图、识别、提交,顺序不能打乱——结果要回填同一个 session。
第一步:抓取验证码图片和 instructions 参数
图片有的内联 base64,有的是独立 URL,两种都要处理:
import requests
from bs4 import BeautifulSoup
import base64
session = requests.Session()
# Load the portal page
page = session.get("https://portal.example.gov/appointment")
soup = BeautifulSoup(page.text, "html.parser")
# Find the CAPTCHA image
captcha_img = soup.select_one("img#captcha-image, img.captcha")
captcha_url = captcha_img["src"]
# Download the CAPTCHA image
if captcha_url.startswith("data:"):
# Base64 encoded inline image
img_data = captcha_url.split(",")[1]
else:
# URL-referenced image
img_response = session.get(captcha_url)
img_data = base64.b64encode(img_response.content).decode()
第二步:调用 CaptchaAI 接口识别
提交到 in.php,每 3 秒轮询一次 res.php,最多 30 次超时报错:
import time
def solve_bls_captcha(image_base64, instructions=""):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": "YOUR_API_KEY",
"method": "base64",
"body": image_base64,
"instructions": instructions,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(30):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY",
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("BLS solve timed out")
# Solve
captcha_answer = solve_bls_captcha(img_data)
第三步:把识别结果提交回表单
用同一个 session 提交,cookie 自动带上,换新 session 会失效:
# Find form fields
form_data = {
"captcha_response": captcha_answer,
"appointment_type": "visa",
"location": "embassy-city",
# ... other form fields
}
# Submit with the same session (cookies preserved)
result = session.post(
"https://portal.example.gov/appointment/submit",
data=form_data
)
if "success" in result.text.lower():
print("Form submitted successfully")
政府门户的三种典型交互模式
摸清交互模式比提速更有效,常见三种:
模式一:多步骤表单,验证码放在最后一步
服务类型、个人信息、时段依次填完,验证码卡最后一步:
# Step 1: Select service type
session.post(url, data={"service": "passport"})
# Step 2: Fill personal details
session.post(url, data={"name": "...", "dob": "..."})
# Step 3: Select appointment slot
session.post(url, data={"slot": "2026-04-10-09:00"})
# Step 4: Solve CAPTCHA and confirm
captcha_answer = solve_bls_captcha(get_captcha_image(session))
session.post(url, data={"captcha": captcha_answer, "confirm": "true"})
模式二:验证码答错后自动刷新
答错会直接换新图,重试要连着重新抓图一起做:
max_attempts = 3
for attempt in range(max_attempts):
# Get fresh CAPTCHA for each attempt
captcha_image = get_captcha_image(session)
answer = solve_bls_captcha(captcha_image)
result = session.post(submit_url, data={"captcha": answer})
if "incorrect" not in result.text.lower():
break
print(f"Attempt {attempt + 1} — CAPTCHA refreshed, retrying")
模式三:会话有效期限制
会话通常 10 分钟过期,提前留余量比重试更省时间:
import time
session_start = time.time()
SESSION_TIMEOUT = 600 # 10 minutes typical
def check_session_valid():
elapsed = time.time() - session_start
if elapsed > SESSION_TIMEOUT - 60: # 1 min safety margin
print("Session expiring — refresh needed")
return False
return True
# Before CAPTCHA submission
if not check_session_valid():
# Start a fresh session
session = requests.Session()
session.get(portal_url)
session_start = time.time()
常见问题排查
| 问题 | 原因 | 处理方式 |
|---|---|---|
| 识别成功但提示会话已过期 | 识别和提交间隔太久 | 识别完立刻提交 |
| 验证码图片加载不出来 | 门户校验 Referer 头 | Referer 设为门户页面 URL |
| 答案正确但表单被拒 | 缺少 CSRF token 或隐藏字段 | 提取表单所有隐藏 input |
| 多次尝试后门户拒绝 | 触发限流 | 拉长尝试间隔 |
| 每次验证码都不一样 | 门户按请求动态生成 | 每次重新抓图再识别 |
会话管理:政府门户对 cookie 和 CSRF 卡得很严
政府门户对会话容忍度比电商网站低:
| 要求 | 落地做法 |
|---|---|
| 精确维护 cookie | 用 requests.Session() 或持久化上下文 |
| 不触发限流 | 请求间加 2–5 秒延迟 |
| 处理 CSRF token | 每次 POST 前提取并带上 |
| 尊重会话超时 | 全流程控制在超时窗口内 |
| 跟随重定向 | 客户端自动处理重定向 |
BLS 验证码出现在政府门户的哪些环节
BLS 验证码常见于需要锁定名额或核实身份的环节:
| 门户场景 | 验证码出现位置 | 目的 |
|---|---|---|
| 签证预约 | 选时段前 | 防止脚本批量占用时段 |
| 材料上传 | 提交前 | 确认操作者是真人 |
| 状态查询 | 展示结果前 | 限制查询频率 |
| 申请表提交 | 提交前 | 防止批量提交 |
BLS 验证码常见的挑战类型
BLS 验证码不是单一样式,常见的有这几类:
- 扭曲文字的图像挑战
- 数学算式挑战
- 自定义图片选择任务
- 文字类谜题
每种挑战对应一个 instructions 参数,告诉 CaptchaAI 按哪种方式识别。
门户需要 JavaScript 渲染时,改用浏览器自动化
验证码由前端动态渲染时,改用 Puppeteer 更合适:
const puppeteer = require('puppeteer');
async function handleBLSPortal() {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://portal.example.gov/appointment');
// Wait for CAPTCHA to load
await page.waitForSelector('img#captcha-image');
// Get CAPTCHA image as base64
const imgBase64 = await page.evaluate(() => {
const img = document.querySelector('img#captcha-image');
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
return canvas.toDataURL('image/png').split(',')[1];
});
// Solve with CaptchaAI
const answer = await solveBLSCaptcha(imgBase64);
// Type the answer
await page.type('#captcha-input', answer);
// Submit
await page.click('#submit-button');
// Wait for result
await page.waitForNavigation();
}
常见问题
BLS 验证码和常见的 reCAPTCHA、Turnstile 有什么不同?
BLS 更接近图片验证码:扭曲文字、算式、图片选择为主,依赖图像识别而非行为打分。建议先抓一张真实图片确认题型。
识别 BLS 验证码要多久?
识别本身通常 1 秒内完成,是官方 SLA 上限,真正占时间的是下载、排队和提交,都算进超时预算。
提交后提示会话已过期,是识别太慢了吗?
多数不是,常见原因是没立刻提交,或 cookie、CSRF token 没带过去。验证码放最后一步识别即可避免。
应该用浏览器自动化还是直接发 HTTP 请求?
字段能从 HTML 直接拿到时,requests.Session() 更快;只有依赖 JavaScript 渲染时才需要浏览器自动化。
相关文章
- BLS 验证码识别分步教程
- 用 Node.js 识别 BLS 验证码
- BLS 验证码的网格识别原理
下一步
门槛不在识别率,而在会话和提交时机。获取 CaptchaAI API 密钥,实测一遍。