☰
基于OCR的屏幕价格识别与自动点击脚本实战
2026/10/3 15:19:26 网站建设 项目流程

做这个项目之前,我其实已经被“人工盯价格”这件事折磨很久了。手动刷新页面、反复盯数字、等到符合心理价位再赶紧点购买,这一套流程不仅枯燥,而且特别容易手慢。后来我想,既然价格是屏幕上的文字,自动点击又不过是在固定位置模拟鼠标,那为什么不用 OCR 把价格读出来,再用脚本去点?于是就有了这个代号“三角洲”的自动购买工具:屏幕截图、OCR 文字识别、价格解析、自动点击,四个环节串成一条流水线。项目本身是一个桌面自动化练习,但思路完全可以迁移到自动签到、数据录入、UI 自动化测试等场景。如果你正想学 OCR 落地,或者需要写一个能“看见文字再操作”的脚本,这篇分享应该能帮你省不少弯路。

1. 项目整体设计:为什么是“OCR + 自动点击”而不是别的方案

动手之前最难的不是写代码,而是想清楚技术路线。市面上做自动点击的思路不少,按键精灵能录制鼠标轨迹,Windows 自动化可以用 UIA 抓控件,甚至某些付费工具自带“找图”“找色”功能。可一旦目标界面里的文字是动态变化的,比如实时刷新的价格数字,前面这些方案都会乏力。“找固定图片”匹配不了变化的字符,“录制固定轨迹”只要界面偏移一次就全废。

所以核心思路锁定为:不关心按钮长什么样,不关心价格出现在第几行,只关心“屏幕上某个区域里有没有出现满足条件的价格文本”。这套逻辑拆成四个模块:

  • 截屏模块:定时抓取目标区域的屏幕图像
  • 预处理模块:裁切、放大、灰度化,让 OCR 更容易识别
  • OCR 识别模块:把图像中的文字转换成字符串
  • 决策与点击模块:解析价格,对比阈值条件,决定是否模拟点击

这个方案的优点是抗界面变化能力强,价格字号变了、颜色变了、背景图换了,只要人眼能看清,OCR 大多也能识别;缺点是整体链路比单纯模拟点击长,每一步都有丢精度的可能。用术语说,这是一个“感知-决策-执行”的闭环:感知层靠 OCR,决策层靠代码逻辑,执行层靠自动点击。任何一个环节出错,购买动作都可能误触发或者漏触发。

1.1 需求拆解:价格识别真正要解决的三件事

表面上看,价格识别就是“把图片里的数字变成字符串”。但实际做下来,难点有三个,比想象中麻烦得多。

第一,价格文本的上下文干扰。屏幕上往往不止一个数字,有库存量、商品编号、倒计时,还可能有一堆“第几件”“打几折”之类的小字。如果不先定位到价格区域,直接把整屏扔给 OCR,识别结果会乱成一锅粥。所以要先裁切,把范围限定在“价格大概率出现”的那块矩形里。

第二,价格的格式问题。有的界面显示“¥ 1,299.00”,有的显示“1299”,有的用全角符号,还有的带千分位逗号、货币缩写。OCR 识别出来的字符串往往是不干净的,比如“¥1,299.00”可能被识别成“¥1,299.00”也可能漏掉点号变成“1299.00”,更糟糕的是逗号和句号会被误判。解析时必须写正则、做清洗,统一转成浮点数。

第三,识别置信度问题。任何 OCR 引擎都不可能 100% 准确,尤其当背景复杂、字体过小、光线不均的时候。如果直接把置信度低的识别结果拿来做购买判断,一旦把“1299”看成“1999”,判断逻辑就完全错了。所以判断逻辑里必须加入容错和二次确认机制,不能识别一次就点。

这三件事,决定了整个项目的代码结构不能只写一个 OCR 调用就完事,必须把“区域定位”“文本清洗”“置信度校验”都做成独立函数。

1.2 技术选型:PaddleOCR、Tesseract、iTextSharp 与按键精灵怎么挑

我一开始列了四个候选方案:PaddleOCR、Tesseract、C# 环境的 iTextSharp、按键精灵本地 OCR。为什么要比较这些?因为网上教程太多,每个都说自己快,实际一跑就翻车。

