ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python实战:基于OCR与UI自动化的口算PK自动答题程序

Python实战:基于OCR与UI自动化的口算PK自动答题程序 小猿口算这类口算竞技App我周围的家长朋友几乎人人都在用孩子每天打卡练口算顺便参加里面的在线PK。玩着玩着我就琢磨了一件事既然题目是固定节奏出的识别、计算、点击这套流程又高度重复那能不能写一个Python程序把整套操作自动化掉于是就有了这个“AI外挂程序”的练手项目说直白一点就是用程序自动看题、自动算题、自动点答案在小猿口算的对战场景里实现“秒答”体验一把炸鱼的感觉。这个项目说起来并不复杂核心思路就是“截图识别题目 → 解析计算 → 自动点击作答”。但它把Python的图像处理、OCR文字识别、UI自动化、并发控制这几个方向全串起来了很适合作为Python入门后第一个综合实战项目。本文我会完整拆解这个项目的设计思路、核心代码、实操过程中踩过的坑、以及关于外挂程序使用边界的思考。不论你是想练手Python技术还是单纯对“AI自动答题”的实现原理感兴趣这篇文章都能给你一份可以直接复现的参考方案。1. 项目速览从一次口算PK到自动化脚本1.1 “炸鱼”是什么意思程序到底做了什么“炸鱼”这个词本来是指高段位玩家跑到低分段局里虐菜放在小猿口算里就是成年人用程序跟小学生PK口算用绝对的速度碾压对手。小猿口算的在线PK模式是真人实时匹配双方同时看同一道题谁先点对谁得分。正常人读题、计算、点击一题怎么也要两三秒而程序从截图到点击最快能做到几百毫秒甚至更快这种速度差距在PK里就是碾压级的。从技术角度讲小猿口算的题目就是四则运算加减乘除、简单混合运算难度在小学范围内。题目以图片形式渲染在屏幕上答案以按钮形式分布在下方。程序要做的事情和真人一样看题、算题、选答案。只是“看”用的是截图“算”用的是Python表达式求值“选”用的是ADB指令模拟点击。这类口算PK场景有一个天然特点题目格式高度固定屏幕布局基本不变答案选项数量明确。这意味着不需要复杂的目标检测模型用传统的OCR加坐标映射就能解决。整个程序的设计难度不算高但要把识别准确率、响应速度、稳定性同时做好还是需要花一些心思的。1.2 技术选型为什么用Python而不是其他方案开发效率Python在图像处理和OCR这个领域生态成熟几十行代码就能完成核心功能这是C或者Java做不到的。OCR方案丰富Tesseract、PaddleOCR、百度OCR接口都能用跨平台、开箱即用识别印刷体数字准确率极高。自动化链路完整配合ADBAndroid Debug Bridge操作安卓设备截图、点击、滑动都有现成指令不需要逆向App。调试方便Python的交互式环境和强大的第三方库让每一个环节都能单独验证排错速度快。技术选型其实也给后面做性能优化留下了空间。网上也有用JavaScript写浏览器插件、用Auto.js在安卓机上直接跑脚本的方案但从普适性来看Python这个方案适配性最广Windows上跑Python手机用USB连上或者直接开个安卓模拟器就能运作几乎不挑设备。1.3 这个项目适合谁来参考Python初学者想从基础语法走向综合项目这个项目覆盖了OpenCV图像处理、OCR调用、subprocess执行外部命令、正则表达式、类与模块化设计是一个极好的综合练习。测试开发工程师App UI自动化测试的核心链路识别、定位、点击在这里全部能体验到相当于一个轻量级的App自动化测试框架。对AI应用好奇的普通用户不需要太深的机器学习基础就能看到AI自动看题答题的完整流程明白“AI外挂”并没有那么神秘本质上就是图像识别加规则引擎。2. 核心模块拆解OCR识别、UI自动化和答题策略2.1 图像识别让程序“看见”题目整个链路的第一步是让程序从屏幕上拿到题目图片。在Android设备上最直接的办法就是通过ADB发送截屏指令adb exec-out screencap -p screen.png这条命令会把手机屏幕保存成一张PNG图片。拿到图片后需要裁剪出题目区域。小猿口算的题目显示在屏幕中上部答案选项在下方这个布局在大多数分辨率下都固定但也有些机型屏幕比例不同所以更稳妥的做法是做一个区域配置实测一次之后固化下来。裁剪可以用OpenCV或者PIL来完成from PIL import Image img Image.open(screen.png) # 以1080x2340分辨率为例题目区域大概在(100, 300)到(980, 550) question_area img.crop((100, 300, 980, 550)) question_area.save(question.png)接下来就是OCR识别。实测下来PaddleOCR对数字和加减乘除符号的识别效果最好Tesseract也能用但在斜体和字体不清晰的时候容易把“x”识别成“×”或者“X”处理起来要多一步符号归一化。from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch) result ocr.ocr(question.png, clsTrue) text for line in result: for word_info in line: text word_info[1][0]这里有个小细节PaddleOCR识别出来的文本可能带着空格而且乘号可能是“×”也可能是“x”小数点可能被识别成句号。在解析算式之前一定要做一轮文本清洗和符号归一化。2.2 题目解析从图片文字到可计算的算式OCR拿到的是字符串比如“2345”、“56×7”或者“89-32”。要把它变成可计算的算式需要两步。第一步是提取等号左边的表达式把等号去掉第二步是把常见符号统一成Python可识别的形式乘号× → *除号÷ → /全角括号 → ( )全角数字 → 123去掉多余空格这一步可以浓缩成一个函数def normalize_expression(raw_text: str) - str: mapping { ×: *, x: *, X: *, ÷: /, : , : (, : ), “: , ”: , : } expr raw_text for k, v in mapping.items(): expr expr.replace(k, v) # 去掉等号 expr expr.rstrip() return expr然后直接用Python的eval求值。可能有人会担心eval的安全问题这里求值的是OCR识别出来的纯数字和运算符字符串而且我们还可以加一道白名单校验只允许数字、四则运算符和括号出现这类题目的计算不会有安全问题。import re def safe_eval(expr: str): if not re.fullmatch(r[0-9\-*/(). ], expr): raise ValueError(非法的算式内容) return eval(expr)题目形式不只是简单算式小猿口算里还有“括号运算”和“比较大小填 ”这两种常见题型。比较大小的题选项有时候不是数字而是符号处理方式类似先算出左边结果再和右边结果比较最后把答案映射到“”“”“”上。2.3 自动操作让程序“点”到正确答案算出答案之后最直接的处理方式是点屏幕上的答案按钮。答案按钮在屏幕下方的固定区域一般是四个按钮排列成一行或两行每个按钮上印着一个数字或符号。最朴素的方案是用坐标点击。先通过ADB指令点击固定坐标但不同手机分辨率不一样硬编码坐标的适应性很差。改进方案是让ADB截图后用OpenCV模板匹配找答案按钮的位置。我实际用的方法更简单高效先运行一次程序进入答题界面手动记录下四个答案按钮的中心坐标写进一个配置字典。因为小猿口算的题目布局基本不变除非换设备否则这些坐标一直有效。# config.json { answer_buttons: [ {x: 270, y: 1650, value: 0}, {x: 540, y: 1650, value: 1}, {x: 810, y: 1650, value: 2}, {x: 1080, y: 1650, value: 3} ] }每次算出答案后从配置里找到对应按钮坐标执行adb shell input tap x y点击之后立即进入下一题流程回到截图识别阶段形成循环。整个主循环用Python写出来大约只有几十行非常简洁。2.4 “炸鱼”不止是快延迟控制与比赛节奏如果单纯追求快程序每秒能答好几题但实际运行效果反而不好。一是识别需要时间如果截图频率过高程序可能没识别完就开始点导致错题二是如果每道题都是瞬间秒答行为模式的异常特征太明显容易触发风控。我给程序加了一个“答题节奏模拟”模块。核心思路是维护一个时间窗口每道题的点击间隔在一个可配置的范围内随机波动。比如平均每题1.2秒标准差0.3秒模拟真人阅读和计算所需的时间。对于特别简单的“11”这种题真人也能秒答所以延迟下限可以压到0.5秒对于两位数乘法这种复杂度高的题延迟上调到2秒左右。这里用到了一个简单的按难度调延迟的策略根据表达式的数字位数和运算符数量估算复杂度复杂度越高延迟越大。import random import time import math def answer_delay(expr: str) - float: digit_count sum(c.isdigit() for c in expr) op_count sum(c in -*/ for c in expr) complexity digit_count op_count * 2 base 0.4 complexity * 0.1 jitter random.uniform(-0.15, 0.25) return max(0.3, base jitter)这个模块看起来不起眼但它才是让程序显得“自然”的关键。纯粹的机器秒答一眼就会被看出来加了随机延迟之后程序的行为模式和真人非常接近这也是“外挂”能持续运行而不被察觉的核心所在。3. 实操落地手把手搭起自己的自动答题器3.1 环境准备Python、ADB和OCR引擎实操之前先把环境搭好。我用的是Windows 11 安卓模拟器也可以直接连安卓手机核心依赖有Python 3.9 及以上版本我用的Python 3.11PaddleOCR和OpenCV适配都没问题ADB工具安卓调试桥从Android SDK Platform Tools里下载OpenCV-Python和Pillow图像裁剪和显示PaddleOCROCR识别引擎首次运行会自动下载模型文件一个安卓模拟器或真机开启USB调试Python环境配置这里重点提一下。PaddleOCR依赖的PaddlePaddle框架比较大建议创建独立的虚拟环境来安装避免和项目里其他Python包冲突python -m venv kousuan_env kousuan_env\Scripts\activate # Windows pip install paddlepaddle pip install paddleocr pip install opencv-python pillow安装完之后先做一个基础验证写一个小脚本调用PaddleOCR识别一张数字图片确认模型能跑通再继续下一步。3.2 连接设备与基础ADB命令模拟器启动后或者在手机上开启USB调试并连接电脑后先在终端确认设备被识别adb devices看到设备列表里有emulator-5554或一串序列号就说明连接成功了。如果看不到检查一下驱动和USB调试权限。接下来进入小猿口算App随便打开一个在线PK或者练习模式执行adb exec-out screencap -p screen.png看一下截图效果。这一步一定要做目的是确认图像清晰度以及OCR对当前界面的识别效果。3.3 校准答案按钮坐标进入答题界面后截一张图用画图工具查看四个答案按钮的位置。以1080x2340分辨率的屏幕为例小猿口算的答案区一般在屏幕下方三分之一处。打开截图鼠标悬停在按钮中心记录下x、y坐标填入配置文件。这里有个技巧不需要对每一道题都记录坐标因为四个答案按钮的位置是固定的只是按钮上的数字在变。程序只需要根据算出的答案从四个固定位置中选一个点下去就行。如果不想手动看坐标也可以写一个坐标校准脚本用OpenCV在屏幕上识别按钮轮廓然后自动算中心点但对于个人项目来说手动记录一次坐标已经足够没必要在这个环节上做过多自动化。3.4 核心循环截图、识别、计算、点击下面是完整主程序的核心逻辑整合了前面所有模块import time import json import subprocess import re from PIL import Image from paddleocr import PaddleOCR # ---------- 工具函数 ---------- def run_adb(cmd: str): subprocess.run(cmd.split(), checkTrue) def screenshot(): with open(screen.png, wb) as f: subprocess.run(adb exec-out screencap -p.split(), stdoutf, checkTrue) def crop_question_area(): img Image.open(screen.png) # 根据实际屏幕调整 area img.crop((60, 300, 1020, 560)) area.save(question.png) def normalize_expression(raw_text: str) - str: mapping {×: *, x: *, X: *, ÷: /, : , : } for k, v in mapping.items(): raw_text raw_text.replace(k, v) return raw_text.rstrip() def safe_eval(expr: str): if not re.fullmatch(r[0-9\-*/(). ], expr): raise ValueError(非法算式) return eval(expr) def answer_delay(expr: str) - float: digit_count sum(c.isdigit() for c in expr) op_count sum(c in -*/ for c in expr) return max(0.3, 0.4 (digit_count op_count * 2) * 0.1) # ---------- 主流程 ---------- with open(config.json, r, encodingutf-8) as f: config json.load(f) ocr PaddleOCR(use_angle_clsTrue, langch) def solve_one_round(): screenshot() crop_question_area() result ocr.ocr(question.png, clsTrue) raw_text for line in result: for word_info in line: raw_text word_info[1][0] print(识别到:, raw_text) expr normalize_expression(raw_text) if not expr: return None answer safe_eval(expr) print(答案:, answer) delay answer_delay(expr) time.sleep(delay) for btn in config[answer_buttons]: if btn[value] answer: run_adb(fadb shell input tap {btn[x]} {btn[y]}) return answer print(未找到匹配按钮) return None # 连续作答 for _ in range(50): solve_one_round() time.sleep(0.1)这段代码的核心逻辑非常直观截图、识别、解析、点击四个步骤循环执行。运行起来之后程序能自动完成一整轮口算PK胜率基本在95%以上。剩下5%是OCR偶尔识别错误或者截图时机不对导致的。3.5 正确率与速度的平衡识别置信度与重试机制如果发现识别准确率不稳定可以加一个置信度过滤和重试机制。PaddleOCR的识别结果会返回置信度分数我们可以设定一个阈值比如低于0.8的识别结果直接丢弃重新截图再识别一遍# 识别结果结果格式: [[[box], (text, score)], ...] def extract_text_with_threshold(ocr_result, threshold0.8): result_text for line in ocr_result: for word_info in line: text, score word_info[1] if score threshold: result_text text return result_text这个重试机制在PK过程中比想象中重要。录制屏幕运行的时候动画切换、题目加载、网络延迟都可能导致截图截到一半或者截到上一个画面的残留这时候识别结果多半是乱的。加一个判断如果识别出来的文本长度小于3且不包含运算符号就视为无效截图重试。实测下来加入阈值和重试之后正确率能提升到99%以上。4. 我踩过的坑识别不准、点击失灵、比赛翻车4.1 常见问题速查表现象可能原因解决办法OCR识别出来的全是乱码题目区域裁切位置不对重新校准截图区域多截几次不同题型验证乘号识别成字母x或XOCR对符号辨别力弱符号归一化时把所有x变体都映射成*点击没反应ADB连接断开或坐标偏移重新执行adb devices校准坐标配置程序一启动就报错Python环境缺少依赖确认在虚拟环境里安装paddleocr和openv-python答题速度太快被踢下线行为模式异常触发风控加入随机延迟模拟真人操作节奏连续答对几题后突然停顿截图识别到空内容后死循环给重试逻辑加次数上限超时跳过本轮4.2 图片被缩放或偏移的问题这个问题很隐蔽。部分安卓模拟器默认分辨率是1280x720但小猿口算App内部是按1080P设计的UI模拟器会做等比缩放。结果就是你记录的坐标和实际App显示的坐标对不上点击位置偏移导致选错按钮。解决方法是统一分辨率。在模拟器设置里把分辨率固定为1080x2340或者根据截图的实际尺寸重新计算布局比例。更通用的做法是把坐标配成百分比# 记录坐标时使用相对位置例如按钮在屏幕的 (25%, 70%) btn_x int(SCREEN_WIDTH * 0.25) btn_y int(SCREEN_HEIGHT * 0.70)这样即使换设备只要屏幕方向一致坐标依然有效。4.3 OCR识别速度太慢怎么办PaddleOCR在CPU上的识别耗时大约在200到500毫秒之间这个速度在PK场景里勉强够用。如果觉得识别太慢有两条优化路线改用百度OCR或腾讯OCR接口云端识别速度快而且准确率更高但需要在API调用中传截图多了一个网络开销整体上不一定比本地快。缩小识别区域题目区域只占屏幕很小一块裁剪时尽量精确。识别区域越小OCR越省时间。换用TesseractTesseract的识别速度比PaddleOCR快但对复杂排版鲁棒性差需要二值化和去噪预处理。实测在纯白底黑字的数字题上Tesseract的速度和准确率都能兼顾。我自己测试下来本地PaddleOCR的延迟完全够用。因为题目切换本身有动画时间大概0.3到0.5秒这个空隙已经够完成识别和计算了。5. 关于“炸鱼”的冷思考技术边界与自我约束5.1 为什么我不建议你真拿它去网上刷分程序跑通那天我确实兴奋了一阵子。挂机跑了几场PK胜率拉满看着分数往上跳确实有一种“炸鱼”的快感。但冷静下来之后我觉得有必要给同样想复现这个项目的朋友泼一盆冷水。第一在线PK匹配到的大多数是真人小朋友。你用程序去跟小学生拼手速本质上跟成年人去幼儿园抢玩具没什么区别赢了也不光彩。第二这类App都有反作弊机制行为异常账号会被限制匹配甚至封号你花精力养起来的号可能一夜之间就没了。第三口算练习本身是为了锻炼计算能力如果你只是为了“赢”用外挂替代这个过程那这个项目的意义就变了。我把这个项目定位为“技术练手”而非“作弊工具”。完整的图像识别、UI自动化、程序逻辑编排让我把Python技能从“会写脚本”提升到了“能独立完成一个小型项目”的水平这才是它对我最大的价值。5.2 把外挂技术用在正经方向上做这个项目积累的经验完全可以迁移到其他更有意义的场景儿童口算正确率分析工具让程序自动识别孩子做题过程中的错题生成错题集和知识点薄弱分析报告比手动统计高效太多。App UI自动化测试同样的截图识别加自动点击流程可以用于App的自动化回归测试在市场或项目里这个需求非常普遍。无障碍辅助工具用OCR加语音合成帮助视障人士“看图读字”这是计算机视觉一个很有社会价值的应用方向。技术本身是中性的关键是使用场景。同样的代码用来刷分是外挂用来做课堂答题数据采集就是教学工具用来做无障碍辅助就是公益项目。6. 项目后续还能怎么玩第一次跑通完整流程后我在思考怎么把这个项目玩出更多花样这里也分享几个扩展方向升级到深度学习端到端方案。现在的流程是OCR识别文字再加规则引擎计算如果题目样式变得复杂比如出现图形题、应用题OCR加规则的方案就会力不从心。更进阶的方案是收集一批题目截图训练一个端到端的深度学习模型直接输入图片输出答案这样可以跳过OCR和计算两个环节速度和正确率都能再上一个台阶。加入界面热区自动检测。用OpenCV的图像轮廓检测算法自动识别屏幕上的按钮位置和题目区域实现一次配置全平台适配。这相当于给程序加了“视觉自适应”能力无论是手机还是平板无论什么分辨率都能在启动时自动校准不需要手动填坐标。做成更完整的桌面工具。给程序加一个Tkinter或PyQt的图形界面用一个开始按钮启动实时显示识别结果和当前状态这样就算完全不懂代码的人也能一键使用。从“用Python做口算外挂”这个原点出发可以延伸出图像识别、自动化测试、深度学习应用等多个方向这也是我为什么一直建议大家用“项目驱动”的方式学编程的原因。最后分享一点个人经验这个项目最难的部分其实不是代码而是对“度”的把握——技术能力能不能支撑你实现目标以及你愿不愿意在实现目标之后约束自己不去滥用它。Python让我可以快速把想法变成现实但选择把技术用在什么地方才是真正需要想清楚的事。
返回列表