sitekey、pageurl 传对了,结果却还是不准?问题往往出在两个容易被忽略的可选字段——instructions 和 code。本文按接入顺序讲清楚提取、提交、排查。
BLS CAPTCHA 参数速查表
| 参数 | 必需 | 类型 | 说明 |
|---|---|---|---|
method |
是 | 字符串 | 固定值 bls |
sitekey |
是 | 字符串 | 目标网站的 BLS CAPTCHA 密钥 |
pageurl |
是 | 字符串 | 展示验证码的页面 URL |
instructions |
否 | 字符串 | 从验证码图片区域提取的文字说明 |
code |
否 | 字符串 | BLS CAPTCHA 的代码/类型标识 |
json |
否 | 整数 | 需要 JSON 格式响应时设为 1 |
三个必填参数决定了请求能不能提交成功,两个可选参数决定了识别准不准:
method、sitekey、pageurl缺一不可,漏传会直接返回ERROR_BAD_PARAMETERSinstructions建议在挑战文字单独展示、没有嵌进图片时补上,能明显减少误判code只在部分站点区分挑战子类型时才有意义,多数场景可以不传
第一步:从页面提取 sitekey、instructions 和 code
# extract_bls.py
import re
from selenium import webdriver
from selenium.webdriver.common.by import By
def extract_bls_params(url):
"""Extract BLS CAPTCHA parameters from a page."""
driver = webdriver.Chrome()
driver.get(url)
params = {"pageurl": url}
# Extract sitekey
captcha_el = driver.find_element(By.CSS_SELECTOR, "[data-sitekey], .bls-captcha")
sitekey = captcha_el.get_attribute("data-sitekey")
if sitekey:
params["sitekey"] = sitekey
# Extract instructions if visible
try:
instructions_el = driver.find_element(
By.CSS_SELECTOR, ".captcha-instructions, .captcha-text"
)
params["instructions"] = instructions_el.text.strip()
except Exception:
pass
# Extract code from hidden input or script
page_source = driver.page_source
code_match = re.search(r'captcha_code["\']?\s*[:=]\s*["\']([^"\']+)', page_source)
if code_match:
params["code"] = code_match.group(1)
driver.quit()
return params
# Usage
params = extract_bls_params("https://bls-example.com/appointment")
print(params)
两点提醒:
- 上面的选择器只是兜底方案,接入前务必用开发者工具核对目标页面的真实 DOM 结构
- 国内环境装 Selenium、requests 慢的话,
pip install加-i指向清华 TUNA 镜像能省不少时间
提示:如果同一个页面同时出现 reCAPTCHA 和 BLS CAPTCHA,先确认当前展示的是哪一种再提交,
method传错会直接拿到无效结果。
第二步:把参数提交给 CaptchaAI
基础请求示例
# solve_bls_basic.py
import requests
import time
import os
def solve_bls(sitekey, pageurl, instructions=None, code=None):
"""Solve BLS CAPTCHA via CaptchaAI API."""
api_key = os.environ["CAPTCHAAI_API_KEY"]
payload = {
"key": api_key,
"method": "bls",
"sitekey": sitekey,
"pageurl": pageurl,
"json": 1,
}
# Add optional parameters for higher accuracy
if instructions:
payload["instructions"] = instructions
if code:
payload["code"] = code
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 for result
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"]
if data["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(data["request"])
time.sleep(5)
raise TimeoutError("BLS solve timeout")
# Usage
solution = solve_bls(
sitekey="your-bls-sitekey",
pageurl="https://bls-example.com/appointment",
instructions="Select images in the correct order",
)
print(f"Solution: {solution}")
instructions 参数:什么时候必须传,为什么能提升准确率
instructions 告诉 CaptchaAI 挑战在问什么。文字单独展示、不嵌在图片里时,一并提交能明显减少误判。
# Common BLS instruction patterns:
instructions_examples = [
"Select images in the correct order",
"Click the images in order from left to right",
"Arrange the images by number",
"Select the matching image",
"Click in the order shown",
]
# Extract instructions from the CAPTCHA image area
def get_instructions_from_page(driver):
"""Try multiple selectors to find instruction text."""
selectors = [
".captcha-instructions",
".bls-captcha-text",
"#captcha-prompt",
".challenge-text",
]
for sel in selectors:
try:
el = driver.find_element(By.CSS_SELECTOR, sel)
text = el.text.strip()
if text:
return text
except Exception:
continue
return None
code 参数:识别具体是哪一种 BLS 挑战
部分 BLS 实现用内嵌代码值区分挑战子类型,code 就是把这个标识传给 CaptchaAI。它藏得比 sitekey 深,通常在隐藏 input 或内联脚本里,需要用正则提取。
# Detect BLS CAPTCHA code from page
def detect_bls_code(page_source):
"""Detect which BLS CAPTCHA code/type is being used."""
patterns = [
(r'captchaType["\']?\s*[:=]\s*["\'](\w+)', "captchaType"),
(r'data-captcha-code["\']?\s*=\s*["\'](\w+)', "data attribute"),
(r'bls_code["\']?\s*[:=]\s*["\'](\w+)', "bls_code"),
]
for pattern, source in patterns:
match = re.search(pattern, page_source)
if match:
return match.group(1)
return None
用 Selenium 串联完整流程
把前面几步串成一个端到端脚本,顺序是:
- 先填好验证码之外的表单字段
- 提取
sitekey、instructions - 调用
solve_bls拿到 token - 把 token 注入页面并提交表单
不同项目需要调整的通常只是选择器和表单字段本身。
# full_bls_flow.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os
import re
def solve_bls_with_selenium(url, form_data=None):
"""Complete BLS CAPTCHA flow using Selenium."""
driver = webdriver.Chrome()
driver.get(url)
wait = WebDriverWait(driver, 15)
# Fill any form fields before CAPTCHA
if form_data:
for field_id, value in form_data.items():
el = wait.until(EC.presence_of_element_located((By.ID, field_id)))
el.clear()
el.send_keys(value)
# Extract CAPTCHA parameters
captcha_container = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "[data-sitekey], .bls-captcha"))
)
sitekey = captcha_container.get_attribute("data-sitekey")
# Get instructions
instructions = None
try:
inst_el = driver.find_element(By.CSS_SELECTOR, ".captcha-instructions")
instructions = inst_el.text.strip()
except Exception:
pass
# Solve via API
solution = solve_bls(
sitekey=sitekey,
pageurl=driver.current_url,
instructions=instructions,
)
# Inject solution
driver.execute_script("""
var input = document.querySelector('input[name="captcha-response"], #captcha-response');
if (input) {
input.value = arguments[0];
} else {
var hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'captcha-response';
hidden.value = arguments[0];
document.forms[0].appendChild(hidden);
}
""", solution)
# Submit form
submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], #submit")
submit_btn.click()
# Wait for confirmation
wait.until(EC.url_changes(url))
result_url = driver.current_url
driver.quit()
return result_url
生产环境使用建议
跑通 demo 之后,上线前建议再检查几点:
- 轮询间隔别低于 5 秒,压得太紧容易触发限流
- 给
solve_bls包一层重试逻辑,遇到网络抖动或超时就退避重试,而不是直接抛异常中断整个流程 timeout按目标站点的实际响应速度调整,默认 30 秒能覆盖大多数场景,响应慢的站点适当调大- 出错时把
task_id一起打进日志,方便回头定位具体是哪次提交失败
常见问题
instructions 参数必须每次都传吗?
不是必须的。挑战文字单独展示、不嵌在图片里时,传上能明显减少误判。
没有 code 参数会影响识别成功率吗?
一般不会,它只在部分实现区分挑战子类型时才有意义,提取不到就不传。
提取不到 sitekey 该怎么排查?
先确认验证码是否异步加载,是的话用 WebDriverWait 等元素出现后再提取,再核对选择器是否匹配真实结构。
常见报错与排查
| 问题 | 原因 | 处理方式 |
|---|---|---|
ERROR_BAD_PARAMETERS |
缺少 sitekey 或 pageurl |
检查两个必填字段是否都提取到了值 |
| 结果被判定为错误 | 挑战含糊,未传 instructions |
补上 instructions,尤其是文字提示单独展示的挑战 |
| 识别出的类型不对 | 目标其实不是 BLS CAPTCHA | 确认不是 reCAPTCHA 或站点自定义组件 |
提取不到 sitekey |
验证码动态加载 | 显式等待元素渲染完成后再提取 |
相关指南
把 instructions 和 code 用对,是 BLS CAPTCHA 识别准确率的关键一步——从 CaptchaAI 开始。