PaddleOCR 是目前中文场景下最省心的开源方案。它对中文、数字、特殊符号的支持都比较好,模型体积可选,部署也不算复杂,CPU 环境也能跑。我最后选它作为主力引擎。Tesseract 是传统老牌 OCR,优点是轻量、跨平台,但中文识别率确实一般,而且对简单数字场景有时反而会因为“太聪明”而认错。iTextSharp 实际上是一个 PDF 操作库,OCR 只是它的边缘功能,适合从 PDF 里抽取文字,不适合实时截屏识别,如果你用它来抓屏幕价格,等于拿错工具干活。按键精灵的本地 OCR 思路其实和我这个项目类似,也是“识别文字再点击”,但它的脚本语言偏封闭,图像处理和模型升级都受限,适合快速做小脚本,不适合做复杂逻辑。

选型的时候我给自己定了几条标准:能离线跑、中文数字识别准、社区资料多、可以自定义模型。按这个标准,PaddleOCR 几乎没有对手。如果你预算很敏感,也可以先用 Tesseract 做原型,但要做好心理准备,识别率的调优成本会高出不少。

1.3 工作流程总览:一个完整闭环如何串起来

整个工具的运行逻辑并不复杂,我用一个无限循环来描述:程序启动后,先进入初始化阶段,加载 OCR 模型、读取配置文件;然后进入主循环,第一步截屏,第二步预处理,第三步 OCR 识别,第四步解析价格,第五步判断是否满足购买条件,满足则执行点击,不满足则等待几秒再进入下一轮。

用伪代码写就是:

while True: screenshot = capture_screen(region) processed = preprocess(screenshot) text = ocr_recognize(processed) price = parse_price(text) if price is not None and price <= target_price: click_purchase_button() time.sleep(cooldown) time.sleep(interval)

循环本身不复杂,真正的工程难点在于每一步的稳定性。截屏可能截到遮挡,预处理可能把数字搞糊,OCR 可能认错,解析可能碰到异常字符,点击时可能鼠标被占用。所以代码里每步都要有异常处理,把状态日志打出来。调试的时候,我最常做的事就是盯着日志看它到底卡在哪一步。

2. 核心细节解析与实操要点

方案定下来之后,就是一步步抠细节。这个项目里没有哪一步是可以“随便写写就行的”,尤其 OCR 识别之前的图像处理,几乎决定了整个项目的成功率。很多新手一上来就调模型参数,其实问题往往出在图像质量上。

2.1 OCR 引擎部署:PaddleOCR 的安装与环境坑

PaddleOCR 的安装门槛不算高,但坑确实多。官方推荐用 Python 3.7 到 3.10,虚拟环境建议单独建一个,不要直接装到系统 Python 里。安装分两步,先装 PaddlePaddle 框架,再装 PaddleOCR 包。

CPU 环境安装比较简单:

python -m pip install paddlepaddle==2.5.2 -i https://mirror.baidu.com/pypi/simple pip install paddleocr==2.7.0

如果机器有 NVIDIA 显卡,想用 GPU 加速,可以去 PaddlePaddle 官网按 CUDA 版本选对应的安装命令。不过我这个项目里 OCR 识别区域很小,CPU 推理一张截图也就几百毫秒,所以 GPU 不是必须的。

有个特别常见的坑是“OCR could not create a primitive ...”。这个报错在部分旧版本 PaddleOCR 或者依赖冲突时会出现,通常和 GPU 显存不足、Xbyak 库加载失败有关。CPU 环境遇到这个问题,多半是模型推理库初始化失败,最简单的解决办法是把 paddlepaddle-gpu 卸掉,改装纯 CPU 版。如果是显存不足,可以调低 batch_size 和 det_limit_side_len,或者干脆把识别区域裁得小一点。

另一个常见问题是 VS2017 环境下编译报错。PaddlePaddle 官方发布的是预编译包,正常情况下不需要编译源码,但如果你的 Python 版本太新,找不到对应的 wheel,就会尝试从源码编译,这时候会要求完整的 VC++ 工具链。建议直接新建 Python 3.9 虚拟环境,别和高版本 Python 死磕。

2.2 图像预处理:放大、灰度、二值化的经验参数

OCR 对图像质量极其敏感。同一个价格数字,原图直接识别可能报“no text detected”,放大两倍再灰度化之后就能轻松识别。我在项目里总结了一套比较稳定的预处理流程,按顺序做:

先把截图裁切到价格区域,这个区域在配置里写死坐标。然后对区域做缩放,目标是把价格数字的高度放大到 40 像素左右。比如原图价格字高只有 16 像素,那就把图像放大 2.5 倍。接着转灰度,因为颜色信息对 OCR 没有帮助,还会引入干扰。再之后做二值化,把背景变白、文字变黑,减少阴影和噪点的影响。最后做一次轻微的中值滤波,去处椒盐噪点。

