夜雨聆风学习资料网

ARTICLE · 1284922

月底录发票别再手敲:两条路线批量进Excel

月底录发票别再手敲:两条路线批量进Excel

推广:aiwebcool.com - 最硬核的 AI 工具指南网站,挑工具前先来这查一眼

实战SOP

月底录发票别再手敲:两条路线批量进Excel

发布于 2026年10月30日 · 7 分钟阅读

每到月底,财务和行政的桌角总会准时出现一摞报销发票:专票、普票、电子发票打印件,有的还压着红章。逐张打开、逐个字段敲进 Excel,一晚上过去,价税合计栏还差三十行。这件事完全可以交给脚本:把整个文件夹的发票批量喂给 OCR,跑完直接得到一张汇总表。本文给两条可复现的完整路线,从装依赖到导出 xlsx 一步不落。

先把分工说清楚:站内那篇 DeepSeek-OCR 部署教程讲的是「把 OCR 模型跑起来」,属于工具导向;本篇讲的是「把一摞发票变成一张 Excel」,属于任务导向,部署细节不再重复展开。

两条路线怎么选

主流做法分两条:云 API 路线和本地开源路线。一张表看清差异:

维度
路线 A:云 API
路线 B:本地 PaddleOCR
字段结构化
接口直接返回发票号码、税额等命名字段
开源版没有发票专用模型,字段映射须自写规则
数据出域
图片上传到服务商服务器
数据不出本机
费用
百度智能云有按月发放的免费额度
纯本地零成本,CPU 可跑
工程量
装好依赖即可调用
装环境加写正则规则
适合场景
每月几十到上千张、追求省事
票据敏感、量大、愿意维护脚本

一句话结论:图省事走 A,数据敏感或票量大走 B;也可以先拿 A 的免费额度把流程跑通,再迁到 B。

动手前建议先把文件归拢:新建一个「发票」文件夹,电子发票 PDF 原件直接另存进去,纸票用手机平拍(镜头贴近平面、避开灯光直射),命名尽量带上开票方和日期,后面排查漏识别时能对上号。文件夹结构没有强制要求,本文脚本示例用的就是同目录「发票」文件夹加汇总表输出的布局,照搬即可。

路线 A:云 API 六步走

以百度智能云「增值税发票识别」为例。官方文档给出的接口是 POST https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice,入参 image、url、pdf_file、ofd_file 四选一,优先级依次降低。也就是说 PDF 发票可以直接传 pdf_file,不必先转图片;base64 编码并 urlencode 后不超过 8M,图片最短边不小于 15px、最长边不超过 4096px;多页 PDF 用 pdf_file_num 指定页码,不传默认识别第 1 页。

第一步:装依赖。 一条命令装齐四个库:

pip install pymupdf pandas openpyxl requests

一个许可提醒:PyMuPDF 采用 AGPL/商业双许可——个人项目与开源(AGPL 兼容)场景免费,闭源商用需向厂商购买商业授权;发票场景若在公司内部工具里用,先确认许可边界,介意的话可换 pdf2image(GPL 组件依赖,同样要核对)或直接走云 API 的 pdf_file 直传,绕开本地渲染。

第二步:开通服务、管好密钥。 在百度智能云控制台创建 OCR 应用,拿到 API Key 和 Secret Key。密钥不要写进代码,用环境变量承载——官方 SDK 指南原话就是「不要把 AccessKey 写进项目代码」。

第三步:换 access_token。 调用识别接口前,先拿两个 Key 换 token,之后把 token 带在 URL 参数里请求:

import os, requests  def get_token():     # 密钥放环境变量,不硬编码     resp = requests.get(         "https://aip.baidubce.com/oauth/2.0/token",         params={             "grant_type": "client_credentials",             "client_id": os.environ["BAIDU_OCR_API_KEY"],             "client_secret": os.environ["BAIDU_OCR_SECRET_KEY"],         },     )     return resp.json()["access_token"]

第四步:确认票种与参数。 接口默认 type=normal,覆盖增值税普票、专票、电子发票(含全电票)和区块链发票;卷票传 type=roll;开 seal_tag=true 会附带印章判断结果。注意票种边界:定额发票、通用机打发票、医疗发票不在增值税发票识别范围内,硬传会失败或字段缺失,这类票要换百度智能云的智能财务票据识别接口 multiple_invoice,一次能拆 13 种常见票据的混贴图。

第五步:收集文件、跑批量脚本。 用标准库 pathlib 或 glob 递归通配文件夹里的 PDF、JPG、PNG,零第三方依赖。PDF 用 PyMuPDF 渲染成 300dpi 的 PNG——PDF 默认按 72dpi 渲染,150dpi 够网页预览但不够 OCR 认小字,OCR 输入建议 300dpi,且 PNG 优于 JPG(避免压缩伪影干扰笔画)。逐张调接口,把 words_result 字段装进字典,失败的单记状态、不中断整批:

