Python3 批量处理银行电子回单

使用 Python3 PyMuPDF Camelot 批量处理银行电子回单,PDF 分割,PDF 提取信息。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# A PDF splitting tool for Bank of ChengDe receipts
# Code written by das2m using Copilot on May 22, 2025, at 14:49.import os
import glob
import fitz  # PyMuPDF
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
from tkinter import ttk
from datetime import datetime
import re
import camelot
import uuid
from termcolor import coloredORGANIZATIONNAME='Organization Name'class App:def __init__(self, root):self.root = rootself.root.title("PDF Split")self.root.geometry("800x600")# TKinter Frame 1: Place 2 buttonsself.frame_1st = Frame(root)self.load_button = Button(self.frame_1st, text="Load", command=self.load_files)self.split_button = Button(self.frame_1st, text="Split", command=self.split)self.frame_1st.pack(side="top", anchor="nw", padx=10, pady=10)self.load_button.pack(side="left")self.split_button.pack(side="left")# TKinter Frame 2: Place tableself.frame_2nd = Frame(root)columns = ["Index", "Filename", "Total Pages"]self.table = ttk.Treeview(self.frame_2nd,           # father containercolumns=columns,show="headings",           # hide the first columnselectmode='browse',height=10,                 # lines count)self.table.heading("Index", text='序号')self.table.heading("Filename", text='文件名')self.table.heading("Total Pages", text='总页数')self.table.column("Index", width=50)self.table.column("Filename", width=650)self.table.column("Total Pages", width=100)self.frame_2nd.pack(fill=BOTH, expand=1, padx=10, pady=10)self.table.pack()# TKinter Frame 3: Output informationself.frame_3rd = Frame(root)self.status_bar_label=Label(self.frame_3rd, text="Output:")self.status_bar_text = Text(self.frame_3rd)self.frame_3rd.pack(fill=BOTH, expand=1, padx=10, pady=10)self.status_bar_label.pack(side='top', anchor='nw')self.status_bar_text.pack(fill=BOTH, expand=1)self.pdfs = []def display_status(self, message):self.status_bar_text.tag_add('purple', END)self.status_bar_text.tag_config('purple', foreground='purple')self.status_bar_text.insert(END, message, 'purple')self.status_bar_text.insert(END, '\n', 'purple')self.status_bar_text.see(END)def load_files(self):self.pdfs = filedialog.askopenfilenames(filetypes=(("PDF files", "*.pdf"), ("All files", "*.*")))for i, pdf in enumerate(self.pdfs):doc = fitz.open(pdf)self.table.insert("", "end", values=(i + 1, pdf, doc.page_count, "", ""))self.display_status(f"File {pdf} has loaded!\n")def match_date(self,text):pattern1 = r'(\d{4}年\d{2}月\d{2}日)'match = re.search(pattern1, text)if match:date_str = match.group(1)return date_str.replace("年", "-").replace("月", "-").replace("日", "")else:return Nonedef get_table_info(self, table):print(colored("=====Upper table=====","blue"))#for i in range(table.shape[0]):#    for j in range(table.shape[1]):#        print(f"Row {i}, Column {j}: {table.iloc[i, j]}")info={}info['payer_name']=table.iloc[0,3] # Payer account nameif(info['payer_name']==ORGANIZATIONNAME):info['direction']='pay'else:info['direction']='recipent'# 'Bank branch code \n5900369300013\n313142782061'#info['payer_bank_account']=re.findall(r'\n(\d*)\n',table.iloc[1,3])[0] # Payer account number#info['payer_bank_code']=re.findall(r'\n\d*\n(\d{12})',table.iloc[1,3])[0] # Payer bank branch code#info['payer_bank_name']=table.iloc[2,3] # Payer bank nameinfo['recipient_name']=table.iloc[3,3] # Recipient account name# '50902401040005190\nBank branch code \n103142790248'#info['recipient_account']=re.findall(r'(\d*)\n',table.iloc[4,3])[0] # Recipient account number#info['recipient_code']=re.findall(r'\n.*\n(\d{12})',table.iloc[4,3])[0] # Recipient bank branch code#info['recipient_bank']=table.iloc[5,3] # Recipient bank nameinfo['amount']=re.findall(r'¥(\d*\.\d{2})\n',table.iloc[6,0])[0] # Amount info['purpose']=re.findall('交易渠道\n(.*)\n',table.iloc[7,1])[0] # Purposeinfo['transaction_number']=re.findall(r'\n(\d+)$',table.iloc[10,1])[0] # Transaction numberinfo['verification_code']=re.findall(r'\b[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}\b',table.iloc[11,1]) # Verification codeinfo['print_count']=re.findall(r'打印次数 \n(\d)',table.iloc[11,1])[0] # Print countreturn infodef get_table_info_down(self, table):print(colored("=====Lower table=====","green"))#for i in range(table.shape[0]):#    for j in range(table.shape[1]):#        print(f"Row {i}, Column {j}: {table.iloc[i, j]}")info={}info['payer_name']=table.iloc[0,2] # Payer account nameif(info['payer_name']==ORGANIZATIONNAME):info['direction']='pay'else:info['direction']='recipent'info['recipient_name']=table.iloc[3,2] # Recipient account nameinfo['amount']=re.findall(r'¥(\d*\.\d{2})\n',table.iloc[6,0])[0] # Amount info['purpose']=re.findall('交易渠道\n(.*)\n',table.iloc[7,0])[0] # Purposeinfo['transaction_number']=re.findall(r'\n(\d+)\n',table.iloc[10,0])[0] # Transaction numberinfo['verification_code']=re.findall(r'\b[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}\b',table.iloc[11,0]) # Verification codeinfo['print_count']=re.findall(r'\n(\d)\n',table.iloc[11,0])[0] # Print countreturn infodef split(self):# Clean up target and temp directoriesif os.path.exists('target'):for file in glob.glob('target/*'):os.remove(file)else:os.mkdir('target')if os.path.exists('temp'):for file in glob.glob('temp/*'):self.display_status(f"Found {file} in temp directory, Now deleting it!")os.remove(file)else:os.mkdir('temp')for item in self.table.get_children():values = self.table.item(item)["values"]# Open PDF filedoc = fitz.open(values[1])# Iterate through PDF file (by page count)for page_number in range(len(doc)):# Create a new PDF documentnew_doc = fitz.open()# Copy the current page from the original documentnew_doc.insert_pdf(doc, from_page=page_number, to_page=page_number)# Generate output filenameoutput_file=f"temp/{uuid.uuid4().hex}.pdf"# Save the new documentnew_doc.save(output_file)# Close the new documentnew_doc.close()self.display_status(f"Splitting page {page_number + 1} from {values[1]}")# Get the page to splitpage = doc[page_number]# Calculate the rectangular areas for the upper and lower partspage_rect = page.rectrect1 = fitz.Rect(page_rect.x0, page_rect.y0, page_rect.x1, page_rect.y1 / 2)rect2 = fitz.Rect(page_rect.x0, page_rect.y1 / 2, page_rect.x1, page_rect.y1)# Create two new PDF files to save the split partsnew_doc1 = fitz.open()new_doc2 = fitz.open()# Add the split pages to the new PDF filesnew_page1 = new_doc1.new_page(width=page.rect.width, height=page.rect.height / 2)new_page1.set_mediabox(rect1)new_page1.show_pdf_page(new_page1.rect, doc, page_number, clip=rect1)page_text1= new_page1.get_text("raw")if len(page_text1)>0:date1 = self.match_date(page_text1)new_page2 = new_doc2.new_page(width=page.rect.width, height=page.rect.height / 2)new_page2.set_mediabox(rect2)new_page2.show_pdf_page(new_page2.rect, doc, page_number, clip=rect2)page_text2= new_page2.get_text("raw")if len(page_text2)>0:date2 = self.match_date(page_text2)# Use camelot to read the temporary filetables = camelot.read_pdf(output_file, pages="1", flavor='lattice')if tables.n>0 and not tables[0].df.empty:info = self.get_table_info(tables[0].df)info['date']=date1# Save final files with specified namestarget1 = f"target/{info['date']}-{info['transaction_number']}-{info['purpose']}-{info['payer_name']}-{info['recipient_name']}-{info['amount']}-{info['direction']}-{info['print_count']}.pdf"if os.path.exists(target1):self.display_status(f"File {target1} already exists, Overwriting it!")new_doc1.save(target1)# Close documentnew_doc1.close()if len(tables)>1 and not tables[1].df.empty:info2 = self.get_table_info_down(tables[1].df)info2['date']=date2# Save final files with specified namestarget2 = f"target/{info2['date']}-{info2['transaction_number']}-{info2['purpose']}-{info2['payer_name']}-{info2['recipient_name']}-{info2['amount']}-{info2['direction']}-{info2['print_count']}.pdf"if os.path.exists(target2):self.display_status(f"File {target2} already exists, Overwriting it!")new_doc2.save(target2)new_doc2.close()# Delete temporary fileos.remove(output_file)doc.close()self.display_status(f"Done! You can find the files in the target directory!")# Creating tkinter window
root = Tk()
app = App(root)
root.mainloop()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/81923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信奥赛CSP动态规划入门-最小硬币问题