处理时这套代码可以作为参考:

import cv2 def preprocess_image(crop_img, scale=2.5): img = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY) img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC) _, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) img = cv2.medianBlur(img, 3) return img

有一点要提醒:二值化不是越极端越好。如果背景是浅色、字体是深色,用 Otsu 自适应阈值通常没问题;如果界面有渐变背景或者半透明遮罩,强制二值化会把文字一并涂掉。遇到这种情况,可以先不二值化,只做灰度和放大,效果反而更好。所以预处理千万不能做成“一根筋”,最好预留一个参数开关,遇到识别率低时切换策略。

2.3 价格文本解析:对付千分位、逗号和 OCR 错的实战写法

OCR 识别出来的文本不可能是干净的“1299”或“199.9”。常态是“¥ 1,299.00”被识别成“¥1,299.00”或者“1,299.00”,有时候逗号会变成句号,有时候小数点直接消失,最头疼的是“0”和“O”、“1”和“l”混淆。所以解析函数必须有很强的清洗能力。

我的解析思路分四步:先移除货币符号和空格,比如“¥”、“$”、“CNY”;再用正则把千分位逗号去掉,但要注意别把小数点前的千分位和小数点本身搞混;接着统一小数点和全角数字,把中文全角数字转成半角;最后用 float 转换并捕获异常。

代码写出来大致是这样:

import re def parse_price(text): if not text: return None # 去掉货币符号和相关字符 text = re.sub(r'[¥¥$€,\s]', '', text) # 全角数字转半角 text = text.replace(':', ':').replace('.', '.') text = text.replace('0', '0').replace('1', '1').replace('2', '2') text = text.replace('3', '3').replace('4', '4').replace('5', '5') text = text.replace('6', '6').replace('7', '7').replace('8', '8') text = text.replace('9', '9') # 提取浮点数 match = re.search(r'\d+\.?\d{0,2}', text) if match: try: return float(match.group()) except ValueError: return None return None

这个解析逻辑看着简单,但实际调参时我发现一个细节:OCR 偶尔会把“1299”识别成“1299.0”或者“1299.OO”,用\d+\.?\d{0,2}这个正则能匹配“1299”,也能匹配“1299.0”,但匹配不了“1.299”这种点号加数字的形式。所以如果目标界面的价格喜欢用“1.299”而非“1299”,解析函数需要额外处理:检测到点号后去掉它,再当成整数处理。这就是那种“真正跑起来才发现的坑”,文档里永远教不到。

2.4 自动点击:坐标怎么定位才能不点歪

价格识别出来之后,接下来就是点击购买按钮。这里最忌讳的是直接把按钮坐标写死,因为窗口在屏幕上移动、分辨率变化、DPI 缩放都会导致坐标失效。我把坐标分为两种:固定偏移坐标和动态识别坐标。

固定偏移坐标适用于按钮位置相对价格区域恒定的情况,比如价格在中间、按钮在价格下方 80 像素处。这种方案最简单,只要在配置文件里写“按钮相对价格中心的偏移量”就行。动态识别坐标则是用模板匹配或者二次 OCR 找到按钮文字,然后点击对应位置,这种方法更稳,但识别成本更高。

在实际项目中,我采取了折中方案:先定位价格区域中心点,然后按配置里的偏移量计算按钮位置。代码用 pyautogui 实现:

import pyautogui def click_purchase(center_x, center_y, offset_x, offset_y): # 从价格中心偏移到购买按钮 btn_x = center_x + offset_x btn_y = center_y + offset_y pyautogui.moveTo(btn_x, btn_y, duration=0.2) pyautogui.click()

点击前我还加了一道保险:在按钮位置附近小范围移动鼠标,避免快速连点造成系统识别为异常操作。点击之后立刻进入冷却期,防止程序因为界面跳转而重复点击。这里的间隔不能太短,我一般设置 3 秒到 5 秒,给页面刷新留出时间。

3. 实操过程:从搭建环境到跑通自动购买

很多教程喜欢把环境准备一笔带过,但这些往往是新手最容易卡住的地方。我把自己完整跑通的过程记录下来,你照着做,大概率能一次成功。

3.1 环境准备与依赖安装

我先创建一个干净的虚拟环境,推荐用 Python 3.9,因为 PaddlePaddle 对它的支持最稳。命令行执行:

python -m venv delta_env delta_env\Scripts\activate # Windows 系统 # Linux/Mac 使用: source delta_env/bin/activate

激活环境后,先升级 pip 再装依赖:

