GLM-OCR实操手册:批量处理100+张发票图片,输出统一JSON格式结构化数据
GLM-OCR实操手册:批量处理100+张发票图片,输出统一JSON格式结构化数据
你是不是也遇到过这样的烦恼?财务部门每个月都要处理上百张发票,一张张手动录入信息,不仅耗时费力,还容易出错。或者,作为开发者,你需要从大量扫描件中提取结构化数据,但传统的OCR工具要么识别不准,要么格式混乱,后续处理起来让人头疼。
今天,我要分享一个实战解决方案:用GLM-OCR模型,一键批量处理上百张发票图片,直接输出干净、统一的JSON格式数据。整个过程就像把一堆杂乱无章的纸质文件,瞬间变成整齐划一的电子表格,省时省力,准确率还高。
这篇文章,我将手把手带你走通整个流程。从环境搭建、模型启动,到编写一个高效的批量处理脚本,最后教你如何解析和利用这些结构化的JSON数据。无论你是想优化财务流程,还是构建自己的文档自动化系统,这套方法都能直接拿来用。
1. 为什么选择GLM-OCR处理发票?
在动手之前,我们先搞清楚,面对发票识别这个具体任务,GLM-OCR到底强在哪里。
1.1 传统OCR的痛点
你可能用过一些OCR工具或API,它们通常有这些问题:
- 格式不统一:识别出来的文字是一大段,日期、金额、公司名称混在一起,需要自己写复杂的规则去“抠”出来。
- 复杂版面抓瞎:发票上可能有表格(如商品清单)、印章、手写备注。普通OCR容易把这些元素的文字混在一起,或者直接忽略。
- 后期处理麻烦:你需要写大量的后处理代码,进行文本清洗、正则匹配、实体识别,才能得到结构化数据。
1.2 GLM-OCR的“多模态”优势
GLM-OCR不是一个简单的“看图识字”工具。它内置的“多模态理解”能力,让它能像人一样“看懂”文档的布局和逻辑。
- 理解文档结构:它能区分标题、段落、表格、图章,知道哪些文字属于“销售方”,哪些属于“金额”。
- 端到端结构化输出:通过精心设计的提示词(Prompt),我们可以直接要求它按我们想要的JSON字段(如
invoice_number,total_amount,date)输出结果,省去中间步骤。 - 批量处理友好:模型服务一旦启动,就可以通过API持续调用,非常适合编写脚本进行批量处理。
简单说,GLM-OCR把“图像识别”和“信息结构化”两步合并成了一步,这正是处理发票这类格式化文档时我们最需要的。
2. 快速部署与启动GLM-OCR服务
我们的目标是批量处理,所以首先要让模型作为一个服务跑起来。跟着下面的步骤,10分钟内就能搞定。
2.1 环境准备与一键启动
假设你已经在服务器上拿到了GLM-OCR的项目文件。它的目录结构通常是这样的:
/root/GLM-OCR/
├── serve_gradio.py # 核心服务启动脚本
├── start_vllm.sh # 一键启动脚本
└── ... # 其他配置和文档
启动服务非常简单,只需要一条命令:
# 进入项目目录
cd /root/GLM-OCR
# 执行启动脚本
./start_vllm.sh
运行这个脚本后,它会自动激活所需的Python环境(通常是py310),并启动一个Gradio Web服务。
首次启动时,由于需要从本地缓存加载约2.5GB的模型文件,可能需要等待1-2分钟。看到终端输出类似 Running on local URL: http://0.0.0.0:7860 的信息时,就说明服务启动成功了。
2.2 验证服务是否正常
打开你的浏览器,访问 http://你的服务器IP地址:7860。 你会看到一个简洁的Web界面,可以上传图片,选择任务类型(文本识别、表格识别等)进行测试。这个界面主要用于功能验证和调试,我们批量处理的核心将通过Python API来完成。
确保这个页面能正常打开和交互,我们的“发动机”就算准备就绪了。
3. 编写批量发票处理Python脚本
Web界面一次只能处理一张图,我们要的是批量处理。接下来,我们编写一个Python脚本,这才是自动化的核心。
3.1 脚本核心思路
脚本的逻辑很清晰:
- 连接到我们刚刚启动的GLM-OCR服务。
- 扫描指定文件夹里的所有发票图片(支持.jpg, .png等)。
- 循环处理每一张图片:发送图片和定制化的提示词给模型。
- 接收模型返回的结构化文本(JSON格式)。
- 保存结果,可以按图片单独存,也可以合并成一个总文件。
3.2 完整脚本代码
创建一个文件,比如叫做 batch_process_invoices.py,然后把下面的代码复制进去。代码里有详细的注释,帮你理解每一步在做什么。
import os
import json
import glob
from gradio_client import Client
import time
class GLMOCRInvoiceProcessor:
def __init__(self, server_url="http://localhost:7860"):
"""
初始化处理器,连接到GLM-OCR服务。
server_url: 你的GLM-OCR服务地址,默认是本地7860端口。
"""
print(f"正在连接GLM-OCR服务: {server_url}")
self.client = Client(server_url)
print("连接成功!")
def build_invoice_prompt(self):
"""
构建用于识别发票的提示词(Prompt)。
这个Prompt是告诉模型“你要做什么、按什么格式输出”的关键。
这里我们要求模型以JSON格式输出发票的核心字段。
"""
prompt = """Text Recognition: Please extract key information from this invoice and output in strict JSON format. The JSON must contain the following fields:
- "invoice_number": (string) The invoice serial number.
- "date": (string) The issue date of the invoice.
- "seller": (string) The name of the seller/supplier.
- "buyer": (string) The name of the buyer/purchaser.
- "total_amount": (string) The total amount (including tax), with currency symbol if present.
- "tax_amount": (string) The tax amount.
- "items": (array of objects) A list of purchased items. Each item should have "description", "quantity", "unit_price", and "amount" fields.
Extract only the information clearly present in the invoice. If a field is not found, set its value to null.
Output JSON only, no other text.
"""
return prompt
def process_single_image(self, image_path):
"""
处理单张发票图片。
image_path: 发票图片的完整路径。
返回模型识别的原始结果(字符串)。
"""
print(f"正在处理: {os.path.basename(image_path)}")
try:
# 调用Gradio客户端的predict方法,传入图片路径和定制化的Prompt
result = self.client.predict(
image_path=image_path,
prompt=self.build_invoice_prompt(),
api_name="/predict" # 这是Gradio服务定义的API端点
)
# result 是一个字符串,里面应该包含我们要求的JSON
return result
except Exception as e:
print(f"处理图片 {image_path} 时出错: {e}")
return None
def process_batch(self, image_folder, output_folder="./results"):
"""
批量处理一个文件夹内的所有发票图片。
image_folder: 存放发票图片的文件夹路径。
output_folder: 结果保存的文件夹路径。
"""
# 创建保存结果的文件夹
os.makedirs(output_folder, exist_ok=True)
# 查找文件夹下所有常见的图片文件
image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.webp']
image_paths = []
for ext in image_extensions:
image_paths.extend(glob.glob(os.path.join(image_folder, ext)))
print(f"在文件夹 '{image_folder}' 中找到 {len(image_paths)} 张图片。")
if not image_paths:
print("未找到图片文件,请检查路径和文件格式。")
return
all_results = []
for i, img_path in enumerate(image_paths):
print(f"\n--- 进度 [{i+1}/{len(image_paths)}] ---")
raw_result = self.process_single_image(img_path)
if raw_result:
# 尝试从返回的文本中解析JSON。模型可能返回一些额外文本,我们需要找到JSON部分。
json_str = self._extract_json_from_result(raw_result)
if json_str:
try:
data = json.loads(json_str)
# 为每条数据添加来源文件名
data["source_file"] = os.path.basename(img_path)
all_results.append(data)
# 将单张图片的结果保存为独立的JSON文件
output_file = os.path.join(output_folder, f"{os.path.splitext(os.path.basename(img_path))[0]}.json")
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f"结果已保存至: {output_file}")
except json.JSONDecodeError as e:
print(f"解析JSON失败: {e}。原始返回:{raw_result[:200]}...")
else:
print(f"未能从返回结果中提取JSON。原始返回:{raw_result[:200]}...")
# 为避免请求过快,可以添加短暂间隔(可选)
# time.sleep(0.5)
# 将所有结果合并保存到一个总文件中
if all_results:
summary_file = os.path.join(output_folder, "all_invoices_summary.json")
with open(summary_file, 'w', encoding='utf-8') as f:
json.dump(all_results, f, ensure_ascii=False, indent=2)
print(f"\n所有处理完成!批量处理摘要已保存至: {summary_file}")
print(f"共成功处理 {len(all_results)} 张发票。")
def _extract_json_from_result(self, text):
"""
一个简单的辅助函数,从模型返回的文本中提取JSON字符串。
模型通常会把JSON包裹在 ```json ... ``` 标记中,或者直接输出。
"""
# 尝试查找代码块标记
import re
json_code_block = re.search(r'```(?:json)?\s*(.*?)\s*```', text, re.DOTALL)
if json_code_block:
return json_code_block.group(1).strip()
# 如果没有标记,尝试直接查找第一个 { 和最后一个 }
start = text.find('{')
end = text.rfind('}')
if start != -1 and end != -1 and end > start:
return text[start:end+1]
# 如果还是找不到,返回原始文本(可能它直接就是JSON)
return text.strip()
# 使用示例
if __name__ == "__main__":
# 1. 初始化处理器
processor = GLMOCRInvoiceProcessor("http://localhost:7860") # 确保这里的地址和你的服务匹配
# 2. 指定你的发票图片文件夹路径
invoice_images_folder = "/path/to/your/invoice/images" # 请修改为你的实际路径
# 3. 运行批量处理,结果会保存在当前目录下的 `results` 文件夹里
processor.process_batch(invoice_images_folder)
3.3 如何运行这个脚本
- 将脚本中的
/path/to/your/invoice/images替换成你存放上百张发票图片的真实文件夹路径。 - 在终端中,确保你和脚本在同一个目录下,并且GLM-OCR服务正在运行(
http://localhost:7860可访问)。 - 运行脚本:
python batch_process_invoices.py
然后,你就能看到终端滚动着处理进度,results文件夹里会生成两个文件:每一张发票对应的独立.json文件,以及一个汇总了所有发票数据的all_invoices_summary.json。
4. 解析与使用结构化JSON结果
模型不是万能的,输出可能需要微调。我们来看看怎么理解和优化这些结果。
4.1 结果示例与解读
处理一张发票后,你可能会得到这样一个JSON文件:
{
"invoice_number": "INV-2023-08472",
"date": "2023年11月15日",
"seller": "上海某某科技有限公司",
"buyer": "北京某某设计事务所",
"total_amount": "¥8,850.00",
"tax_amount": "¥1,050.00",
"items": [
{
"description": "专业软件授权许可(一年期)",
"quantity": "2",
"unit_price": "¥3,900.00",
"amount": "¥7,800.00"
},
{
"description": "技术咨询服务",
"quantity": "1",
"unit_price": "¥1,050.00",
"amount": "¥1,050.00"
}
],
"source_file": "invoice_sample1.jpg"
}
这个结构非常清晰,可以直接导入到数据库(如MySQL、MongoDB),或者用Pandas加载进行分析:
import pandas as pd
import json
with open('./results/all_invoices_summary.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# 将数据转换为DataFrame
df = pd.DataFrame(data)
print(df[['invoice_number', 'date', 'seller', 'total_amount']].head())
# 计算总金额统计
df['total_amount_num'] = df['total_amount'].str.replace('¥', '').str.replace(',', '').astype(float)
print(f"发票总张数: {len(df)}")
print(f"总金额: {df['total_amount_num'].sum():.2f}")
4.2 优化识别效果的实用技巧
如果发现某些字段识别不准,可以尝试以下方法优化:
-
精炼你的Prompt:这是最重要的环节。在
build_invoice_prompt函数里,你可以:- 更具体地描述字段:例如
"date": (string) The issue date in 'YYYY-MM-DD' format.强制格式。 - 提供例子:在Prompt中加入
Example output: {"invoice_number": "NO.001", ...},让模型模仿。 - 分步指令:对于复杂发票,可以尝试让模型先描述版面,再提取信息。
- 更具体地描述字段:例如
-
图片预处理:
- 确保图片清晰、端正。可以在批量处理前,用OpenCV等库自动进行旋转校正、去噪、提高对比度。
# 简单的OpenCV旋转校正示例(需安装opencv-python) import cv2 def preprocess_image(image_path): img = cv2.imread(image_path) # 这里可以添加灰度化、二值化、旋转检测等代码 # ... return processed_img_path -
后处理校验:
- 编写简单的规则校验数据,比如检查日期格式、金额是否为数字。对于识别为
null的关键字段,可以记录下文件名,后续人工复核。
- 编写简单的规则校验数据,比如检查日期格式、金额是否为数字。对于识别为
5. 总结
回过头看,我们用GLM-OCR批量处理发票的流程,其实就三步:启动服务、运行脚本、使用数据。这套方法的核心优势在于:
- 效率飞跃:从手动录入或传统OCR的半自动化,升级为全自动结构化输出,处理上百张图片从几小时缩短到几分钟。
- 结果即用:直接获得JSON格式数据,无缝对接后续的财务系统、数据分析平台或数据库。
- 灵活可调:通过修改Prompt,你可以轻松适配不同格式的发票,甚至扩展到合同、报表等其他文档类型。
当然,任何AI模型都不是百分百准确。对于非常重要的财务数据,我建议将本方案作为“强力辅助”,设置一个验证环节(比如金额超过一定阈值需人工确认),这样既能保证效率,又能控制风险。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐



所有评论(0)