import os, time, base64, glob import fitz  # PyMuPDF import pandas as pd import requests  VAT_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice"  def pdf_to_png(pdf_path, dpi=300):     # PDF 渲染成 300dpi PNG,OCR 认小字更稳     doc = fitz.open(pdf_path)     pix = doc[0].get_pixmap(matrix=fitz.Matrix(dpi / 72, dpi / 72))     return pix.tobytes("png")  def recognize(token, img_bytes):     resp = requests.post(         VAT_URL,         params={"access_token": token},         headers={"Content-Type": "application/x-www-form-urlencoded"},         data={"image": base64.b64encode(img_bytes).decode()},     )     return resp.json()  def main():     token = get_token()     rows = []     files = glob.glob("发票/*.pdf") + glob.glob("发票/*.jpg") + glob.glob("发票/*.png")     for path in files:         row = {"文件名": os.path.basename(path), "识别状态": "成功"}         try:             if path.lower().endswith(".pdf"):                 img = pdf_to_png(path)             else:                 img = open(path, "rb").read()             w = recognize(token, img).get("words_result", {})             row.update({                 "发票号码": w.get("InvoiceNum", ""),                 "开票日期": w.get("InvoiceDate", ""),                 "销售方税号": w.get("SellerRegisterNum", ""),                 "合计金额": w.get("TotalAmount", ""),                 "合计税额": w.get("TotalTax", ""),                 "价税合计": w.get("AmountInFiguers", ""),             })         except Exception as e:             row["识别状态"] = f"失败:{e}"         rows.append(row)         time.sleep(0.6)  # 免费态 2 QPS,串行节流     pd.DataFrame(rows).to_excel("发票汇总.xlsx", index=False)  if __name__ == "__main__":     main()

顺带一提,脚本里统一先转 PNG 是为了让输入可控、便于本地留档复查;如果你追求最少步骤,接口本身支持 pdf_file 直传,跳过转图那步也能跑。

第六步:额度与限速要心里有数。 按百度智能云官方免费测试资源页(页面更新时间 2026-10-22,本文 10-30 核对):增值税发票识别个人认证 1,000 次/月、企业认证 2,000 次/月,额度按月发放,当月没用完不结转;官方原文还写明「成功调用与失败调用均消耗免费测试资源」——传一张废图照样扣次,别拿模糊测试图刷量。QPS 以官方 QA 口径为准:未开通付费 2 QPS,开通付费后提升至 10 QPS;社区流传的 5 QPS、3 QPS 说法与官方口径不一致,本文不采。2 QPS 意味着脚本必须串行加 time.sleep 节流,或对限流错误做指数退避重试,上面脚本的 sleep(0.6) 就是干这个的。

其他云厂商可作对照:腾讯云混贴票据识别接口单图支持 14 种标准报销发票混贴,默认请求频率 5 次/秒;阿里云 RecognizeInvoice 支持增值税专票、普票、电子发票,格式覆盖最广(含 OFD、TIFF),官方自述总体识别准确率可达 98%——注意这是厂商自报口径。腾讯云免费额度各处说法不一,动手前以控制台资源包列表实际显示为准。

路线 B:本地 PaddleOCR

选它的理由:PaddleOCR 是目前 GitHub 上关注度最高的开源 OCR 项目之一(截至 2026-10-30 实测 91,929 星),Apache-2.0 许可。最新 release v3.7.0 发布了 PP-OCRv6 系列,分 tiny、small、medium 三档,CPU 就能跑——官方 release 说明给了 OpenVINO CPU 推理加速 5.2 倍的实证,发票批量场景用 CPU 完全可行,只是吞吐低于 GPU;不上显卡就没有显存压力。

pip install paddlepaddle paddleocr

环境注意:仓库提交记录里提到 PaddlePaddle 3.3.0 的 CPU 原生推理对 paddleocr 模型存在 bug,官方曾给出版本回退建议。装环境时把 PaddlePaddle 版本锁住是常见做法,遇到推理异常先查版本兼容,别急着怀疑代码。

关键认知,也是本文最想帮你避开的坑:开源版 PaddleOCR 没有增值税发票专用模型。 正确用法是 PP-OCRv6 做通用文字检测识别,拿到一串文本块之后自己写正则规则映射字段。这和云 API 直接返回 InvoiceNum 命名字段的工程量差异,正是两条路线选型的核心论据。需要表格或版面还原时,v3.6.0 起的 PaddleOCR-VL 配合 PP-StructureV3 管道支持表格识别与印章识别开关,可以叠加使用。

import re, glob import pandas as pd from paddleocr import PaddleOCR  ocr = PaddleOCR(ocr_version="PP-OCRv6", device="cpu")  # CPU 即可跑  def extract_fields(texts):     # 从文本块里按规则捞字段,开源路线的核心工作量在这里     row = {}     for t in texts:         if re.fullmatch(r"\d{8}", t):          # 8 位发票号码             row["发票号码"] = t         m = re.search(r"(\d{4}年\d{1,2}月\d{1,2}日)", t)         if m:             row["开票日期"] = m.group(1)         m = re.search(r"(?:价税合计|合\s*计).*?([\d,]+\.\d{2})", t)         if m:             row.setdefault("价税合计", m.group(1))     return row  rows = [] for path in glob.glob("发票/*.png") + glob.glob("发票/*.jpg"):     result = ocr.predict(path)     texts = result[0]["rec_texts"]             # 通用文本块列表     rows.append({"文件名": path, **extract_fields(texts)})  pd.DataFrame(rows).to_excel("发票汇总_本地.xlsx", index=False)