python -m pip install --upgrade pip pip install paddlepaddle==2.5.2 pip install paddleocr==2.7.0 pip install opencv-python pillow numpy pyautogui

安装过程如果遇到网络慢,可以临时把 pip 源切换到国内镜像。装完之后先做一个最简验证,确认 PaddleOCR 能正常加载:

from paddleocr import PaddleOCR ocr = PaddleOCR(use_angle_cls=True, lang='ch', show_log=False) print("OCR engine loaded")

如果这一步没报错,说明环境基本没问题。如果报“no module named 'paddle'”,说明 PaddlePaddle 没装成功,得重新检查 Python 版本和 pip 命令。

3.2 截图与价格区域定位

截屏我用的是 pyautogui 自带接口,它能截取整个屏幕,也能只截取指定区域。首先要确定价格区域坐标,我建议先打开目标界面,用截图工具查看价格文字左上角和右下角的像素坐标,然后填进配置文件。

举个例子,如果价格显示在屏幕坐标 (800, 450) 到 (1000, 500) 之间,配置文件里就写:

{ "price_region": [800, 450, 200, 50], "target_price": 1500.0, "purchase_offset": [0, 80], "interval_seconds": 2, "cooldown_seconds": 5 }

这里的price_region四个数字是 x、y、width、height。截屏代码很简单:

import pyautogui def capture_screen(region): x, y, w, h = region img = pyautogui.screenshot(region=(x, y, w, h)) return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)

有一点必须注意:如果你的 Windows 开了缩放(比如显示缩放 125%),pyautogui 截图的坐标和实际逻辑坐标会不一致。最简单的解决办法是让程序运行时把缩放设为 100%,或者在代码里乘以缩放系数。

3.3 识别、解析与判断购买条件的完整代码

我把核心流程封装成一个类,方便调试和扩展。下面这段代码是核心逻辑的简化版,但可以跑通整个闭环:

import time import cv2 import numpy as np import pyautogui from paddleocr import PaddleOCR class DeltaAutoBuyer: def __init__(self, config): self.region = config["price_region"] self.target_price = config["target_price"] self.offset = config["purchase_offset"] self.interval = config["interval_seconds"] self.cooldown = config["cooldown_seconds"] self.ocr = PaddleOCR(use_angle_cls=True, lang='ch', show_log=False) def preprocess(self, img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray = cv2.resize(gray, None, fx=2.5, fy=2.5, interpolation=cv2.INTER_CUBIC) _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) return cv2.medianBlur(thresh, 3) def recognize_price(self, img): processed = self.preprocess(img) result = self.ocr.ocr(processed, cls=True) if not result or not result[0]: return None text = " ".join([line[1][0] for line in result[0]]) return self.parse_price(text) def parse_price(self, text): # 前面写的解析函数,略 pass def run(self): print("开始监控价格...") while True: try: img = self.capture_screen(self.region) price = self.recognize_price(img) print(f"识别到的价格: {price}") if price is not None and price <= self.target_price: print("价格满足条件,准备点击...") center_x = self.region[0] + self.region[2] // 2 center_y = self.region[1] + self.region[3] // 2 btn_x = center_x + self.offset[0] btn_y = center_y + self.offset[1] pyautogui.click(btn_x, btn_y) time.sleep(self.cooldown) except Exception as e: print(f"运行异常: {e}") time.sleep(self.interval)

实际使用中,识别和点击之间我还加了一个“事后确认”流程:点击后立刻再截一次图,重新识别价格,如果发现价格已经不是目标区间,说明点击成功;如果价格没变,可能是点偏了,需要把鼠标移动到按钮中心重试。这一步对防止误点很有帮助。

3.4 运行效果与调试记录

我拿一个模拟界面做了多次测试,设置目标价格为 1500,价格区域固定在屏幕某块区域,每次刷新后随机生成 800 到 2000 之间的价格。跑下来整体识别成功率能到 95% 左右,大多数失败集中在背景特别复杂、价格数字太小的场景。

有一次遇到特别奇葩的情况:界面在价格旁边有个旋转的“加载”图标,图标扫过的瞬间把价格数字遮住了一半,OCR 直接把“1499”识别成了“199”。输出日志里价格跳变很大,我当时没注意,差点触发一次错误购买。后来我在预处理里加了多帧判断:连续识别三次,如果价格一致再执行点击,或者直接等加载图标消失再截屏。这种“动态场景下的脏数据”问题,靠纯图像预处理解决不了,必须结合时序逻辑来过滤。

