资讯动态

百度旋转图片验证码识别与自动化适配实战

发布时间:2026/9/15 21:41:09 来源:尧图企业网站定制
百度旋转图片验证码应该有不少朋友在登录百度账号、转存网盘资源或者发帖时候遇到过。它跟常见的滑块验证不太一样不是把碎片推到对应凹槽里而是给你一张倾斜的图片让你用鼠标或者手指把它转到水平位置。这个交互看起来很简单背后涉及图像处理、角度回归、人机交互轨迹模拟等多个技术点。这篇文章我把旋转图片验证码的运行机制、识别思路和自动化适配过程整理出来给正在搞自动化测试、爬虫调试或者纯粹对验证码技术感兴趣的朋友一份能直接上手的参考。1. 旋转图片验证码的运行机制拆解1.1 它一般出现在哪些场景百度系的验证码不是固定一个地方出现而是由风控系统动态下发。我实际遇到比较多的场景是这么几类未登录状态下批量访问搜索结果页触发风控后弹出验证。百度网盘在分享链接下载大文件时需要先完成验证。贴吧发帖、回帖频率过高被临时要求验证。新设备、新网络环境下登录账号为了确认是真实用户在操作。这个机制跟大多数验证码平台一样本质是后端根据用户行为、设备指纹、IP可信度算出一个风险分值达到阈值就弹验证码。旋转样式的验证码属于“高交互成本”那一类比滑块要难一点但比点选文字要简单一些。因为图片倾斜角度一旦超过45度人的眼睛也需要反应一下才能判断哪边是上自动化程序如果要精确识别就需要真正理解图像内容而不是靠简单的模板匹配就能过关。1.2 从页面加载到校验通过的完整链路我拆过百度的旋转验证码交互流程大致是这么走的前端发起验证初始化请求拿到一个验证码ID和图片数据。图片数据通常以base64格式返回前端把它渲染到一个圆形容器里。用户按住图片下方的滑块按钮拖拽图片跟随旋转。松手后前端把旋转的角度值连同用户操作轨迹提交给后端校验接口。后端判断角度是否在误差允许范围内同时校验轨迹特征。校验通过返回token前端拿着这个token继续刚才被拦截的业务请求。这里面有两个关键参数一个是用户最终旋转的角度误差一个是操作轨迹。角度误差是明面上可以量化的轨迹数据则是后端暗地里用来判断“这个操作是真人还是脚本”的依据。我实测下来百度这个旋转验证的角度容错范围大概是正负6到8度不是特别严格。也就是说你转了个大概齐只要不超过误差阈值后端是认的。真正卡人的其实是轨迹特征和请求频率。1.3 角度是怎么生成和校验的服务端在下发验证码的时候会先选定一张正常的图片记录图片原本“水平”的状态然后随机生成一个旋转角度比如逆时针137度接着把图片旋转后再发给前端。前端用户操作的过程本质上就是把图片再转回去转的角度接近-137度或者说是223度图片就回到水平状态。这里有一个很容易踩坑的认知误区很多人以为后端校验的是最终图片是否“回正”不是的。后端拿到的只是用户最终停下的角度数值它把这个数值跟原始随机角度做差值运算差值落在容错区间内就算通过。所以在做自动化适配的时候没必要真的去做图像比对确认最终画面是否水平只需要预测出“当前图片需要转多少度才能回正”然后把这个角度提交上去就行。另外不同版本的验证码返回参数名可能不一样有时候是rawAngle有时候是angle还有时候会把初始角度通过一个JSON字段下发到前端。拿到初始角度之后配合预测出的修正角度就能算出最终提交值。这个细节等后面实操环节再展开。2. 交互设计与图片结构里的门道2.1 为什么用旋转而不是滑块或点选从产品设计角度看滑块验证码已经被破解得太成熟了很多开源方案能直接识别缺口位置并模拟拖动。点选文字验证码容易受中文识别模型训练集影响题库一旦更新旧模型立刻失效。旋转验证码相对来说更依赖“对图像语义的理解能力”这种能力在深度学习普及之前是比较难无差别实现的。而且旋转交互的过程天然会产生一条连续轨迹这条轨迹比滑块那种单方向拖动包含更多特征维度。人用手控制鼠标转一圈速度是时快时慢的中间有时候还会停顿回拉一下这些特征到了后端都会被提取出来做行为建模。对于普通用户来说旋转验证甚至不需要动脑子就是“看着歪了什么转正就行”门槛很低。但对自动化程序来说既要识别图像方向又要模拟出人类轨迹难度直接翻倍。这应该就是旋转验证码能长期存在的原因。2.2 图片资源里的猫腻我抓到过多次旋转验证码图片总结下来有几个比较有意思的特点图片固定是正方形边长通常在300到500像素之间加载后会按CSS缩放到指定显示尺寸。图片背景一般是不透明的可能是纯色背景也可能是有语义的人像、风景、商品图。大部分情况下图片里会有明显的地平线、建筑物边缘、人像五官或者文字方向这些都是用来判断方向性的关键线索。部分验证码会在图片四周加入类锯齿状的白边或者在角落加上倾斜的噪点线条这是专门用来干扰计算机视觉算法识别主轴的。如果深入研究过各家验证码会发现百度这套旋转验证码的图片素材其实很多来源于自家生态内的内容比如地图街景里的建筑物、百科词条里的标志物、网盘里被用户上传的相册图片。不同来源对应的特征差异很大比如地图街景经常有道路和天际线人像图片主要靠五官方向和下颌线来判定。这个特点决定了针对一种类型图片训练的模型换到另一类图片上效果可能会掉得厉害。所以做工程化方案的时候需要收集尽量多样化的图片样本或者干脆在推理时加一个图像分类的预处理先判断图片类型再选择对应的方向估计策略。2.3 轨迹采集与风控模型前端会把用户从按下滑块到松开的完整动作采集下来包括鼠标或触摸的坐标序列、时间戳、压力值移动端甚至可能加上设备陀螺仪数据。这些数据打包成一个加密字段跟在角度值后面一起提交。风控后端拿到轨迹后会提取类似下面这些特征总耗时人类完成一次旋转通常需要0.8秒到3秒太快或者太慢都异常。轨迹点数人类操作一秒钟大概会有60到100个采样点脚本如果只上传了端点坐标一查一个准。移动是否平滑人类拖拽会有微小抖动轨迹点不会在一条完美直线上。是否有停顿和回退人偶尔会拖过再拖回来微调这个很关键纯脚本轨迹通常是一条直线到头。所以哪怕你的角度预测非常准只要轨迹数据是直接拉一条直线生成的照样过不了校验。这就是为什么很多搞自动化的人角度识别做得很好最后还是卡在“最后一公里”。3. 自动化识别的几种技术路线3.1 传统视觉方案边缘检测加主轴估计最早做旋转验证码识别没上深度学习之前大家用的都是传统图像处理。核心思路其实就一句话找到图片中最显著的方向性线索然后算出它和水平方向的夹角。操作大概是这样的先把图片转成灰度图用Canny算子提取边缘然后对边缘像素做主成分分析或者霍夫直线检测得到图像的主方向。如果图片里有明显的地平线、建筑物轮廓、梯形结构主方向往往能对应到真实的重力方向用这个跟水平方向做差就是需要旋转的角度。我实际试过这个方案在纯风景、有明显天空和地面分界的图片上准确率能到80%以上。但一碰到特写人像、抽象图案、光线杂乱的图片就崩了。因为主成分分析本质上是统计所有边缘的整体方向分布当画面里有大量任意方向的纹理时主方向会被带偏。另一个传统思路是用特征点匹配预先准备一批“正立”的参考图库把待旋转图片跟参考图库做ORB或者SIFT特征匹配通过匹配点对求解单应性矩阵从而估算出旋转角度。这方案需要维护一个较大的正立图库而且对图片内容要求很高如果待验证图片在参考图库里找不到相似样本匹配就会失败。总结下来传统方案优点是不需要训练数据、纯OpenCV就能跑缺点是泛化能力差只能在比较理想的图片上工作。现在做工程化方案基本上都转向深度学习方向估计了。3.2 深度学习方案让模型直接预测角度深度学习做旋转验证码识别主流做法很有意思跟一般图像分类不一样它不是预测这是什么物体而是把整张图片作为输入输出一个代表旋转角度的数值。换句话说这是一个回归任务目标变量是0到360度之间的角度。模型通常这样搭用ImageNet预训练的ResNet50作为特征提取骨干网络去掉最后的全连接分类层接一个输出维度为1的全连接层输出值乘以360就得到预测角度。还有一些变体做法是输出两个值一个表示正弦、一个表示余弦通过atan2还原角度。前者简单直接但存在角度接近0度时误差被放大的问题后者做了周期连续性建模实际训练中更容易收敛。关键技巧在数据准备上。需要准备一批“正立”图片然后随机旋转任意角度作为训练输入旋转前的原始角度就作为标签。数据增强时要注意除了常规的平移、缩放、颜色抖动不要做随机旋转增强否则会把标签弄乱。我搭过一个这样的模型用1000张正立图每张随机旋转10次生成1万条训练样本在ResNet50上微调了10个epoch验证集上的平均绝对误差能控制在5度以内。这个精度已经足够通过百度的旋转验证了。提升精度的小技巧是把角度回归改成“角度区间分类加细粒度回归”的分层方案。也就是先判断图片属于哪个粗区间比如每45度一个桶共8个桶做分类然后在桶内做回归预测与桶中心角度的偏移量。这种方案比直接回归误差更小而且训练时更稳定。3.3 第三方打码平台作为兜底自己训练模型需要标注数据如果只是临时接一下验证码建模成本可能高于收益。这种情况下可以考虑接入第三方打码平台。打码平台的接入逻辑比较简单把验证码图片上传到平台接口平台上的真人标注员会帮你旋转并返回正确角度然后你再把这个角度模拟提交。平台通常按次收费单次价格很低。但这个方案有几个风险要单独提一下延迟不稳定高峰期可能等几秒才有结果。部分平台会收集图片数据存在泄露风险。用打码平台相当于把自动化特征暴露给了第三方如果第三方接口不稳定整体流程可靠性会受影响。我的建议是打码平台适合“应急”和“低频跑通”场景适合做开发阶段测试长期稳定跑的话还是自己训练模型更可控。3.4 模拟交互的自动化方案角度预测只是解决了“该转多少度”的问题剩下还有“怎么把转的操作执行出来”。这一步需要在浏览器自动化环境里完成。目前主流方案是Playwright它同时支持Chromium、Firefox、WebKitAPI也比旧时代的Selenium简洁很多。流程分三步启动浏览器访问目标页面触发验证码弹出。通过CSS选择器定位到旋转滑块和图片容器。根据预测角度计算滑块拖拽的距离角度转距离需要知道图片半径然后执行拖动操作。这里最核心的难点是角度到像素距离的换算。旋转验证码的图片是圆形布局滑块按某一半径拖动时角度和弧长满足这样一个关系弧长 角度(弧度) * 旋转半径所以只需要知道图片的显示半径和中心点就能算出滑块的移动距离。半径可以从元素的渲染尺寸拿中心点可以用元素边界加上偏移量计算出来。轨迹模拟是关键。直接一步把鼠标从起点拖到终点轨迹时长只有几十毫秒必然会被风控拦下来。正常人的操作是一次完整的、有加速度变化、有转折停顿的拖拽过程。所以工程上会用贝塞尔曲线生成一条带随机性的轨迹并在中段加入若干停顿点模拟出“先看一下、再拖一下、拖过头再回拉一点”的真人习惯。4. 一次完整实操识别角度并自动通过验证4.1 环境准备先列一下我这次实操用的环境Python 3.10OpenCV 4.8PyTorch 2.0Playwright 1.40及以上版本ResNet50预训练权重安装命令很简单我直接放在这里pip install opencv-python torch torchvision playwright playwright install chromium4.2 抓取验证码图片并分析接口先用Playwright的录制功能打开触发验证码的页面手动完成一次验证通过开发者工具里的网络面板就能看到验证码相关的接口。我一般会关注这几个字段图片接口返回的base64字符串。初始化响应里是否有原始旋转角度参数。提交验证时角度字段的加密情况。实际操作中为了做训练集需要批量抓取验证码图片。这里有一个取巧的办法反复触发验证码接口但不做任何操作直接关闭弹窗每次弹窗都会下发一张新图片。把这些图片保存下来再筛掉重复的就能积累一批样本。不过要注意频率控制短时间内高频请求接口很容易触发账号风控。我当时的节奏是每个账号间隔10秒才拉一次每次拉完等2分钟再拉下一轮跑了两个小时没有任何异常。抓下来的图片统一存成JPG格式命名用时间戳加随机字符串方便后续处理。4.3 训练角度预测模型训练数据我采用的思路是先整理一批“正立”图片然后程序对每张图片随机旋转一个角度旋转后的图作为输入旋转角度作为标签。这样生成的训练集全部是自动标注不需要人工参与。生成样本的核心代码大概长这样import cv2 import numpy as np def generate_sample(image, max_angle360): # 随机生成旋转角度 angle np.random.uniform(0, max_angle) h, w image.shape[:2] center (w // 2, h // 2) # 旋转保持原图尺寸超出部分用黑边填充 rot_mat cv2.getRotationMatrix2D(center, angle, 1.0) rotated cv2.warpAffine(image, rot_mat, (w, h), borderModecv2.BORDER_CONSTANT, borderValue(0, 0, 0)) return rotated, angle模型方面我用了ResNet50做特征提取把最后一层替换成输出维度为2的全连接层分别代表角度的正弦和余弦值。推理时用atan2还原出最终角度。训练时的损失函数用的是角度差的L1损失但在计算之前需要先做角度差标准化。这里有个经验值把角度差映射到-180到180度区间再算损失模型收敛速度会快很多。核心训练逻辑大概是这样的import torch import torch.nn as nn from torchvision import models class AngleModel(nn.Module): def __init__(self): super().__init__() self.backbone models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V1) self.backbone.fc nn.Sequential( nn.Linear(2048, 512), nn.ReLU(), nn.Dropout(0.3), nn.Linear(512, 2) ) def forward(self, x): out self.backbone(x) return out def angle_loss(pred, target_angle): # 将预测值转为yiwei角度 pred_angle torch.atan2(pred[:, 0], pred[:, 1]) * 180 / torch.pi diff (pred_angle - target_angle 180) % 360 - 180 return torch.abs(diff).mean()训练结束后把模型转成ONNX格式部署单张图片推理耗时在毫秒级完全够用。实际测试中对地图街景类图片平均误差约3.5度对人像类图片约5.2度对抽象纹理类图片约7.1度。整体命中率预测角度和真实角度差值在正负8度以内约为93%。4.4 端到端自动化验证模型有了接下来就是把它接进Playwright流程里。完整的操作逻辑我从经验出发大概分这几步打开页面等待验证码弹出。定位图片容器截取验证码区域图像。用训练好的模型预测需要旋转的角度。根据图片容器半径把角度换算成滑块需要拖动的像素距离。用模拟轨迹把滑块拖到指定位置。松手等待校验结果。如果失败重试并调整轨迹参数。角度到像素距离的换算用前面提到的弧长公式。这里补充一个细节图片容器在页面上经常不是原始尺寸而是缩放了。所以计算半径的时候一定要用element.bounding_box()拿渲染尺寸不要用图片原始像素尺寸。下面是核心交互代码的简化版本import asyncio import math from playwright.async_api import async_playwright async def rotate_slider(page, target_angle, center_x, center_y, slider_handle): # 计算需要旋转的弧度 radians math.radians(target_angle) radius 100 # 根据元素bounding_box计算得到 distance radians * radius box await slider_handle.bounding_box() start_x box[x] box[width] / 2 start_y box[y] box[height] / 2 # 用贝塞尔轨迹执行拖动省略中间轨迹生成逻辑 await page.mouse.move(start_x, start_y) await page.mouse.down() # 分多步移动模拟人类拖动每步有停顿 steps 50 for i in range(1, steps 1): end_x start_x distance * i / steps await page.mouse.move(end_x, start_y, steps3) await asyncio.sleep(0.01) await page.mouse.up()这段代码只是骨架实际跑的时候需要加上轨迹随机化、停顿逻辑、失败重试。我建议把重试次数控制在3次以内连续失败就放弃不要硬杠不然容易被风控记住。5. 踩坑记录与排查技巧5.1 图片被压缩或加入干扰导致识别不准有段时间我在测试环境跑得好好的模型一到线上识别率掉了将近20个百分点。排查之后发现线上验证码图片经过了更激进的JPEG压缩画质下降明显而且边缘被加了模糊处理的白色锯齿。针对这个问题我做了两步优化训练阶段就把训练集图片做随机JPEG压缩和质量降低模拟线上压缩环境推理阶段先对图片做一次轻量的锐化预处理突出边缘信息。优化之后识别率基本恢复到训练时的水平。5.2 角度偏移量为什么忽正忽负不少人在提交角度时发现模型预测的角度明明很准但验证就是不过。后来定位到问题是“角度符号”没有对齐。同一个旋转操作前端和后端记录的可能是顺时针或逆时针两个方向如果符号搞反了差的就不是几度而是接近180度。排查方法很简单手动在页面上转一次同时抓包看提交的参数值和前端初始化时返回的原始角度把它俩对比一下把角度值调整成统一坐标系。我习惯约定为顺时针为正逆时针为负然后在代码里统一处理。5.3 轨迹过于顺滑被判成机器人第一次跑通自动化时角度识别精确到了1度以内但成功率只有不到三成。后来抓了提交的轨迹参数开始分析数据发现我生成的轨迹点太均匀了每两个点之间的时间间隔几乎完全一致这种“完美轨迹”反而异常。后来我在轨迹里加入了几种人类操作的随机特征开始时停留100到200毫秒再拖动、拖动中点缓慢加速减速、松手前有微小的回拉动作。成功率直接提升到85%以上。事实证明在旋转验证这个场景里更像人比更精确更重要。5.4 高频访问触发更强验证即使全部校验通过短时间多次触发验证码后端可能直接甩出更复杂的点选验证码甚至短信验证。这说明风控不只看单次验证是否通过还会统计触发频率。应对策略无非是降低并发、增加随机延迟、使用多个账号分摊压力。但这里要非常严肃地提醒一句如果你是在做正经的自动化测试请务必在测试环境或者有授权的环境里跑如果是爬虫业务也要尊重目标网站的条款别给人家服务器造成压力别做撞库、批量注册这类违法操作。验证码本质上保护的是正常用户的安全这个前提要想清楚。5.5 不同产品线验证码样式不同百度贴吧、百度网盘、百度搜索触发出来的旋转验证码虽然交互形式差不多但图片尺寸、滑块位置、DOM结构、接口参数名都有差异。适配新场景时最快的方式是先用Playwright打开页面手动过一遍抓完整的接口日志把差异点列出来然后再决定是改配置还是改代码。我见过有些团队把不同产品线的验证码适配做成了可配置的JSON模板DOM选择器、接口参数名、容错角度这些东西都抽到配置里上线新场景只需要补充配置就行不用动代码。这个思路在维护多套验证适配时非常实用。6. 一些关于工程化的经验做旋转验证码自动化最容易翻车的地方其实不是模型精度而是“把一次性脚本当成长期方案来用”。验证码是动态对抗的产物今天能过的方案明天页面改个DOM结构可能就废了。所以工程化的时候一定要把代码按照“采集层、识别层、交互层、调度层”四个模块拆开采集层负责拿图片和初始化参数变化时只需要改这里。识别层只负责输入图片输出角度跟页面完全解耦。交互层负责执行拖动动作主要依赖页面DOM结构。调度层负责频率控制、失败重试、账号管理等。每一层独立维护哪一层被风控针对了就单独替换那一层。我见过有人把四层代码全写在一个文件里页面一改就整个推倒重来那基本是每天都在救火的节奏。还有模型要定期重新训练。验证码平台会不断更新图片素材三个月前的素材分布和现在可能完全不一样。我自己的习惯是每个月跑一次批量采集把新图片加入训练集重新微调模型保持在最新素材上的识别率不下滑。最后再分享一个小技巧角度预测的置信度可以拿到调度层做策略决策。比如模型对某张图片输出角度很犹豫连续两次预测结果差异超过15度那就说明这张图可能是平台的“对抗样本”别硬刚直接标记失败跳过等下一轮验证码重新弹出来就行。单次通过率不需要做到100%整体流程跑通才是目标。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价