基于关键字定位的自动化PDF合同拆分

需求背景:

  1. 问题描述

    我有一份包含多份合同的PDF文件,需要将这些合同分开并进行解析。

    传统方法(如以固定页数作为分割点)不够灵活,无法满足需求。

  2. 现有方法的不足

    网上找到的工具大多依赖手动输入页数作为分割点,这种方式不够智能,且需要用户提前知道每份合同的页数范围,效率较低。

灵感核心:

  1. 动态分割点

    通过输入一个唯一关键字(如"合同编号"、"甲方"等)来自动定位合同的分割点,从而实现自动分割。

  2. 实现步骤:

    1、将PDF文件的每一页转换为图片。

    2、使用OCR技术识别图片内容,提取关键字。

    3、定位关键字所在的页码,并将这些页码作为分割点。

    4、使用PDF处理工具将PDF文件拆分为多个独立文件。

    注意:1,2步是因为我的PDF文件包含图片,读取内容困难,所以采用OCR识别技术提取文字。如果你的不是就可以修改成直接读取pdf文件内容。

代码实现

1、用到了百度OCR所以需要去获取access_token,代码如下:

python 复制代码
import requests
import json

"""
client_id,client_secret去百度OCR中获取
详情看链接:https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu
"""

def getAccessToken(client_id, client_secret):
    url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}"

    payload = ""
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    if str(response) == "<Response [200]>":
        # print(response.text)
        # 将JSON字符串解析为字典
        data = json.loads(response.text)
        token = data.get("access_token")
        print("执行成功:", response)
        print("access_token:", token)
        return token
    else:
        print("错误信息:", response.content)
        return response

2、具体实现代码

需要提前下载以下库:PyMuPDF,requests,PyPDF2

python 复制代码
import re
from PyPDF2 import PdfReader, PdfWriter
import fitz  # PyMuPDF
import base64
import os
import requests


def pdf_to_images(pdf_path, output_folder, dpi=300):
    """
    将PDF文件的每一页转换为图片。

    参数:
        pdf_path (str): 输入PDF文件的路径。
        output_folder (str): 输出图片文件夹路径。
        dpi (int): 图片分辨率,默认为300 DPI。
    """
    # 确保输出文件夹存在
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    # 打开PDF文件
    doc = fitz.open(pdf_path)

    # 遍历每一页并转换为图片
    for page_num in range(len(doc)):
        page = doc.load_page(page_num)
        pix = page.get_pixmap(dpi=dpi)
        image_path = os.path.join(output_folder, f"page_{page_num + 1}.png")
        pix.save(image_path)
        print(f"已保存图片:{image_path}")


def perform_ocr(image_path, access_token):
    """
    对单个图片文件进行OCR识别。

    参数:
        image_path (str): 图片文件的路径。
        access_token (str): OCR API的访问令牌。

    返回:
        str: OCR识别结果。
    """
    ocr_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic"
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}

    # 读取图片文件并进行Base64编码
    with open(image_path, 'rb') as img_file:
        img_data = base64.b64encode(img_file.read())

    # 发送OCR请求
    response = requests.post(
        f"{ocr_url}?access_token={access_token}",
        data={'image': img_data},
        headers=headers
    )

    # 解析OCR结果
    if response.status_code == 200:
        ocr_result = response.json()
        return '。'.join(item['words'] for item in ocr_result.get('words_result', []))
    return ""


def find_split_pages(image_folder, access_token, search_text):
    """
    查找包含目标文本的页码。

    参数:
        image_folder (str): 包含图片的文件夹路径。
        access_token (str): OCR API的访问令牌。
        search_text (str): 要查找的文本。

    返回:
        list: 包含目标文本的页码列表。
    """
    split_pages = []

    # 遍历图片文件夹中的所有图片
    for filename in os.listdir(image_folder):
        if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp')):
            image_path = os.path.join(image_folder, filename)
            result = perform_ocr(image_path, access_token)

            # 提取页码并检查是否包含目标文本
            page_num = int(re.findall(r'\d+', filename)[0])
            if search_text in result:
                split_pages.append(page_num)

    return sorted(split_pages)