示例正则只覆盖三类最典型字段。真实票面远比示例复杂:全电票版式、卷票、带折痕的扫描件都会让规则漏抓,所以本地路线的产出务必保留一列人工复核状态。

字段映射与 Excel 导出

云 API 返回的是英文字段名,落表前要做一次映射。百度智能云官方文档里发票识别的关键字段对照如下:

返回字段
含义
建议列名
InvoiceNum
发票号码
发票号码
InvoiceDate
开票日期
开票日期
SellerRegisterNum
销售方纳税人识别号
销售方税号
TotalAmount
合计金额
合计金额
TotalTax
合计税额
合计税额
AmountInFiguers
价税合计(小写)
价税合计
CommodityTaxRate
税率(在商品明细数组内)
税率

商品明细数组里的 CommodityName、CommodityPrice 等字段可按需展开成子表,这里不铺开。导出环节用 pandas 出数、openpyxl 做样式:pandas 的 to_excel 写 .xlsx 底层用的就是 openpyxl 引擎;当前 pandas 稳定版 3.0.6(2026-09-18 发布)要求 Python 3.11 以上。给汇总表加一点办公体面:

from openpyxl import load_workbook from openpyxl.styles import Font  wb = load_workbook("发票汇总.xlsx") ws = wb.active for cell in ws[1]:     cell.font = Font(bold=True)   # 表头加粗 ws.freeze_panes = "A2"            # 冻结首行 for col in "FGH":                 # 金额三列套千分位     for cell in ws[col][1:]:         cell.number_format = "#,##0.00" wb.save("发票汇总.xlsx")

两个库的分工口诀:按「表格」思考用 pandas,按「单元格和样式」思考用 openpyxl。注意 openpyxl 不计算公式——如果表里写了公式,用 load_workbook(data_only=True) 读结果前,须先在 Excel 或 LibreOffice 里重算保存过。

大模型路线:作补充,不当主力

GLM-OCR(智谱开源,0.9B 参数)支持 text、formula、table、custom 四种模式,custom 模式用自定义 JSON schema 提示词做结构化抽取,社区有开发者用它对收据抽出发票号、日期、明细与合计的实测示例。但口径要说全:这是官方能力宣称加第三方通用实测,目前没有发票专项公开基准,实测者的结论也是「结构化抽取靠 prompt 明确约束后稳定性才大幅提升」。0.9B 参数对显存要求很低,票量不大、想用一行 prompt 代替一套正则时可以试;要稳定产线,前两条路线更可靠。资源清单见站内 GLM-OCR 篇(文末延伸阅读)。

常见报错与对策

识别失败和识别不准要分开排查:前者是接口报错或返回空,多为图片质量问题;后者是有结果但字段错位,多为版式与语义问题。排查顺序四步走:图片质量、版式与语言、引擎参数、业务兜底。

采集端的高频原因:覆膜票面灯光直射反光过曝、倾斜拍摄致字段畸变、折痕污渍褪色、红色发票章压住税号或金额栏、截图滤镜过度压缩丢细节。对策是柔光平拍、抚平折痕、印章遮挡时微调角度、优先用电子发票 PDF 原件而非翻拍截图。量化看有多伤:百度智能云一篇技术文的实验口径称 15 度倾斜使字符识别准确率下降 23%,另有案例印章覆盖导致 35% 的金额字段识别错误——注意这是该文实验与案例数据,不是行业通识。

接口侧可对照官方错误码表(以腾讯云文档为例,各家逻辑相似):ImageBlur 图片模糊就重拍或预处理;ImageDecodeFailed 解码失败就核对文件确为 PNG/JPG/PDF 且未损坏;文件超限就压缩或拆分。票种不匹配(定额、机打、医疗发票硬传增值税接口)就换混贴票据接口。

大模型路线有三类典型错误也值得知道(腾讯云官方公众号口径):字段串行(购销方名称与税号错位)、金额串行(不含税金额、税额、价税合计读串)、明细截断(多行明细只出前几行);专业 OCR 按字段坐标返回,不发生串行问题。

合规边界,一句话说清

发票含税号、购销方名称等经营信息,走云 API 前确认服务商数据协议(百度智能云协议明确用户业务数据归用户所有);验真伪只认国家税务总局全国增值税发票查验平台——每票每天最多查验 5 次、可查最近 5 年内开具的发票、当日开具当日可查验;AI 审查结果作辅助,不替代专业意见。

AI 声明:本文由 AI 辅助生成,经人工审核编辑。文中涉及第三方模型与服务,请以官方说明为准。互动:百度云 API 免费额度按月发(个人 1000 次/月),PaddleOCR 本地 CPU 就能跑。注意坑:开源版没有发票专用模型,字段要自己写正则映射。附完整可跑脚本。 

- AI 甜点 · 每天一块 AI 资讯甜点 -

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料