别再用Ctrl+F找合同了!这个Python脚本让效率暴涨10倍(附源码-自动化办公神器)

222 阅读8分钟

大家好呀,我是你们的花姐!上周公司法务部的小美跟我吐槽,说她每天就像个人肉扫描仪,要在几百份合同里找什么金额、签约方这些信息。我好奇问了一句:"你们天天挖这些数据到底图啥?" 她当场给我上了一课...


一、搞懂小美的需求

小美巴拉巴拉说了一大堆,我总结了下核心其实就是:法务部需要从大量合同中提取关键信息,用于大额合同监控、签约方背景核查、有效期动态追踪等。

简单说就是法务部需从300份PDF合同中提取「合同金额」「签约方」「有效期」等字段 假如我们的合同是下面这种样式

image.png


二、Python自动化三件套

第一步:装备你的数字助理
在电脑搜索栏输入"cmd"打开小黑窗,粘贴这两行魔法:

pip install pdfplumber pandas

(这是在给你的电脑安装两个小帮手:PDF拆解专家+数据整理大师)

第二步:复制花姐的智能扫描仪
新建"合同小助手.py"文件,粘贴以下代码:

import pdfplumber
import re
import pandas as pd
import os

def contract_scanner(pdf_path):
    try:
        with pdfplumber.open(pdf_path) as pdf:
            text = ''.join([page.extract_text() for page in pdf.pages if page.extract_text()])  # 防止某些页面没有文本
        
        if not text:
            print(f"警告: 无法从 {pdf_path} 提取文本。")
            return None
        
        # 信息提取规则:使用正则表达式进行匹配
        try:
            # 提取合同总价
            amount_match = re.search(r'合同总价:\s*¥([\d,]+)', text)
            amount = amount_match.group(1) if amount_match else None

            # 提取甲乙方
            parties_match = re.search(r'甲方:\s*(.*?)\n乙方:\s*(.*?)\n', text)
            partyA, partyB = (parties_match.group(1), parties_match.group(2)) if parties_match else (None, None)

            # 提取有效期至
            validity_match = re.search(r'有效期至:\s*(20\d{2}-\d{2}-\d{2})', text)
            validity = validity_match.group(1) if validity_match else None

            # 提取签订日期
            signed_on_match = re.search(r'签订日期:\s*(\d{4}-\d{2}-\d{2})', text)
            signed_on = signed_on_match.group(1) if signed_on_match else None

            return [amount, partyA, partyB, validity, signed_on]
        
        except Exception as e:
            print(f"提取信息时发生错误:{e}")
            return None
    
    except Exception as e:
        print(f"处理文件 {pdf_path} 时发生错误:{e}")
        return None

# 批量处理流程
all_contracts = []
for file in os.listdir('contracts'):
    if file.endswith('.pdf'):
        contract_data = contract_scanner(f'contracts/{file}')
        if contract_data:
            # 将文件名添加到数据中
            contract_data.append(file)
            all_contracts.append(contract_data)

# 生成结构化数据并保存为 Excel 文件
if all_contracts:
    # 表头改为中文,并将文件名列添加到DataFrame
    df = pd.DataFrame(all_contracts, 
                      columns=['合同总价', '甲方', '乙方', '有效期至', '签订日期', '文件名']
                     )
    df.to_excel('合同数据库.xlsx', index=False)
    print("合同数据已保存到 '合同数据库.xlsx'")
else:
    print("没有提取到任何合同数据。")

第三步:见证奇迹

  1. 把所有合同PDF扔进"contracts"文件夹
  2. 双击运行这个py文件
  3. 打开生成的Excel,你会看到这样的魔法阵:

image.png


核心代码解析

