YesCaptcha插件+自建API实战:用DdddOCR实现浏览器自动化测试中的验证码绕过
YesCaptcha插件自建API实战用DdddOCR实现浏览器自动化测试中的验证码绕过验证码一直是自动化测试工程师的痛点。每当脚本运行到关键步骤一个突然弹出的验证码就能让整个流程戛然而止。传统解决方案要么成本高昂要么识别率不稳定。本文将分享如何通过YesCaptcha插件与自建DdddOCR API的组合构建一套经济高效的验证码处理方案。这套方案特别适合中小团队或个人开发者它既保留了商业方案的便捷性又通过开源技术降低了成本。我们将从API搭建开始逐步深入到与Selenium/Playwright的集成最后探讨如何应对识别率玄学这个现实问题。1. 技术选型与架构设计在开始编码前我们需要明确两个核心组件的定位YesCaptcha插件负责浏览器环境中的验证码捕获和结果回填DdddOCR服务提供本地的验证码识别能力它们通过一个简单的Flask API进行通信整体架构如下图所示[浏览器] ←→ [YesCaptcha插件] ←→ [自建API] ←→ [DdddOCR]这种设计有三大优势隐私性所有验证码图片不会离开本地环境成本控制无需支付按次计费的识别服务可扩展性可随时切换OCR引擎而不影响浏览器端配置2. 搭建DdddOCR识别服务DdddOCR以其开箱即用的特性成为理想选择。我们先搭建基础的识别服务from flask import Flask, request, jsonify import ddddocr import base64 import uuid app Flask(__name__) ocr ddddocr.DdddOcr(show_adFalse) # 禁用广告输出 app.route(/captcha/v1, methods[POST]) def handle_captcha(): try: image_data request.json.get(image) if not image_data: return jsonify({error: Missing image data}), 400 # 移除base64前缀 if base64, in image_data: image_data image_data.split(base64,)[1] decoded base64.b64decode(image_data) result ocr.classification(decoded) return jsonify({ success: True, result: result }) except Exception as e: return jsonify({ success: False, error: str(e) }), 500 if __name__ __main__: app.run(host0.0.0.0, port5000)关键参数说明参数说明推荐值show_ad控制台广告显示Falseuse_gpuGPU加速根据设备情况charsets字符集设置根据验证码类型调整启动服务python server.py --threaded # 启用多线程处理3. YesCaptcha插件配置与对接YesCaptcha插件需要配置我们的自建API端点在Chrome中安装YesCaptcha插件点击插件图标 → 设置 → 自定义API填写API地址http://your-server-ip:5000/captcha/v1保存设置常见问题排查如果遇到CORS错误需要在Flask中添加CORS支持from flask_cors import CORS CORS(app) # 允许所有跨域请求测试API是否正常工作curl -X POST http://localhost:5000/captcha/v1 \ -H Content-Type: application/json \ -d {image:base64-encoded-image}4. 与自动化测试框架集成4.1 Selenium集成方案在Selenium脚本中我们需要处理两种常见场景场景一传统图片验证码from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait def solve_captcha(driver): # 等待验证码元素出现 captcha_img WebDriverWait(driver, 10).until( lambda d: d.find_element(By.XPATH, //img[contains(src,captcha)]) ) # 触发YesCaptcha识别 driver.execute_script( const img arguments[0]; const event new MouseEvent(contextmenu, { bubbles: true, clientX: img.getBoundingClientRect().left 10, clientY: img.getBoundingClientRect().top 10 }); img.dispatchEvent(event); , captcha_img) # 等待插件处理 time.sleep(2) # 根据网络情况调整场景二reCAPTCHA挑战对于Google reCAPTCHAYesCaptcha可以自动处理可见挑战但需要确保插件已启用reCAPTCHA支持页面加载了reCAPTCHA API有足够的等待时间# 在reCAPTCHA iframe出现后 driver.execute_script( document.querySelector(.g-recaptcha).scrollIntoView(); ) time.sleep(3) # 等待插件处理4.2 Playwright集成技巧Playwright的浏览器上下文需要特别处理插件加载from playwright.sync_api import sync_playwright with sync_playwright() as p: # 加载带插件的浏览器实例 context p.chromium.launch_persistent_context( user_data_dir./profile, args[ f--disable-extensions-except/path/to/yescaptcha, f--load-extension/path/to/yescaptcha ] ) page context.new_page() page.goto(https://target-site.com) # 处理验证码出现的逻辑 if page.locator(#captcha-image).is_visible(): page.click(#captcha-image, buttonright) page.wait_for_selector(#yescaptcha-menu) page.click(text识别验证码)5. 识别率优化与监控DdddOCR的识别率确实存在波动我们可以通过以下策略改善训练数据增强# 自定义字符集提高特定类型验证码识别率 ocr ddddocr.DdddOcr( charsetsABCDEFGHJKLMNPQRSTUVWXYZ23456789, # 去除容易混淆的字符 use_gpuTrue )结果验证机制def validate_captcha(text): # 基础校验规则 if len(text) ! 4: return False if not text.isalnum(): return False return True监控方案设计记录每次识别的响应时间对失败请求进行重试定期统计识别成功率app.route(/captcha/v1, methods[POST]) def handle_captcha(): start_time time.time() try: # ...原有处理逻辑... # 添加监控日志 log { timestamp: datetime.now().isoformat(), duration: time.time() - start_time, success: True, type: request.json.get(type, unknown) } write_log(log) return jsonify(response) except Exception as e: # 错误日志 log.update({success: False, error: str(e)}) write_log(log) raise6. 生产环境部署建议对于需要7×24小时运行的自动化测试系统建议服务部署使用Gunicorn替代Flask开发服务器gunicorn -w 4 -b :5000 server:app负载均衡 当QPS较高时可以通过Nginx实现负载均衡upstream ocr_servers { server 127.0.0.1:5000; server 127.0.0.1:5001; server 127.0.0.1:5002; } server { listen 80; location / { proxy_pass http://ocr_servers; } }性能监控 使用Prometheus Grafana监控关键指标指标名称说明预警阈值request_duration_seconds请求处理时间1serror_rate错误率20%active_threads活跃线程数80% max_threads7. 备选方案与降级策略任何OCR方案都不可能100%可靠必须设计降级方案人工介入流程当连续失败超过3次时自动截图保存验证码通过邮件/IM通知负责人多引擎备用def recognize_with_fallback(image): try: # 主引擎尝试 result ocr.classification(image) if validate_result(result): return result # 备用引擎 from other_ocr import backup_recognize return backup_recognize(image) except: return manual_process(image)验证码预处理from PIL import Image, ImageFilter def preprocess_image(image_bytes): img Image.open(io.BytesIO(image_bytes)) img img.filter(ImageFilter.SHARPEN) # 锐化 img img.point(lambda x: 0 if x 128 else 255) # 二值化 return img.tobytes()这套方案在实际项目中已经稳定运行超过6个月平均识别率保持在78%左右。对于特别复杂的验证码我们最终采用了商业API自建服务的混合模式——常规验证码走本地API特殊类型自动切换到付费服务。这种组合既控制了成本又保证了关键业务的顺畅运行。