实战 SOP
实战 SOP

发票批量识别导入Excel:月底录票还要手敲多久

一摞发票变一张 Excel 的两条路线实战(与站内 deepseek-ocr 本地部署篇分工:那篇管把模型跑起来,本篇管批量结构化导出)。路线 A 百度智能云增值税发票识别:vat_invoice 接口支持 pdf_file 直传,免费额度个人 1,000 次/月、企业 2,000 次/月按月发放、失败调用也扣次,免费态 2 QPS/付费 10 QPS(官方口径,社区流传的 5/3 QPS 不采)。路线 B PaddleOCR 本地(v3.7.0/PP-OCRv6、91,929 星、Apache-2.0、CPU 可跑),关键认知加粗:开源版没有发票专用模型,字段映射须自写正则——这个工程量差异正是两条路线的选型核心论据。工程链路完整可跑:pathlib/glob 批量收集、PyMuPDF 300dpi 渲染(附 AGPL/商业双许可提醒与替代方案)、逐张识别装字典、pandas/openpyxl 导出带样式的 xlsx。报错对策覆盖模糊、盖章遮挡、倾斜、票种不匹配与 QPS 限流;真伪查验只认全国增值税发票查验平台(每票每天 5 次、近 5 年内开具)。腾讯云/阿里云对照与大模型路线(GLM-OCR)小节均带口径限定,无营销识别率数字。

发布于 2026年10月30日9 分钟阅读
<!-- invoice-ocr-to-excel-sop | sop | 发票批量识别导入Excel:月底录票还要手敲多久 -->

每到月底,财务和行政的桌角总会准时出现一摞报销发票:专票、普票、电子发票打印件,有的还压着红章。逐张打开、逐个字段敲进 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 页。

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

text
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 参数里请求:

python
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 字段装进字典,失败的单记状态、不中断整批:

python
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;不上显卡就没有显存压力。

text
pip install paddlepaddle paddleocr

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

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

python
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 以上。给汇总表加一点办公体面:

python
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 审查结果作辅助,不替代专业意见。

延伸阅读

常见问题

Q1: 发票 OCR 识别失败是什么原因?

最常见的是图片质量:反光、模糊、倾斜、印章遮挡关键字段、过度压缩。其次是票种不匹配——定额发票、通用机打发票不在增值税发票识别接口范围内,要换混贴票据接口。云 API 免费态还有 QPS 限制,并发过高会被限流。

Q2: 批量识别发票怎么录入 Excel?

三段式流程:用 pathlib 或 glob 收集文件夹里的 PDF/JPG/PNG;PDF 用 PyMuPDF 渲染成 300dpi 图片;逐张调 OCR 接口或本地模型,字段装进字典,最后用 pandas 的 to_excel 一次性导出 xlsx。本文路线 A 的主脚本就是完整可跑的示例。

Q3: PDF 电子发票能直接识别吗,要先转图片吗?

百度智能云接口支持 pdf_file 直传,不必先转图片;但把 PDF 统一渲染成 300dpi PNG 再传,输入更可控,也方便本地留档复查。扫描件同理,优先用 PDF 原件而不是二次截图。

Q4: 开源 PaddleOCR 能直接识别出发票字段吗?

不能直接出字段。开源版没有增值税发票专用模型,只能拿到通用文字块,需要自己写正则映射发票号码、日期、金额等字段。追求开箱即用的命名字段,云 API 路线工程量小得多。

Q5: 批量识别出来的发票怎么验真伪?

OCR 只解决「读出来」,真伪要另查。官方唯一在线渠道是全国增值税发票查验平台,注意每票每天最多查验 5 次、可查最近 5 年内开具的发票;云厂商也有发票验真接口可作批量抽查,结果同样以税务平台为准。

本文由 AI 辅助生成,经人工审核编辑。最后更新:2026-10-30

常见问题

