0

Python+PaddleOCR实现电子档案智能分类:财务人员的自动化提效实战

2026.06.03 | youres | 20次围观

从手动整理300份合同说起

去年底接手一个烂尾项目:把公司近三年的采购合同全部电子化归档。300多份PDF扫描件,每份少则5页多则30页,全部混在一起,没有目录,没有分类,文件名就是日期加一串编号。

我的第一反应是找实习生——但想想时薪和出错率,最后还是决定自己动手。先试了OCR在线识别平台,识别准确率感人,大量表格数据对不齐。花了三天识别完之后,更崩溃的事情来了:要把识别出来的文本归类到"合同主体"、"付款条款"、"验收条款"等十几类中,纯手工复制粘贴,又花了五天。

那段时间每天下班都腰酸背痛。后来痛定思痛,用Python+PaddleOCR搭了一套自动化分类流水线,类似的活现在半天就能搞定。这篇文章就复盘整个方案,包含踩过的坑和最终落地的完整代码。

为什么选PaddleOCR而不是Tesseract

选型阶段对比了三个主流方案:Tesseract、EasyOCR和PaddleOCR。Tesseract英文识别不错,但中文表格简直是灾难;EasyOCR上手快,准确率对中文场景不够理想;最终选了PaddleOCR,原因很实际:

  • 中文场景优化:PaddleOCR是百度开源的,中文识别准确率在我测试的三个方案里最高,尤其是中文合同里常见的宋体、仿宋体
  • 表格识别内置支持:原生支持表格结构还原,不用自己写后处理
  • 部署方便:pip install paddleocr一条命令搞定,不用配环境变量

硬件要求方面,CPU可以跑,但速度感人——我实测识别一份20页的合同PDF,CPU需要3-5分钟,GPU(RTX 3060)只需要15-20秒。有批量处理需求的话,建议配个GPU。

完整方案架构

整个流水线分四步走:

PDF/图片 → OCR识别 → 关键词分类 → 归档输出
   ↓           ↓           ↓           ↓
读取文件    PaddleOCR   规则引擎      按分类存放
  ↓           ↓           ↓           ↓
转图片      结构化文本   置信度判断    自动命名

核心逻辑是:OCR识别后,对文本内容做关键词匹配和置信度打分,根据分值决定分类。分类结果写入对应目录,文件名自动按"日期_合同编号_分类"格式重命名。

环境搭建:三行命令跑起来

pip install paddleocr paddlepaddle  # CPU版本
# pip install paddlepaddle-gpu      # GPU版本(需要NVIDIA驱动)

# 国内镜像加速安装
pip install paddleocr -i https://mirror.baidu.com/pypi/simple

GPU版本安装有个坑要注意:如果同时装了CPU和GPU版本,运行时可能会随机选版本,导致速度不稳定。建议先pip uninstall paddlepaddle,再装GPU版:

pip uninstall paddlepaddle paddlepaddle-gpu -y
pip install paddlepaddle-gpu -i https://mirror.baidu.com/pypi/simple

安装完成后验证一下GPU是否正常调用:

import paddle
print(paddle.device.cuda.device_count())  # 返回GPU数量

OCR识别模块:核心代码

我封装了一个FileOCR类,统一处理PDF和图片:

import os
from paddleocr import PaddleOCR
from pdf2image import convert_from_path
import numpy as np
from PIL import Image

class FileOCR:
    def __init__(self, use_gpu=True):
        self.ocr = PaddleOCR(
            use_angle_cls=True,
            lang='ch',
            use_gpu=use_gpu,
            show_log=False,
            rec_algorithm='CRNN'  # 轻量算法,速度优先
        )
    
    def extract_text_from_image(self, img_path):
        """从单张图片提取文本"""
        result = self.ocr.ocr(img_path, cls=True)
        texts = []
        for line in result[0] if result and result[0] else []:
            text = line[1][0]
            confidence = line[1][1]
            texts.append({"text": text, "conf": confidence})
        return texts
    
    def extract_text_from_pdf(self, pdf_path, dpi=200):
        """从PDF提取文本(每页转图片后OCR)"""
        images = convert_from_path(pdf_path, dpi=dpi)
        all_texts = []
        for i, image in enumerate(images):
            img_array = np.array(image)
            result = self.ocr.ocr(img_array, cls=True)
            page_texts = []
            for line in result[0] if result and result[0] else []:
                text = line[1][0]
                confidence = line[1][1]
                page_texts.append({"text": text, "conf": confidence})
            all_texts.append({"page": i+1, "content": page_texts})
        return all_texts

这里有个关键参数dpi:设置越高识别越准确,但速度越慢。扫描件质量好的设150就够,模糊的老档案建议设300。实测同样一份合同,150dpi和300dpi的识别时间差3倍,准确率差距不到2%。

分类引擎:规则+置信度双保险