针对**“最小硬币问题”**的详细分步解析与程序实现,通过将大问题分解为小问题的方式讲解动态规划的应用: 一、问题拆解步骤 1. 明确问题定义 大问题:用面值1元和5元的硬币凑出N元,最少需要多少枚硬币? 小问题&#…

docker多阶段构建镜像

工作中常常用到docker,现在有这样一个场景:已经有一个打好的docker镜像A,主要是包含系统文件(1-3G)、pyhton env(9-10G)文件、模型权重文件modelA(65G)和python代码文件。同时重新安装pyhton env耗时很久(安装torch/cuda/sglang)等等。现在要…

目标检测 RT-DETR(2023)详细解读

文章目录 主干网络:Encoder:不确定性最小Query选择Decoder网络: 将DETR扩展到实时场景,提高了模型的检测速度。网络架构分为三部分组成:主干网络、混合编码器、带有辅助预测头的变换器编码器。具体来说,先利…

学习 Pinia 状态管理【Plan - May - Week 2】

一、定义 Store Store 由 defineStore() 定义,它的第一个参数要求独一无二的id import { defineStore } from piniaexport const useAlertsStore defineStore(alert, {// 配置 })最好使用以 use 开头且以 Store 结尾 (比如 useUserStore,useCartStore&a…

智能制造:基于AI制造企业解决方案架构设计【附全文阅读】

该方案面向制造企业管理者、技术工程师及智能制造转型需求企业,聚焦AI技术在制造业的落地应用。核心内容以AI开放平台为基础,构建“感知层-算法层-平台层-认知层”技术架构,提供从数据采集、模型训练到智能检测的全流程解决方案。 方案通过机…

JVM 高质量面试题

📌 文章目录 一、JVM 内存结构与运行时模型1. JVM 内存结构分区及作用2. 栈帧结构及方法调用链维护3. 逃逸分析及其对对象分配策略的影响4. TLAB 的作用及提升对象创建效率的机制二、垃圾回收器与 GC 调优1. CMS 与 G1 垃圾收集器的设计区别及适用场景2. Full GC 频繁问题的排…

使用Spring Boot和Spring Security构建安全的RESTful API

使用Spring Boot和Spring Security构建安全的RESTful API 引言 在现代Web开发中,安全性是一个不可忽视的重要方面。Spring Boot和Spring Security是Java生态中广泛使用的框架,它们提供了强大的工具来保护RESTful API。本文将介绍如何结合这两个框架&am…

Web项目流程总结

前端 1.下载pnpm管理器 pnpm是一种JavaScript包管理器。 前提:确保安装了Node.js 和 npm 打开cmd,以管理员身份运行(输入以下指令) npm install -g pnpm 安装成功后显示: npm: 这是 Node.js 的包管理工具&#xf…

Java中static关键字深度解析:从入门到高阶实战

Java中static关键字深度解析:从入门到高阶实战 目录 static的本质与核心特性静态变量 vs 实例变量:底层对比静态方法的设计哲学与应用场景高级用法:突破常规的static技巧 4.1 静态代码块:类加载的“初始化引擎”4.2 静态内部类&…

学习人工智能:从0到1的破局指南与职业成长路径

当AI绘画工具在几秒内生成一幅媲美专业画师的作品,当AI程序员自主优化代码逻辑,当AI客服精准解答复杂问题——一个现实愈发清晰:人工智能(AI)不再是科技公司的专属游戏,而是每个普通人必须直面的时代命题。…

2025年医美行业报告60+份汇总解读 | 附 PDF 下载

原文链接:https://tecdat.cn/?p42122 医美行业在消费升级与技术迭代的双重驱动下,已从边缘市场逐步走向主流。数据显示,2024 年中国医美市场规模突破 3000 亿元,年复合增长率达 15%,但行业仍面临正品率不足、区域发展…

深入了解Springboot框架的启动流程

目录 1、介绍 2、执行流程 1、运行run方法 2、初始化SpringApplication对象 1、确定容器类型 3、加载所有的初始化器 4、加载Spring上下文监听器 5、设置程序运行的主类 3、进入run方法 1、开启计时器 2、Headless模式配置 3、获取并启用监听器 4、准备环境 1、设…

【Java多态】:灵活编程的核心

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:【Java】内容概括 【前言】 在Java面向对象编程的世界中,多(Polymorphism) 是一个核…

Python打卡训练营day33——2025.05.22

知识点回顾: PyTorch和cuda的安装 查看显卡信息的命令行命令(cmd中使用) cuda的检查 简单神经网络的流程 数据预处理(归一化、转换成张量) 模型的定义 继承nn.Module类 定义每一个层 定义前向传播流程 定义损失函数和优…

uni-app学习笔记九-vue3 v-for指令

v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法&#xff0c;其中 items 是源数据的数组&#xff0c;而 item 是迭代项的别名&#xff1a; <template><view v-for"(item,index) in 10" :key"index"…

【C++算法】70.队列+宽搜_N 叉树的层序遍历

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a; 题目链接&#xff1a; 429. N 叉树的层序遍历 题目描述&#xff1a; 解法 使用队列层序遍历就可以了。 先入根节点1。queue&#xff1a;1 然后出根节点1&#xff0c;入孩子节点2&#xff0c;3&a…

pycharm无法正常调试问题

pycharm无法正常调试问题 1.错误代码 已连接到 pydev 调试器(内部版本号 231.8109.197)Traceback (most recent call last):File "E:\Python\pycharm\PyCharm 2023.1\plugins\python\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 304, in _on_runr r.deco…

【机器学习基础】机器学习入门核心算法:线性回归(Linear Regression)

机器学习入门核心算法&#xff1a;线性回归&#xff08;Linear Regression&#xff09; 1. 算法逻辑2. 算法原理与数学推导3. 评估指标4. 应用案例5. 面试题6. 扩展分析总结 1. 算法逻辑 核心思想 通过线性方程拟合数据&#xff0c;最小化预测值与真实值的误差平方和&#xff0…

手机打电话时由对方DTMF响应切换多级IVR语音菜单(话术脚本与实战)

手机打电话时由对方DTMF响应切换多级IVR语音菜单 &#xff08;话术脚本与实战&#xff09; --本地AI电话机器人 上一篇&#xff1a;手机打电话时由对方DTMF响应切换多级IVR语音应答&#xff08;二&#xff09; 下一篇&#xff1a;手机打电话时由对方DTMF响应切换多级IVR语音…

flutter dart class语法说明、示例

&#x1f539; Dart 中的 class 基本语法 class ClassName {// 属性&#xff08;字段&#xff09;数据类型 属性名;// 构造函数ClassName(this.属性名);// 方法返回类型 方法名() {// 方法体} }✅ 示例&#xff1a;创建一个简单的 Person 类 class Person {// 属性String name;…