发票 OCR 识别失败是什么原因?
最常见的是图片质量:反光、模糊、倾斜、印章遮挡关键字段、过度压缩。其次是票种不匹配——定额发票、通用机打发票不在增值税发票识别接口范围内,要换混贴票据接口。云 API 免费态还有 QPS 限制,并发过高会被限流。
批量识别发票怎么录入 Excel?
三段式流程:用 pathlib 或 glob 收集文件夹里的 PDF/JPG/PNG;PDF 用 PyMuPDF 渲染成 300dpi 图片;逐张调 OCR 接口或本地模型,字段装进字典,最后用 pandas 的 to_excel 一次性导出 xlsx。本文路线 A 的主脚本就是完整可跑的示例。
PDF 电子发票能直接识别吗,要先转图片吗?
百度智能云接口支持 pdf_file 直传,不必先转图片;但把 PDF 统一渲染成 300dpi PNG 再传,输入更可控,也方便本地留档复查。扫描件同理,优先用 PDF 原件而不是二次截图。
开源 PaddleOCR 能直接识别出发票字段吗?
不能直接出字段。开源版没有增值税发票专用模型,只能拿到通用文字块,需要自己写正则映射发票号码、日期、金额等字段。追求开箱即用的命名字段,云 API 路线工程量小得多。
批量识别出来的发票怎么验真伪?
OCR 只解决「读出来」,真伪要另查。官方唯一在线渠道是全国增值税发票查验平台,注意每票每天最多查验 5 次、可查最近 5 年内开具的发票;云厂商也有发票验真接口可作批量抽查,结果同样以税务平台为准。

相关文章

实战 SOP

3B 小模型日吞 20 万页文档:DeepSeek-OCR 部署实录

DeepSeek-OCR 本地部署实录:视觉侧把整页压成几十到几百个 vision token,解码侧 3B MoE 激活仅约 570M,官方论文口径单张 A100-40G 日产 20 万页以上训练数据,24GB 消费卡即可入场。两代官方仓库路线不同:环境与 Transformers 推理走二代(Apache-2.0),上游 vLLM 与 API 服务化走一代(MIT)成熟生态;cuda11.8+torch2.6.0+flash-attn 2.7.3 环境逐字实录,五种分辨率模式 token 账本(64 到 400+),NGram 防复读处理器与 128821/128822 表格白名单,附显存社区口径警示(官方未公布)与 10 条排障清单。

2026年10月25日9 分钟阅读
实战 SOP

录音转文字还在按分钟付费?本地 59 秒转完

本地语音转文字五步实战(faster-whisper 主引擎,仓库 github.com/SYSTRAN/faster-whisper):按显存选型(24GB 卡 large-v3+float16+beam 5;8GB 卡 int8_float16 不够降 medium;纯 CPU small/base+int8)→ 安装(GPU 需 CUDA 12 cuBLAS + cuDNN 9,旧组合降级 ctranslate2 3.24.0/4.4.0)→ 最小转写脚本 → 提速省显存三招(精度 fp16 转 int8 省约 1.6GB、批量 batch 8 多花约 1.5GB 提速约 3.7 倍、beam 第三位再动)→ 进阶(Silero VAD、词级时间戳、自拼 SRT、WhisperX 说话人分离)。基准口径全文统一注明:large-v2、beam_size=5、13 分钟音频、RTX 3070 Ti 8GB——原版 2 分 23 秒/4708MB,faster-whisper int8 59 秒/2926MB,批量化 16 秒/4500MB;纯 CPU 档如实写 whisper.cpp fp32(2m05s/1049MB)比 faster-whisper fp32 更快更省,不写全面碾压。陷阱清单含 GPU 忌纯 int8(CTranslate2 文档:需计算能力 7.0/6.1 以上,老卡静默回退 float32)。结尾单段带过 NVIDIA Nemotron 3.5 Transcribe 新引擎选项,与开源列那篇分工不重复跑分。

2026年10月29日9 分钟阅读
实战 SOP

AI 短剧怎么制作:即梦短剧工作室六步出片

即梦短剧工作室 10 月 26 日上线(官方公告口径),从输入剧本到导出成片六步走:剧本阶段 DreamScript(多题材生成、爆款诊断、10 万字长篇解析)→ 美术设定 Anycook(人物与场景)→ 分镜 DreamShot(画布/故事板双模式、3D 导演台预演)→ 生成 → 后期(片段重拍、声音重构、智能切镜)→ 导出。计费如实写:会员六档积分(725 至 54600)、Seedance 2.5 促销价 0.19 元/秒(CLI 0.18 元/秒),官网两页面价格不一致处以实时价格页为准;站内入口、单独计费、转绘语种等官方未公布项全部留白并给出读者自行验证路径。市场背景按 DataEye 口径:抖音 AIGC 漫剧月度占比从 1 月 0% 升至 12 月 10.88%、单月播放 21.96 亿、全年增长 181 倍。与站内 LibTV×WorkBuddy SOP(平台接入路线)的分工已声明。

2026年10月28日11 分钟阅读