这个脚本是用来扫描PDF合同,提取特定信息,然后生成Excel文件的。主要用了pdfplumber库来提取文本,然后用正则表达式匹配需要的信息,最后用pandas保存成Excel,属于Python基础编程的范畴。

  1. 导入必要的库

    import pdfplumber
    import re
    import pandas as pd
    import os
    

    pdfplumber 用来“打开”PDF文件,re 用来查找特定的文本模式,pandas 负责将提取的数据变成表格,os 用来处理文件操作。🔍

  2. 定义扫描合同的函数

    def contract_scanner(pdf_path):
        try:
            with pdfplumber.open(pdf_path) as pdf:
                text = ''.join([page.extract_text() for page in pdf.pages if page.extract_text()])
    

    这个函数就像一个“合同扫描仪”📄,它从给定的 PDF 文件中提取文本。你可以把它想象成用放大镜去查看文件内容,看看能不能找到我们需要的信息。

  3. 处理没有文本的情况

    if not text:
        print(f"警告: 无法从 {pdf_path} 提取文本。")
        return None
    

    如果 PDF 文件里没有文本,那就像找到了一个空白的纸张,什么都没有!所以我们就给出一个“警告”,告诉你文件里面什么也没有。

  4. 用正则表达式提取合同信息

    amount_match = re.search(r'合同总价:\s*¥([\d,]+)', text)
    

    正则表达式是搜索文本的超级大法,像是在合同里寻找“合同总价”这一行,它会匹配文本里符合格式的部分,比如价格是多少!💰 其实,re.search 就是“钓鱼”的时候丢下的“鱼钩”,看看能不能钓到合同里包含价格的部分。

  5. 提取甲乙方信息

    parties_match = re.search(r'甲方:\s*(.*?)\n乙方:\s*(.*?)\n', text)
    

    然后,我们还要查找合同的甲方和乙方是谁。用正则表达式就像是在合同里找名字,“甲方”和“乙方”的名字就藏在特定的地方,它们不容易逃脱我们的法眼!👀

  6. 处理错误的情况

    except Exception as e:
        print(f"提取信息时发生错误:{e}")
        return None
    

    万一在提取过程中遇到错误,比如格式不对,系统会及时提醒,避免你在数据里踩坑!😅

  7. 批量处理多个合同文件

    all_contracts = []
    for file in os.listdir('contracts'):
        if file.endswith('.pdf'):
            contract_data = contract_scanner(f'contracts/{file}')
            if contract_data:
                contract_data.append(file)
                all_contracts.append(contract_data)
    

    这部分是为了处理文件夹里一大堆的合同文件,如果文件名以 .pdf 结尾,就丢给我们的“合同扫描仪”去扫描,然后把扫描到的信息放到 all_contracts 这个大盒子里!📦

  8. 保存数据为 Excel 文件

    if all_contracts:
        df = pd.DataFrame(all_contracts, columns=['合同总价', '甲方', '乙方', '有效期至', '签订日期', '文件名'])
        df.to_excel('合同数据库.xlsx', index=False)
        print("合同数据已保存到 '合同数据库.xlsx'")
    

    最后,我们把这些扫描到的数据整理成一个 Excel 文件。就像是把一堆杂乱无章的纸张整理成一本整齐的文件夹📂,并且还给每个合同加上了文件名,防止以后找不到是哪一个合同。👩‍💻

  9. 没提取到数据怎么办

    else:
        print("没有提取到任何合同数据。")
    

    如果没有提取到数据,那就告诉你“没有合同数据”——这个时候,你就知道是时候检查一下是不是合同内容本身有问题了!😬


扩展学习建议

这段代码展示了如何从PDF合同文件中提取关键信息,并将提取到的数据保存为Excel表格。感兴趣的同学可以根据自己需求进一步拓展:

1. 扩展正则表达式的应用

  • 更多字段提取:当前代码提取了合同总价、甲乙方、有效期至和签订日期,可以考虑扩展提取更多合同信息,例如:合同编号、付款方式、违约条款、合同状态等。
  • 正则表达式优化:当前的正则表达式较为简单,有时可能会出现匹配不精确的情况。可以尝试使用更复杂的正则模式,或者结合机器学习模型提高提取准确性。

2. 处理复杂的PDF结构

  • 多列/表格提取:如果合同中有表格结构,pdfplumber提供了更强大的表格提取功能。你可以尝试提取表格数据并将其结构化。
  • 处理扫描版PDF:如果合同是扫描版PDF,pdfplumber可能无法直接提取文本。在这种情况下,可以结合OCR技术(如Tesseract)进行图像文字识别。

3. 提高错误处理和健壮性

  • 捕获更多的异常:除了文本提取失败,还可以考虑捕获PDF打开失败、文件损坏、权限问题等其他错误。
  • 日志记录:使用logging模块替换print语句,记录详细的错误信息,方便后续的调试和分析。

4. 优化数据存储和管理

  • 数据库存储:目前数据保存为Excel文件,可以考虑将数据存储到数据库(如MySQL、PostgreSQL等),便于大规模数据管理和查询。
  • 数据清洗:在存储数据之前,可以对提取的信息进行进一步的清洗,例如去掉多余的空格,格式化日期等,保证数据的一致性和准确性。

5. 批量处理和性能优化

  • 多线程或异步处理:当前代码是顺序处理多个文件,若处理文件数较多时,可能会导致性能瓶颈。可以使用多线程处理,提升效率。
  • 分批次处理:对合同文件进行分批处理,避免一次性处理过多文件时内存占用过高。可以根据文件大小或者处理时间进行分批。

6. 界面和可视化

  • 图形用户界面(GUI):你可以考虑为该工具添加一个图形用户界面,使用户可以轻松上传PDF文件,并查看提取结果。例如,使用tkinterPyQt构建GUI。
  • 数据可视化:将提取的合同数据进行可视化分析(如合同总价分布、甲乙方数量统计等),可以使用matplotlibseaborn进行图表绘制。

7. 自动化和定时任务

  • 定期扫描新合同:如果合同文件夹中有新的文件不断增加,可以将这个功能自动化,每天定时扫描并提取新合同数据。
  • 通知和报告:如果程序提取到有异常(如无法提取某些信息),可以通过邮件等方式通知相关人员。

通过以上扩展,你可以让这个项目更加健壮、灵活,并能处理更复杂的合同信息提取任务。


花姐说

技术不是要取代法务同学,而是把你们从重复劳动中解放出来。就像当初Excel取代算盘,Python也不过是我们新的工具伙伴。哪怕你暂时看不懂代码,先学会用这些工具,就能把时间花在更有价值的地方——比如研究怎么用法律AI预防风险。