
1. 从“点选”到“识别”一个看似简单却暗藏玄机的任务“中文点选识别”这六个字拆开来看每个词都平平无奇但组合在一起就构成了一个在互联网安全、人机交互和自动化测试领域里既常见又充满挑战的技术课题。简单来说它的核心任务就是让机器能够“看懂”一张图片或一个界面上的中文文字并准确地“点击”指定的文字或词语。听起来是不是有点像我们每天在手机上进行的验证码操作没错这正是它的一个典型应用场景——验证码识别与对抗。但它的应用远不止于此从自动化测试脚本需要点击某个特定按钮到数据采集工具需要模拟用户点击特定文本链接再到辅助工具帮助用户快速定位界面元素背后都可能需要这项技术的支持。我之所以对这个话题有深入的体会是因为在实际的爬虫开发和自动化项目中我无数次与各种形态的“点选验证码”狭路相逢。早期的验证码只是扭曲的数字字母后来进化到了需要点击图中特定文字比如“请点击下图中所有的‘公交车’”。这种从“识别”到“执行”的跨越对机器而言难度陡增。它不再是简单的OCR光学字符识别问题而是一个融合了目标检测、文本识别和坐标映射的复合型任务。机器需要先找到图中所有的文字区域目标检测然后识别出每个区域里是什么字文本识别最后判断哪些字是目标词并计算出它们的精确屏幕坐标以供模拟点击。每一个环节都可能成为“翻车”现场。2. 技术栈全景不止于OCR的复合型解决方案要实现一个健壮的中文点选识别系统我们不能指望一个“银弹”算法而需要搭建一个技术栈。这个栈通常分为几个清晰的层次每一层都有其特定的技术选型和考量。2.1 图像预处理为识别扫清障碍原始图像往往并不“干净”。可能存在噪声、模糊、低对比度、复杂背景干扰等问题。预处理的目标就是提升图像质量让后续的识别步骤更容易。这里有几个关键操作灰度化与二值化这是最基础的步骤。将彩色图像转换为灰度图减少计算量。然后通过设定一个阈值将灰度图转为黑白二值图像让文字与背景彻底分离。这里的关键在于阈值的选择全局阈值如OTSU算法适用于背景均匀的图片但对于光照不均或背景复杂的验证码可能需要采用自适应阈值算法。去噪与滤波图像中可能存在椒盐噪声或高斯噪声。中值滤波器对去除椒盐噪声特别有效它能很好地保护边缘信息而高斯滤波器则常用于平滑图像、抑制高斯噪声。在实际处理验证码时我常常会先尝试中值滤波因为验证码的干扰线、点常常表现为椒盐噪声。形态学操作这是处理文字笔画断裂或粘连的利器。膨胀操作可以加粗笔画连接断裂的部分腐蚀操作可以细化笔画分离粘连的部分。通过开运算先腐蚀后膨胀可以去除小的白点噪声闭运算先膨胀后腐蚀可以填充小黑洞断裂。例如一个笔画纤细且带有断裂的“中”字经过适当的闭运算后可能会变成一个更完整的连通区域。注意预处理是一把双刃剑。过度处理如过强的滤波或形态学操作可能会扭曲文字形状反而降低识别率。通常需要根据具体的图片样本进行参数调优没有一套放之四海而皆准的参数。2.2 文本检测找到文字在哪里预处理后的图像我们需要定位所有文字出现的位置。这就是文本检测Text Detection模块的任务。近年来基于深度学习的检测模型已经全面超越了传统方法。传统方法连通域分析对于经过良好二值化的简单图片连通域分析Connected Component Analysis是一个轻量且有效的选择。它通过扫描图像将相邻的、具有相同像素值的区域标记为同一个连通域。每个连通域的外接矩形就可以视为一个候选的文字区域。这种方法速度快但对图像质量要求高如果文字与背景分离不彻底或者文字之间有粘连效果就会大打折扣。深度学习方法CTPN、EAST、DBNet对于背景复杂、文字排列不规则如弯曲、倾斜的图片深度学习模型是更好的选择。CTPNConnectionist Text Proposal Network它擅长检测水平文本行将文本行视为一系列宽度固定的细长文本提议框text proposal然后通过循环神经网络RNN将这些提议框连接成完整的文本行。在早期的验证码识别中应用较多。EASTEfficient and Accurate Scene Text Detector这是一个非常高效的模型它可以直接预测每个像素点到文本区域边界的距离几何图从而生成任意方向的旋转矩形框或四边形框。它的速度很快适合需要实时处理的场景。DBNetDifferentiable Binarization Network这是当前在自然场景文本检测中的SOTAState-of-the-Art方法之一。它的核心创新是提出了一个可微分二值化DB模块将二值化这一关键但不可微的步骤融入到训练过程中使得模型可以端到端地学习如何生成更精确的文本区域二值图检测精度非常高。在中文点选场景中文字通常是离散的词语或单字且可能散落在图片各处。因此一个能够输出独立、精确文本框的检测器如DBNet或改进版的EAST比一个输出文本行的检测器如CTPN更为合适。我的经验是对于互联网上常见的点选验证码使用在ICDAR等中文场景数据集上微调过的DBNet模型通常能取得非常好的检测效果。2.3 文本识别读懂文字是什么检测出文本框后我们需要裁剪出这些区域送入文本识别Text Recognition模型识别出具体的字符内容。CRNN CTC 经典组合在过去很长一段时间里CRNNConvolutional Recurrent Neural Network结合CTCConnectionist Temporal Classification损失函数是文本识别的标配。CNN部分如VGG、ResNet负责提取图像特征RNN部分通常是LSTM或BiLSTM负责对特征序列进行建模捕捉上下文信息最后CTC负责对齐不定长的特征序列和标签序列。这个组合对规整的印刷体文字识别效果很好。基于注意力机制的序列识别模型这类模型如Attn使用编码器-解码器Encoder-Decoder结构解码时通过注意力机制动态地关注编码特征的不同部分更适合处理长文本或带有复杂语言模型的场景。但对于点选验证码中的短词、单字其优势并不明显且训练更复杂。Vision Transformer (ViT) 系列近年来Transformer架构在视觉任务上大放异彩。对于文本识别有研究者将图像切块后送入Transformer进行编码然后接一个简单的解码头进行识别。这类模型在大规模数据上表现出强大的特征提取能力但模型参数量大推理速度相对较慢。实践选型建议对于中文点选识别我们面对的字库通常是有限的几百到几千个常用汉字且文字相对规整。一个在大型中文数据集如Chinese Text Recognition Benchmark上预训练并在自己业务相关的验证码样本上微调过的CRNN模型往往是性价比最高的选择。它平衡了精度、速度和部署复杂度。如果对精度有极致要求且计算资源充足可以考虑基于Transformer的模型。2.4 坐标映射与点击执行识别出目标文字后最后一步是将图像坐标转换为屏幕坐标并执行点击。这里有一个关键细节检测模型给出的坐标通常是相对于原始输入图片的。而我们的程序可能是通过浏览器驱动如Selenium或桌面自动化工具如PyAutoGUI来操作屏幕。坐标转换流程获取元素与截图首先需要定位到包含验证码图片的网页元素或屏幕区域。计算缩放比例验证码图片在显示时可能被CSS缩放或者我们截图时进行了缩放。需要计算出图片原始分辨率与当前显示尺寸之间的比例因子scale_factor。坐标映射将检测模型输出的文本框中心点坐标(x_img, y_img)乘以缩放比例得到相对于验证码容器元素左上角的坐标(x_local, y_local)。屏幕绝对坐标再获取验证码容器元素在屏幕上的绝对位置(elem_x, elem_y)最终的目标点击屏幕坐标为(elem_x x_local, elem_y y_local)。执行点击使用自动化工具如Selenium的ActionChains PyAutoGUI的click函数移动到该坐标并执行点击操作。这里需要模拟人类点击的轻微随机延迟和微小位置偏移以避免被反爬机制判定为机器行为。3. 实战构建一个基于深度学习的点选验证码破解示例下面我将以一个模拟的“点击图中所有‘苹果’”验证码为例勾勒一个完整的实战流程。请注意此示例仅用于技术交流与学习请严格遵守相关网站的服务条款不得用于恶意攻击或侵犯他人权益。3.1 环境准备与数据采集首先我们需要一个Python环境并安装必要的库。# 深度学习框架 pip install torch torchvision # 图像处理 pip install opencv-python pillow # 文本检测识别以PaddleOCR为例它集成了优秀的检测识别模型且易于使用 pip install paddlepaddle paddleocr # 自动化与网络请求 pip install selenium requests数据是模型的燃料。我们需要收集目标验证码的图片样本。可以通过编写脚本自动访问目标页面并保存验证码图片。同时需要人工或借助一些半自动工具为这些图片标注两部分信息检测标注每个文字区域的边界框坐标通常为四边形四点坐标或矩形框的左上右下坐标。识别标注每个边界框内的文本内容。标注格式可以参考COCO或VOC数据集格式。对于这个项目一个简单的自定义JSON格式可能更灵活{ image_name: captcha_001.jpg, annotations: [ {bbox: [x1, y1, x2, y2], text: 苹}, {bbox: [x3, y3, x4, y4], text: 果}, {bbox: [x5, y5, x6, y6], text: 香}, ... ] }3.2 模型训练与微调我们选择PaddleOCR作为基础工具链因为它提供了开箱即用的高质量中文检测DB和识别CRNN模型并且支持自定义数据训练。步骤一检测模型微调将收集到的图片和对应的标注文件按照PaddleOCR要求的格式组织通常是一个图片文件夹和一个标注文本文件每行记录图片路径和所有框的坐标、文字。使用PaddleOCR提供的配置文件如det_ch_db.yml修改其中的数据集路径、类别数文本检测通常为1类、训练轮次等参数。启动训练。由于我们有预训练模型微调fine-tuning通常只需要少量数据几百张和较少轮次几十epoch就能达到很好的效果。python tools/train.py -c configs/det/det_ch_db.yml -o Global.pretrained_model./pretrain_models/ch_det_db_resnet50_vd步骤二识别模型微调同样准备识别数据集。这里需要的是裁剪好的单字或词语图片以及对应的文本标签。使用PaddleOCR的识别配置如rec_chinese_common_train.yml修改字典文件ppocr/utils/ppocr_keys_v1.txt确保包含你验证码中出现的所有字符。启动识别模型训练或微调。python tools/train.py -c configs/rec/rec_chinese_common_train.yml -o Global.pretrained_model./pretrain_models/ch_rec_mv3_crnn3.3 集成推理与自动化脚本模型准备好后编写一个完整的推理脚本。以下是一个高度简化的核心逻辑框架import cv2 from paddleocr import PaddleOCR import numpy as np from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains import time class ClickCaptchaSolver: def __init__(self, det_model_dir, rec_model_dir): # 初始化PaddleOCR指定自定义模型路径 self.ocr PaddleOCR(det_model_dirdet_model_dir, rec_model_dirrec_model_dir, use_angle_clsFalse, # 如果不需文字方向分类可关闭 langch) def solve_from_image(self, image_path, target_words): 核心解决函数给定图片路径和目标词列表返回需要点击的坐标列表 img cv2.imread(image_path) result self.ocr.ocr(img, clsFalse) click_positions [] for line in result: for word_info in line: box word_info[0] # 文本框四个点坐标 text word_info[1][0] # 识别出的文本 confidence word_info[1][1] # 置信度 # 过滤低置信度结果 if confidence 0.7: continue # 判断是否为需要点击的目标词 if text in target_words: # 计算文本框的中心点坐标相对于图片 pts np.array(box, dtypenp.int32) center_x int(pts[:, 0].mean()) center_y int(pts[:, 1].mean()) click_positions.append((center_x, center_y, text)) print(f找到目标词 {text} 坐标 ({center_x}, {center_y}) 置信度 {confidence:.2f}) return click_positions def automate_click(self, url, captcha_element_selector, target_words): 自动化流程打开网页截图识别点击 driver webdriver.Chrome() driver.get(url) # 1. 定位验证码元素并截图 captcha_element driver.find_element_by_css_selector(captcha_element_selector) # 这里需要获取元素位置和尺寸并截图。简化起见假设已保存为captcha.png captcha_element.screenshot(captcha.png) # 2. 识别目标坐标 positions self.solve_from_image(captcha.png, target_words) # 3. 坐标转换与点击 # 获取验证码元素在页面中的位置 element_location captcha_element.location element_size captcha_element.size for (img_x, img_y, _) in positions: # 计算相对于元素左上角的坐标假设截图是原尺寸 # 注意如果截图有缩放这里需要乘以缩放因子 relative_x img_x relative_y img_y # 计算绝对屏幕坐标这里简化了实际需考虑滚动条等 absolute_x element_location[x] relative_x absolute_y element_location[y] relative_y # 使用ActionChains执行点击并加入人类化随机延迟和微小偏移 action ActionChains(driver) action.move_by_offset(absolute_x, absolute_y).pause(np.random.uniform(0.1, 0.3)).click().pause(np.random.uniform(0.2, 0.5)).perform() # 每次点击后需要将鼠标移回原点否则偏移会累积 action.reset_actions() driver.quit() if __name__ __main__: solver ClickCaptchaSolver(./custom_det_model/, ./custom_rec_model/) # 模拟解决一个验证码 target_words [苹果, 香蕉] # 假设需要点击这两个词 positions solver.solve_from_image(test_captcha.jpg, target_words) print(f需要点击的坐标{positions})4. 核心挑战与应对策略为什么你的模型总“翻车”在实际应用中直接套用上述流程很可能会遇到各种问题。下面是我在多个项目中总结出的核心挑战和应对策略。4.1 对抗性干扰验证码的“进化”验证码设计者会不断引入干扰增加机器识别的难度。常见的对抗手段包括1. 复杂背景与文字融合文字颜色与背景色相近或者背景有密集的干扰图案、线条穿过文字。应对策略强化预处理。尝试使用颜色分离技术如果验证码文字是固定颜色如红色可以提取红色通道或使用HSV色彩空间进行阈值分割。对于干扰线可以尝试使用形态学开运算或特定的图像修复算法如基于邻域的方法来尝试去除细线同时保留较粗的文字笔画。2. 字体扭曲与变形使用非常规字体或对文字进行随机旋转、缩放、波浪形扭曲、透视变换。应对策略数据增强是关键。在训练检测和识别模型时必须对训练样本施加同样甚至更强烈的空间变换增强如随机旋转-15°到15°、随机缩放0.8到1.2倍、弹性形变、透视变换等。这能极大地提升模型对形变的鲁棒性。识别模型方面CRNNCTC对形变有一定容忍度但更复杂的模型如结合空间变换网络STN可能效果更好。3. 重叠、粘连与断裂文字之间部分重叠或者单个文字的笔画出现粘连或断裂。应对策略这对检测器是巨大考验。DBNet这类基于分割的检测器通过预测文本区域和阈值图对粘连文本的处理能力优于基于回归的检测器。在识别阶段笔画断裂可能被误认为是两个字符。这时需要在识别后处理中结合语言模型即使是简单的字频统计进行纠错或者训练模型时加入大量笔画断裂的增强样本。4. 动态验证码文字位置随机出现或者每次刷新部分文字变化。应对策略这要求我们的系统必须是“零样本”或“少样本”的即模型要足够通用。除了使用大规模预训练模型外几乎没有捷径。对于位置随机我们的检测器本身就应该能处理任意位置。对于文字内容变化则要求识别模型的字符集覆盖足够广。4.2 坐标精度与点击反馈即使识别对了点不准或者点了没反应也是常事。坐标映射误差这是最常见的坑。浏览器缩放、CSS变换、iframe嵌套、页面滚动都会导致坐标计算错误。应对策略必须进行端到端的坐标验证。可以写一个调试脚本让程序在计算出坐标后先在屏幕上画一个红圈通过JavaScript在页面上叠加一个div或者用自动化工具截图后画圈再显示人工确认红圈是否精准套在目标文字上。确保映射逻辑100%正确。对于Selenium使用execute_script执行JavaScript来获取元素精确的视口位置和变换矩阵是更可靠的方法。点击行为模拟简单的element.click()或pyautogui.click(x, y)太像机器了。应对策略人类行为模拟。包括1) 移动轨迹使用贝塞尔曲线模拟人手鼠标移动而非直线移动。2) 随机延迟在移动和点击前后加入随机时间间隔。3) 点击偏差点击坐标不在正中心而是在目标区域内随机偏移几个像素。4) 点击力度有些前端会监听mousedown和mouseup的时间差来模拟“按压”感。可以使用ActionChains的click_and_hold和release方法并间隔一个随机时间。前端反爬监听网站可能监听WebDriver属性、异常快的点击速度、过于规律的点击模式。应对策略1) 使用stealth.min.js等脚本隐藏Selenium的自动化特征。2) 随机化操作流程比如偶尔先悬停再点击或者在点击目标前先随机移动鼠标到无关区域。3) 最重要的分析前端验证逻辑。有时点击后前端会生成一个token这个token是基于点击坐标的某种加密值提交给服务器的。这时就需要逆向JavaScript理解其加密算法直接生成token提交绕过点击步骤。这已进入更复杂的逆向工程领域。4.3 模型泛化与持续维护一个今天能用的模型明天可能就失效了因为验证码更新了。模型泛化能力不足在A网站训练的模型在B网站上效果很差。应对策略域适应Domain Adaptation与增量学习。收集新网站目标域的少量标注数据在原有模型源域训练的基础上进行微调。如果数据持续变化可以考虑建立持续学习Continual Learning的管道定期用新数据更新模型同时防止对旧数据的遗忘。建立数据闭环手动标注数据成本高昂。应对策略尝试半自动或自监督数据标注。例如用现有模型对未标注图片进行预测将高置信度的预测结果作为伪标签加入训练集。或者利用验证码本身的特点如果“点击提交”后的反馈成功/失败可以获取我们可以用强化学习的思想将成功通过的点击序列作为正样本反向优化模型。但这通常需要复杂的系统设计。成本与效率权衡高精度模型往往计算量大速度慢。应对策略模型轻量化与工程优化。将训练好的模型转换为更高效的推理格式如ONNX、TensorRT或使用Paddle Lite、MNN等移动端/边缘端推理引擎。对于检测模型可以尝试更轻量化的backbone如MobileNetV3替代ResNet。在架构上可以探索“检测识别”的一阶段端到端模型虽然设计复杂但可能减少中间过程开销。5. 超越验证码中文点选识别的更广阔天地当我们把目光从“对抗”验证码移开会发现中文点选识别技术在许多正向场景下大有可为。UI自动化测试在移动App或Web应用的自动化测试中经常需要根据屏幕上的文字内容来定位元素并操作。传统的通过ID、XPath定位的方式在动态内容或跨平台场景下非常脆弱。基于视觉的文字点选技术可以构建更稳定、更贴近真实用户操作的自动化脚本。例如测试脚本可以寻找“登录”按钮并点击无论它的位置或样式如何变化。图形化界面GUI自动化与辅助工具对于某些没有提供API或难以用传统方式访问的桌面软件可以通过OCR识别界面上的文字然后模拟点击进行操作实现自动化数据录入或报表生成。对于视障人士的辅助工具识别屏幕文字并转换为语音是核心功能而“点选”则是交互的延伸——用户听到“保存按钮”后可以通过语音或手势触发“点击”操作。文档智能处理与信息提取在扫描的PDF或图片格式的文档中如果需要提取特定栏目下的信息如合同中的“甲方”、“乙方”后面的名称可以先通过文本检测定位所有文字块识别后通过规则或简单的NLP找到目标关键词然后根据其相对位置提取关联信息。这比全文OCR后再进行文本分析在结构化信息提取上有时更精准。交互式数据标注平台在构建机器学习数据集时标注员需要点击图片中的特定物体或文字。平台可以集成预训练的检测识别模型自动预标注出所有文字区域和内容标注员只需要核对和修正或者直接点击确认能极大提升标注效率。从技术本质上看“中文点选识别”是将计算机视觉CV与具体交互任务相结合的一个典型范例。它要求我们不仅要有强大的感知能力看得准还要有精准的执行能力点得对更要能适应复杂多变的真实环境。解决这个问题的过程本身就是对CV工程化能力的一次全面锻炼——从数据采集、模型选型与训练、前后端集成到对抗性应对和系统维护。每一次“翻车”后的调试每一次为提升1%准确率而做的优化积累下来的都是宝贵的工程经验。