分类逻辑我用的是关键词权重打分:先定义每个分类的关键词库,然后对OCR识别出来的文本做匹配计数,最后按权重加总得出分类结论。

class DocumentClassifier:
    def __init__(self):
        # 定义分类关键词库(带权重)
        self.categories = {
            "合同主体": {
                "keywords": ["甲方","乙方","合同编号","签订日期","合同金额","合同期限"],
                "weight": 1.5
            },
            "付款条款": {
                "keywords": ["付款方式","付款期限","预付款","尾款","月结","对公转账","银行账号"],
                "weight": 1.2
            },
            "验收条款": {
                "keywords": ["验收标准","验收方式","验收期限","交付标准","验收报告"],
                "weight": 1.2
            },
            "保密协议": {
                "keywords": ["保密义务","保密期限","违约金","商业秘密","保密协议"],
                "weight": 1.0
            },
            "通用文件": {
                "keywords": [],
                "weight": 0.5
            }
        }
    
    def classify(self, ocr_result):
        """对OCR结果进行分类"""
        # 合并所有页面的文本
        all_text = " ".join([
            item["text"] 
            for page in ocr_result 
            for item in page["content"]
        ])
        
        scores = {}
        for cat, config in self.categories.items():
            if cat == "通用文件":
                continue
            score = 0
            for kw in config["keywords"]:
                count = all_text.count(kw)
                if count > 0:
                    score += count * config["weight"]
            scores[cat] = score
        
        if not scores or max(scores.values()) == 0:
            return "通用文件", 0.0
        
        best_cat = max(scores, key=scores.get)
        confidence = min(scores[best_cat] / 10, 1.0)  # 归一化到0-1
        return best_cat, confidence

这个方案有个局限性:纯关键词匹配对表述多样的文本效果一般。比如"甲乙双方协商确定"这段话,表面是合同主体,但实际可能是其他条款的一部分。后来我在关键词库里加了一个"置信度阈值"——低于0.3的不直接分类,而是标记为"待审核",手动处理。

批量处理主流程

def batch_process(input_dir, output_base):
    ocr = FileOCR(use_gpu=True)
    classifier = DocumentClassifier()
    
    files = [f for f in os.listdir(input_dir) 
             if f.lower().endswith(('.pdf', '.jpg', '.png'))]
    
    results = []
    for i, file in enumerate(files):
        print(f"[{i+1}/{len(files)}] 处理中: {file}")
        
        filepath = os.path.join(input_dir, file)
        if filepath.endswith('.pdf'):
            text_data = ocr.extract_text_from_pdf(filepath)
        else:
            text_data = [{"page": 1, "content": ocr.extract_text_from_image(filepath)}]
        
        category, conf = classifier.classify(text_data)
        
        # 提取日期和编号(简单正则)
        import re
        date_match = re.search(r'd{4}[-/]d{2}[-/]d{2}', file)
        date_str = date_match.group().replace('-', '') if date_match else 'nodate'
        new_filename = f"{date_str}_{os.path.splitext(file)[0]}_{category}.pdf"
        
        # 按分类创建目录并保存
        cat_dir = os.path.join(output_base, category)
        os.makedirs(cat_dir, exist_ok=True)
        shutil.copy(filepath, os.path.join(cat_dir, new_filename))
        
        results.append({
            "file": file,
            "category": category,
            "confidence": round(conf, 2)
        })
        print(f"  → 分类: {category} (置信度: {conf:.0%})")
    
    return results

实战效果与坑点总结

用这套方案处理前面的300份合同:

指标手动处理自动化方案
总耗时8天(约64小时)6小时
准确率约95%(取决于疲劳程度)约92%(需人工复核低置信度)
一致性前后标准不一统一分类标准
人力成本约2000元(实习生)一次性开发成本

准确率略低于纯手工的原因主要是:部分扫描件字迹模糊,或者合同格式不规范(不同公司模板差异很大)。不过可以通过扩充关键词库来提升。

进阶优化方向

当前方案还有优化空间,列几个我验证过有效的:

  • 接大模型做语义分类:OCR后接DeepSeek API做分类判断,准确率能到97%以上,但成本高,适合高价值文档
  • 训练专用分类模型:如果文档格式固定,可以收集一批标注数据微调分类器,长期收益大
  • 多语言支持:PaddleOCR支持多语言,但需要单独下载模型包,中英双语合同建议切换lang参数
  • 增量处理:加一个已处理文件记录表,定期扫描新文件自动处理,适合持续归档场景

写在最后

这套方案不是银弹,它的价值在于把"需要专注力"的重复工作变成"可以喝着咖啡等它跑完"的自动化任务。OCR+规则分类的组合适合标准化程度较高的文档,如果是完全非标准的文件(比如手写信件、涂改过的表格),还是得靠人工。

但至少,它让我从那300份合同里解放了出来——现在那些时间可以用来写代码、写方案、写这篇复盘文章。技术终归是为人服务的,解放时间才是硬道理。

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论