ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Python OCR识别图片自动录入Excel并打包exe的完整指南

用Python OCR识别图片自动录入Excel并打包exe的完整指南 前两天一个朋友抱着笔记本来找我说他手里有三四十张小票照片需要把上面的单号、金额、日期手工录到Excel里一中午录到怀疑人生。我说这种活儿你早该找Python来干一条命令的事。后来我花半小时给他写了段脚本识别图片文字、写入Excel再打包成一个exe直接扔给他双击运行。整个过程踩了几个坑也总结出不少经验这里完整记录一下。这个项目的核心链路就三步图片文字提取OCR→ 结构化写入Excel → 打包成exe免环境运行。适合所有想把日常办公中“看图录入”这种重复劳动自动化的朋友尤其是非程序员出身、电脑上不想装Python环境、只想拿一个exe文件干活的人。下面从选型到落地把完整方案和坑全部掰开揉碎讲清楚。1. 整体设计与技术选型1.1 OCR识别方案怎么选OCR光学字符识别是整套流程的地基方案选不对后面全白搭。目前主流路线有三类云API、本地开源库、系统自带能力。云API百度、阿里、腾讯都提供识别率高尤其是复杂版式、手写体、旋转文字但有个硬伤——需要联网而且免费额度用完就要付费。个人工具和内部小工具用它有点不划算。本地开源库性价比最高适合“图片不是那么刁钻”的常规场景比如打印体文字、Excel表格截图、聊天记录截图。主流两个Tesseract和PaddleOCR。Tesseract是老牌开源王者安装简单一个exe装完就能跑对英文和数字识别稳对中文需要下载语言包识别率属于“够用”级别。PaddleOCR是百度开源中文识别率比Tesseract高一截模型也更新的勤但依赖较重第一次跑要下载模型文件打包exe的时候体积会偏大。我这个场景是小票上的数字和中文混杂行间距不整齐所以我选了PaddleOCR做识别引擎识别率明显更稳。如果你处理的是印刷体英文、公式、或扫描件Tesseract也够用。文章后续两种方案的代码我都会给方便你替换。1.2 Excel写入与打包方案确定Excel写入听起来简单但细节不少。最常见的两个库openpyxl和pandas。pandas写Excel本质是调openpyxl或xlsxwriter优势是处理结构化表格比如DataFrame批量写非常顺手代码短。openpyxl更底层可以精细控制单元格样式、列宽、合并单元格、字体颜色。我这个项目里识别的每一行是一张小票结构固定用pandas.from_dict一行就能搞定但如果你想对表头做点样式美化还是得用openpyxl。最后我选择了openpyxl灵活性最大也不至于为了一个简单功能拖一个很重的pandas进来。打包方案也有讲究。PyInstaller是目前最主流的Python打包工具命令行一条命令就能把脚本变成exe支持Windows/macOS/Linux社区活跃遇到问题搜一下基本有答案。Nuitka是后起之秀打包出来性能更好、更具隐蔽性但配置复杂对新手不友好。这个项目是给同事用的效率工具不是性能敏感型业务选PyInstaller就够了。1.3 项目目录与执行流程规划动手写代码前我习惯先把目录结构和执行流程画清楚避免写到最后四处打补丁。ocr_to_excel/ ├── main.py # 主脚本 ├── requirements.txt # 依赖清单 ├── images/ # 存放待识别图片 ├── output/ # 输出Excel文件夹 └── build/ # PyInstaller打包临时目录执行流程也很清晰读取images目录下所有图片 → 逐张预处理 → OCR识别 → 把结果整理成行数据 → 一次性写入Excel → 打印结果。这样的好处是简单直观不需要使用者理解任何参数双击exe就能跑。2. 环境准备与依赖安装2.1 Python环境搭建如果你电脑上已经装过Python可以直接跳过这步。没装过的去python.org下载安装包安装那一步一定要记得勾选“Add Python to PATH”不然后面在命令行里敲python会提示找不到命令。装完在命令行验证一下。python --version pip --version能看到版本号就说明环境OK。这里有个多年的老坑如果你之前装过Python但忘了加PATH建议重装一次勾上别去手动改环境变量容易越改越乱。2.2 用虚拟环境隔离依赖多年经验告诉我写Python项目随手建虚拟环境是个好习惯。虚拟环境能把项目依赖和系统Python隔离开避免装A库把B库版本给你顶了。用Python自带的venv就够不需要额外装virtualenv。mkdir ocr_to_excel cd ocr_to_excel python -m venv venv venv\Scripts\activate # Windows激活虚拟环境激活后命令行前面会出现(venv)前缀说明现在在虚拟环境里了。后面装的依赖全在这个环境的site-packages里不会污染全局。2.3 安装依赖库这个项目核心依赖就三个PaddleOCR或者pytesseract、openpyxl、Pillow。pip install paddlepaddle paddleocr openpyxl pillow如果你网络不太好国内用户可以用清华源速度快很多pip install -i https://pypi.tuna.tsinghua.edu.cn/simple paddlepaddle paddleocr openpyxl pillow如果你选Tesseract路线还需要额外安装Tesseract-OCR本体这个是C写的软件不是Python库。Windows用户去GitHub的UB-Manneheim构建页面下载安装装完后把安装目录记下来代码里要用。同时安装中文语言包并把TESSDATA_PREFIX环境变量指到tessdata目录。PaddleOCR路线首次运行时会自动下载检测和识别模型到用户目录如果下载失败你可以手动去模型仓库下载然后放到~/.paddleocr目录。这个我在踩坑实录部分详细说。3. 核心代码实现与原理讲解3.1 图片读取与预处理OCR识别前先做预处理这是提高识别率最关键的一步也是新手最容易忽略的。很多情况下直接拿原图去识别准确率就是上不去但转成灰度、调一下对比度准确率能提升一大截。from PIL import Image import numpy as np def preprocess_image(image_path): # 打开图片并转为灰度图 img Image.open(image_path).convert(L) # 将图片转为numpy数组做进一步处理 img_array np.array(img) # 提高对比度像素值小于128的置0大于等于128的置255 img_array np.where(img_array 128, 0, 255).astype(np.uint8) # 转回PIL Image对象 return Image.fromarray(img_array)为什么要转灰度因为彩色图片信息量大字符识别依赖的是轮廓对比度颜色反而是噪声。为什么做二值化黑白两色因为很多OCR引擎对高对比度的输入特别友好识别速度快、准确率高。但是这里必须提醒一句二值化阈值128是经验值不是万能值。我实测发现浅色背景深色文字的图片阈值设在180~200效果更好深色背景浅色文字的图片阈值可能要用50左右。你可以先用128试效果不好再调整。也可以把图片先做缩放处理图片太小时OCR识别不出来太大时识别慢且容易出错。实战中我习惯把图片宽度统一到1500像素左右高度按比例缩放这样速度和准确率最平衡。3.2 调用OCR识别并解析结果PaddleOCR新版API用法很简洁几步就能跑通。from paddleocr import PaddleOCR # 初始化模型langch表示中英文混合识别 ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) def extract_text_from_image(image_path): result ocr.ocr(image_path, clsTrue) lines [] for line in result: if line is None: continue for item in line: box, text_info item text text_info[0] confidence text_info[1] lines.append((text, confidence)) return lines注意result返回值是嵌套列表每个元素包含文本框坐标和识别结果。上面的代码把识别文本和置信度提取成列表方便后续处理。如果你用Tesseract路线代码长这样import pytesseract from PIL import Image # 注意指定tesseract安装路径 pytesseract.pytesseract.tesseract_cmd rC:\Program Files\Tesseract-OCR\tesseract.exe def extract_text_with_tesseract(image_path): # 识别中文--psm 6表示把图片当一行块处理适合票据类 text pytesseract.image_to_string( Image.open(image_path), langchi_simeng, config--psm 6 ) return text两种方案识别结果的解析方式不同PaddleOCR返回结构化列表好拆字段Tesseract返回纯字符串需要你自己按行切分、按规则提取关键信息。3.3 识别结果写入Excel识别出文本后下一步要写入Excel。这里有个关键设计识别结果要怎么组织成表格行我的做法是每一张图片识别出的文本先按行拼成一个大字符串再把需要提取的字段比如“单号: ABC123”里的ABC123用正则匹配出来。如果匹配不到就留空不让程序崩溃。import re def parse_invoice_info(text_by_lines): full_text \n.join([line[0] for line in text_by_lines]) info {} # 示例匹配单号 match re.search(r单号[:]\s*(\w), full_text) info[单号] match.group(1) if match else 未识别 # 示例匹配金额 match re.search(r金额[:]\s*([\d.]), full_text) info[金额] match.group(1) if match else 未识别 # 示例匹配日期 match re.search(r日期[:]\s*(\d{4}[-/]\d{1,2}[-/]\d{1,2}), full_text) info[日期] match.group(1) if match else 未识别 return info然后写入Excel。from openpyxl import Workbook from openpyxl.styles import Font, PatternFill, Alignment def write_to_excel(rows, output_path): wb Workbook() ws wb.active ws.title 识别结果 headers [图片名, 单号, 金额, 日期] # 写入表头 for col, header in enumerate(headers, 1): cell ws.cell(row1, columncol, valueheader) cell.font Font(boldTrue) cell.fill PatternFill(start_colorFFD966, end_colorFFD966, fill_typesolid) cell.alignment Alignment(horizontalcenter) # 逐行写入数据 for row_idx, row_data in enumerate(rows, 2): for col_idx, value in enumerate(row_data, 1): ws.cell(rowrow_idx, columncol_idx, valuevalue) # 调整列宽 for col in range(1, len(headers) 1): ws.column_dimensions[chr(64 col)].width 20 wb.save(output_path) print(fExcel文件已保存到: {output_path})openpyxl的写入逻辑很直白先建Workbook激活默认sheet然后逐单元格写值。表头加粗加底色是方便人看不是必须但加上体验感好很多。列宽20只是经验值字段多时可以做成自适应。3.4 完整主脚本整合把上述模块串起来主脚本长这样import os from pathlib import Path def main(): images_dir Path(images) output_path Path(output/识别结果.xlsx) output_path.parent.mkdir(exist_okTrue) all_rows [] for image_path in sorted(images_dir.iterdir()): if image_path.suffix.lower() not in [.png, .jpg, .jpeg, .bmp]: continue print(f正在处理: {image_path.name}) processed_img preprocess_image(str(image_path)) processed_img.save(temp_processed.png) lines extract_text_from_image(temp_processed.png) info parse_invoice_info(lines) all_rows.append([image_path.name, info.get(单号, ), info.get(金额, ), info.get(日期, )]) if all_rows: write_to_excel(all_rows, str(output_path)) else: print(没有识别到任何图片) if __name__ __main__: main()注意这里有一个细节我用了临时文件temp_processed.png把预处理结果存下来再传给OCR。这样做是因为我本地调试时发现PIL的Image对象如果直接传给PaddleOCR偶尔会有内存数据没同步的问题写成图片文件再读反而稳定。后来我也试过直接用图像数组传给OCR也能工作但临时文件方案排查问题最方便——你还能肉眼看看预处理效果到底怎么样。4. 打包exe的完整流程与踩坑实录4.1 PyInstaller基础命令代码调通了接下来就是打包成exe让没有Python环境的人也能跑。先把PyInstaller装进虚拟环境pip install pyinstaller基础打包命令pyinstaller -F -w main.py-F是打包成单文件-w是运行时不弹出黑色命令行窗口。但-F有个问题所有依赖会被压缩进一个exe里启动时会先解压到临时目录所以启动速度会慢一点。如果内部工具不在乎多出一个文件夹用-D模式启动更快。我给自己用习惯用-F方便分发。刚接触PyInstaller的我建议先不要加任何优化参数直接pyinstaller -F main.py这样即使出错日志也最容易看懂。4.2 No module named xxx报错处理这是打包最常见的问题本地运行好好的一打包就提示ModuleNotFoundError。原因很多最常见的两种情况。第一种是依赖库没有写在脚本里被PyInstaller静态分析到。PyInstaller分析import语句并收集依赖但有些库的导入方式是动态的比如PaddleOCR内部会动态import一些子模块PyInstaller未必抓得到。解决办法是打包时加pyinstaller -F --collect-all paddleocr --collect-all paddle main.py--collect-all会把这个包的所有子模块、配置文件、数据文件全部收集进来。第二种情况是程序运行时才import的模块可以用--hidden-import强制包含pyinstaller -F --hidden-import PIL.Image --collect-all paddleocr main.py4.3 模型文件与资源路径问题PaddleOCR第一次运行时要下载模型文件打包时模型不一定被带进去用户拿到exe后首次运行会尝试联网下载如果没网就废了。这个坑一定要提前解决。我的做法是先把模型下好然后把模型目录当做数据文件一起打包。模型通常存放在用户目录的.paddleocr目录下找到它ls ~/.paddleocr/whl/det/ch/ch_PP-OCRv3_det_infer找到后打包pyinstaller -F --collect-all paddleocr --add-data C:\Users\你的用户名\.paddleocr;.paddleocr main.py--add-data的格式是“源路径;目标路径”Windows下分号分隔Linux和macOS是冒号。目标路径是exe解压后相对路径。还要注意代码里读取资源文件时不能用相对路径。因为-F模式下exe运行时工作室目录是用户当前目录而资源文件被解压在临时目录。标准写法是用PyInstaller提供的sys._MEIPASS变量定位import sys import os def resource_path(relative_path): if hasattr(sys, _MEIPASS): return os.path.join(sys._MEIPASS, relative_path) return os.path.join(os.path.abspath(.), relative_path)Tesseract路线也有类似问题用户电脑上必须安装Tesseract-OCR软件exe不能替你装。解决办法要么打包时把Tesseract目录一起塞进去然后代码里动态指定路径要么在交付时附带一个Tesseract安装包让用户手动装。第一种方式打出来的exe会很大但体验最好。我建议如果是公司内部小范围用直接附安装包便宜省事。4.4 打包体积优化与杀毒软件误报PaddleOCR PaddlePaddle打出来exe动辄800MB起步因为PaddlePaddle这套框架本身就很重。这点你要有心理预期。想压缩体积可以试试这几个手段第一排除用不到的模块。PaddlePaddle打包时会顺带很多没用的组件可以用--exclude-module排除一部分比如pyinstaller -F --exclude-module matplotlib --exclude-module scipy main.py第二换更轻量的OCR方案。Tesseract路线的exe只有二三十MB但中文识别率弱一些。第三如果只是Windows内部小工具其实不必追求单文件exe用-D模式生成一个文件夹体积会更易管理启动也快。杀毒软件误报是另一个高频坑。PyInstaller打包exe的逻辑就是“把Python解释器 脚本 依赖压缩在一起”这个结构和某些恶意程序很像所以360、Windows Defender偶尔会报毒。这是误报不是真有毒。解决办法把exe所在目录加入杀毒软件白名单或者提交到微软、360的误报申诉平台。另外尽量别在压缩包模式下运行exe解压到本地目录跑误报概率会更低。4.5 打包后测试与交付打包完成后不要直接发给别人先在自己电脑上做一轮完整测试双击exe是否正常运行在命令行里带参数运行是否有正确输出图片文件夹放在exe同级目录时是否正常工作换个没有Python的测试机或纯Windows环境测试一遍老实说我刚开始做了个很尴尬的失误打包时图省事直接把全路径写死在代码里比如C:\Users\me\Desktop\test.png结果exe换台电脑就找不到文件。后来学乖了图片目录都改成exe同目录下的相对路径交付时把images文件夹一起带上使用者只需要把图片丢进去即可。5. 常见问题快查表与进阶优化5.1 问题快查表整理一下我实际运行中遇到的问题和解法做成表格方便你排查。问题现象可能原因解决方法识别结果全是乱码没有指定中文字库或语言包确认PaddleOCR的langchTesseract确认安装了chi_sim语言包识别准确率低图片对比度过低、旋转、模糊预处理做灰度化二值化调整阈值先放大图片到1500像素宽打包后提示找不到main.pyPyInstaller打包路径问题确认在项目根目录下执行pyinstaller命令打包后运行提示模型路径错误模型没有打包进去使用--add-data指定模型目录代码用sys._MEIPASS定位打包后exe体积800MBPaddlePaddle框架过大换PaddleOCR轻量模型或Tesseract方案杀毒软件报毒误报加入白名单或提交误报申诉打开Excel提示文件损坏openpyxl保存的样式与旧版Excel不兼容检查保存的扩展名必须是.xlsx不要存成.xls识别输出慢大图PaddleOCR CPU模式预处理缩减图片尺寸用GPU版本PaddlePaddle需要额外配置5.2 提升识别准确率的进阶技巧如果你想把这个工具做得更稳这里有几个我实测有效的技巧。一是图片去噪。手机拍的票据有噪点二值化前可以先做一个高斯模糊或中值滤波。用PIL的filterfrom PIL import ImageFilter img img.filter(ImageFilter.MedianFilter(size3))二是调整扫描倾斜。手拿图片拍照难免倾斜5度以内OCR对倾斜敏感。想处理倾斜可以用OpenCV的最小外接矩形检测出文本块角度再做旋转变换。这个逻辑稍微复杂但对扫面件效果提升巨大。三是多种预处理策略并行识别。我在一个项目中用过“灰度二值化高对比度彩色放大1.5倍”三种预处理分别跑OCR然后用投票法或置信度加权法决定最终字段取值。这样处理速度会慢三倍但准确率能到99%以上适合对结果要求极高的场景。5.3 拓展方向批量处理、GUI界面、定时任务如果你已经成功跑通上面的链路可以顺手往这几个方向扩展。批量处理现在已经支持了遍历文件夹里所有图片更进一步可以加入去重逻辑识别出的文件名如果已在Excel里出现过就跳过。GUI界面可以给exe包一层图形接口使用者不用改代码就能选图片、看识别日志。用tkinter最快自带不需要额外打包追求美观可以用PySide6但打包体积会再大一圈。定时任务适合做“每日定时扫描某个文件夹并自动生成报表”。用Windows任务计划程序定时启动exe再把Excel输出到共享目录全组人都能看。这套组合下来基本就是一个“阅后即焚”级别的智能录入机器人。另外提醒一句如果你的场景需要连续大批量识别别用-F单文件模式解压到临时目录占空间不说反复清理也容易出怪问题。直接-D模式配一个目录程序启动速度会快非常多。结语与一个小技巧最后再分享一个只有实际操作后才会发现的细节图片文件名别用中文。我第一版让用户把图片命名为“2024年1月报销单.png”识别和写入没问题但用PyInstaller打包后运行时偶尔会出现编码错误。两点原因一是Windows控制台默认编码对中文不友好二是PyInstaller临时目录对非ASCII字符的处理在不同Windows版本上表现不一致。解决办法很简单告诉用户图片名统一改成英文、数字和下划线比如invoice_001.png。如果你实在需要保留中文文件名就在代码里把所有print输出改成英文并把Python运行环境编码设置成utf-8或者在打包参数中加--uac-admin但都麻烦。这个项目从我写代码到打包交付差不多三个小时其中八成时间花在打包环境和路径问题上。跑通之后朋友那些三十多张小票几秒钟就出结果了。如果你照这篇文章做一遍大概率两个小时内能跑通第一个exe。代码里任何一步遇到问题回头对照第五节的排查表基本都能解决。
返回列表