调试的时候还有个小技巧:程序运行过程中把每次截图、处理后的图片、识别文本都保存到本地,做成一个日志目录。出问题的时候回看这些中间产物,比盯着一堆打印日志直观太多。我用的是logs/{timestamp}.png的格式,方便按时间排序。

4. 常见问题与排查技巧实录

做这个项目的过程中,我踩了不少坑,也总结了一堆容易被忽略的细节。放在最后,希望能帮你节省大量排查时间。

4.1 OCR 识别率低、报 “no text detected” 怎么办

这是我在开头提到的最常见的错误。如果 OCR 能成功加载模型,但始终识别不出文字,或者经常报 no text detected,大概率不是模型问题,而是图像输入质量太低。我从这三个方向入手排查:

首先检查截屏区域。截到空白区域、纯色背景或者窗口外区域,OCR 当然什么都识别不出来。其次检查预处理参数。如果截图的字很小,放大倍数不够,中文和数字特征不明显,识别率会直线下降。最后检查二值化阈值。Otsu 自适应阈值适合大多数场景,但遇到浅色数字、阴影背景时,会直接把文字抹掉。

我的经验是,先把原图保存在本地,用 OpenCV 的 imshow 看一下能不能“人眼识别”。如果人眼都看不清,OCR 也不可能看清。如果人眼能看清但 OCR 不行,那就调整预处理参数,尤其是放大倍数和对比度。

4.2 点击偏移、误点、点不中的排查思路

点击不准是所有自动点击类工具的通病。我遇到过三种情况:鼠标点到了按钮旁边、点到了别的元素、完全没反应。排查思路是先确认坐标计算方式,再看 DPI 缩放。

DPI 缩放是最隐蔽的坑。当系统设置里“更改文本、应用等项目的大小”不是 100% 时,pyautogui 的点击坐标和截屏坐标很可能不在同一个坐标系里。解决方案有两种:把程序运行时的缩放调成 100%,或者使用pyautogui的size()和position()反复校验坐标。

另一个高频问题:点击之后页面有动画过渡,比如渐入渐出、弹窗、滚动,按钮位置在动画结束后发生偏移。解决方式是点击前先用短等待,等动画结束再点,或者把“点击”改成“先移动、再停顿、再按下”的三段式动作。

4.3 程序稳定性:频繁崩溃、内存占用过高

跑一两天不崩是自动化工具体验的基本要求。PaddleOCR 模型加载后占用内存不算小,如果每轮循环都重新加载模型,内存会爆掉。正确做法是只在程序启动时加载一次模型,循环里复用实例。另外,每次截图生成的是 PIL 或 numpy 对象,用完之后要及时释放引用,必要时手动 gc.collect()。

还有一个容易忽视的问题:循环之间没有抖动随机延迟。连续快速识别、点击,不仅容易被系统检测为异常行为,还可能导致界面响应不过来。我建议把固定间隔改成随机间隔,比如 2 秒上下浮动 30%,让运行节奏更像人工操作。

4.4 常见问题速查表

现象可能原因解决建议
OCR 报 “no text detected”截图区域无文字、字太小扩大区域、提高放大倍数、检查截图坐标
OCR 报 “could not create a primitive...”模型初始化失败、显存或依赖问题换 CPU 版 PaddlePaddle、调低 batch、重建环境
识别价格偶尔跳变背景干扰、文字遮挡多帧确认、等动画消失再截屏
点击无反应DPI 缩放、坐标偏移调 100% 缩放,打印鼠标坐标校验
程序越跑越卡内存泄漏、每轮加载模型模型只初始化一次,及时清理不再使用的对象
安装 PaddleOCR 卡住网络源太慢使用国内镜像源或离线安装 wheel

经常有人问我,为什么不用按键精灵直接做。按键精灵确实能快出一版,但它的 OCR 能力普遍较弱,而且脚本可读性差,想加“连续三次识别再点击”这种复杂逻辑时很别扭。Python 方案的好处是拆开每个模块都能单独调试,识别不准就换模型,点击不准就校准坐标,扩展性完全不一样。这也是我宁可多写几百行代码也不用现成录制工具的原因。

最后再分享一个我后来才加的细节:程序在每次正确购买后,会自动把点击前后的截图和识别文本打包存成一个“购买成功案例”,每周复盘一次。这些历史数据能帮我发现很多偶发性问题,比如哪个时间段背景图会变化、哪个字体在某种背景下识别率特别低。在自动点击这类项目里,真正决定工具好坏的不是“能不能跑通”,而是“长时间不跑偏、不误判”。希望这套完整的思路,能帮你从零搭出自己的价格识别自动点击工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询