def split_pdf(input_pdf, split_pages, output_folder):
    """
    根据指定的页码分割PDF文件。

    参数:
        input_pdf (str): 输入PDF文件的路径。
        split_pages (list): 分割点页码列表。
        output_folder (str): 输出文件夹路径。
    """
    # 确保输出文件夹存在
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    # 获取输入PDF的文件名前缀
    output_prefix = os.path.splitext(os.path.basename(input_pdf))[0]

    # 打开PDF文件
    with open(input_pdf, 'rb') as pdf_file:
        reader = PdfReader(pdf_file)
        prev_split = 0

        # 分割PDF
        for part_num, split_page in enumerate(split_pages, 1):
            writer = PdfWriter()
            for page_num in range(prev_split, split_page):
                writer.add_page(reader.pages[page_num])

            output_path = os.path.join(output_folder, f"{output_prefix}_part{part_num}.pdf")
            with open(output_path, 'wb') as output_file:
                writer.write(output_file)
            print(f"已保存分割文件:{output_path}")
            prev_split = split_page

        # 保存剩余部分
        if prev_split < len(reader.pages):
            writer = PdfWriter()
            for page_num in range(prev_split, len(reader.pages)):
                writer.add_page(reader.pages[page_num])

            output_path = os.path.join(output_folder, f"{output_prefix}_part{len(split_pages) + 1}.pdf")
            with open(output_path, 'wb') as output_file:
                writer.write(output_file)
            print(f"已保存最后分割文件:{output_path}")


def main():
    # 输入和输出路径
    input_pdf = r"D:\project\合同\供应商版本采购合同\1月份合同.pdf"
    image_folder = r"D:\project\合同\供应商版本采购合同\分割"
    output_folder = r"D:\project\合同\供应商版本采购合同\分割结果"

    # OCR相关参数
    search_text = "双方买卖约定"
    access_token = "24.2******"

    # 将PDF转换为图片
    pdf_to_images(input_pdf, image_folder)

    # 查找包含目标文本的页码
    split_pages = find_split_pages(image_folder, access_token, search_text)
    if not split_pages:
        print(f"未找到包含文本 '{search_text}' 的页,无法进行分割。")
        return

    # 分割PDF文件
    split_pdf(input_pdf, split_pages, output_folder)


if __name__ == "__main__":
    main()
相关推荐
啥都鼓捣的小yao40 分钟前
Python在糖尿病分类问题上寻找具有最佳 ROC AUC 分数和 PR AUC 分数(决策树、逻辑回归、KNN、SVM)
python·决策树·机器学习·支持向量机·分类·逻辑回归
拖拉机1 小时前
Python(七)函数
后端·python
E-iceblue1 小时前
通过 Python 在PDF中添加、或删除超链接
python·python pdf库·pdf超链接
2401_890666131 小时前
免费送源码:Java+ssm+MySQL 校园二手书销售平台设计与实现 计算机毕业设计原创定制
java·spring boot·python·mysql·小程序·php·课程设计
SHIPKING3931 小时前
【LangChain少样本提示工程实战】FewShotPromptTemplate原理与应用解析——附运行代码
数据库·python·langchain·llm·fewshotprompt
豆豆1 小时前
day24 学习笔记
笔记·python·opencv·学习
辰阳星宇2 小时前
213、【图论】有向图的完全联通(Python)
开发语言·python·图论
慕卿扬2 小时前
基于python的机器学习(六)—— 数据可视化和数据预处理
笔记·python·学习·机器学习·聚类
朗道十戒2 小时前
在ArcGIS Pro中将栅格NoData值修改为特定值
python·arcgis
LouisCanBe2 小时前
MCP Server 项目发布指南
python·npm·mcp