GitHub项目地址:
点击进入一、项目背景
在处理中登系统导出的证券持有变更信息PDF时,需要:
- 批量读取 PDF
- 提取:
- 证券子账户号码
- 持有人名称
- 自动生成规范命名文件
- 保留原文件
- 输出至独立子文件夹
- 支持拖拽批量处理
为提升效率,开发本地离线可执行工具。
二、功能需求说明
1.输入方式
支持:
- 拖拽单个 PDF
- 拖拽多个 PDF
- 拖拽文件夹
- 拖拽多个文件夹
- 混合拖拽
- 双击运行后弹窗选择文件夹
2.命名规则
新文件名 = 证券子账户号码-持有人名称.pdf
例如:
A290074561-李大强.pdf
3.输出规则
- 不修改原文件
- 在原目录生成子文件夹:
已重命名输出
- 所有新文件复制到该文件夹
- 自动防止重名:
A290074561-李大强(2).pdf
4.日志生成
自动生成:
重命名日志.csv
内容:
| 源文件名 | 输出文件名 |
三、技术实现方案
技术栈
- Python 3.13
- pdfplumber(PDF文本解析)
- shutil(文件复制)
- PyInstaller(打包EXE)
四、核心代码实现
字段提取逻辑
m1 = re.search(r"证券子账户号码\s*:\s*([A-Za-z0-9]+)", text) m2 = re.search(r"持有人名称\s*:\s*(\S+)", text)
新文件名生成
new_filename = f"{sub_account}-{holder}.pdf"
防重名机制
A290074561-李大强.pdf A290074561-李大强(2).pdf A290074561-李大强(3).pdf
复制而非重命名
shutil.copy2(src_path, dst_path)
保证:
- 原文件保留
- 时间戳保留
五、完整最终脚本
import os
import sys
import re
import time
import csv
import shutil
import pdfplumber
OUTPUT_DIR_NAME = "已重命名输出"
def pick_folder_dialog():
try:
import tkinter as tk
from tkinter import filedialog
root = tk.Tk()
root.withdraw()
root.attributes("-topmost", True)
folder = filedialog.askdirectory(title="请选择要处理的PDF文件夹")
root.destroy()
return folder
except Exception:
return ""
def extract_fields(pdf_path: str):
with pdfplumber.open(pdf_path) as pdf:
text = (pdf.pages[0].extract_text() or "")
# 提取证券子账户号码
m1 = re.search(r"证券子账户号码\s*:\s*([A-Za-z0-9]+)", text)
sub_account = m1.group(1) if m1 else ""
# 提取持有人名称
m2 = re.search(r"持有人名称\s*:\s*(\S+)", text)
holder_name = m2.group(1) if m2 else ""
return sub_account, holder_name
def safe_target_path(folder: str, filename: str) -> str:
target = os.path.join(folder, filename)
if not os.path.exists(target):
return target
base, ext = os.path.splitext(filename)
k = 2
while True:
new_name = f"{base}({k}){ext}"
new_path = os.path.join(folder, new_name)
if not os.path.exists(new_path):
return new_path
k += 1
def collect_pdfs_from_folder(folder: str):
return [
os.path.join(folder, n)
for n in os.listdir(folder)
if n.lower().endswith(".pdf")
]
def process_pdf_files(pdf_files):
by_folder = {}
for p in pdf_files:
by_folder.setdefault(os.path.dirname(p), []).append(p)
for src_folder, files in by_folder.items():
out_folder = os.path.join(src_folder, OUTPUT_DIR_NAME)
os.makedirs(out_folder, exist_ok=True)
print(f"\n来源目录: {src_folder}")
print(f"输出目录: {out_folder}")
print("-" * 60)
records = []
ok = 0
for src_path in files:
src_name = os.path.basename(src_path)
try:
sub_account, holder = extract_fields(src_path)
if not sub_account:
print(f"未找到证券子账户号码: {src_name}")
continue
if not holder:
print(f"未找到持有人名称: {src_name}")
continue
new_filename = f"{sub_account}-{holder}.pdf"
dst_path = safe_target_path(out_folder, new_filename)
shutil.copy2(src_path, dst_path)
records.append([src_name, os.path.basename(dst_path)])
print(f"已生成: {os.path.basename(dst_path)}")
ok += 1
except Exception as e:
print(f"处理失败: {src_name} | {e}")
log_path = os.path.join(out_folder, "重命名日志.csv")
try:
with open(log_path, "w", newline="", encoding="utf-8-sig") as f:
w = csv.writer(f)
w.writerow(["源文件名", "输出文件名"])
w.writerows(records)
except Exception as e:
print(f"写日志失败: {e}")
print("\n完成:成功输出", ok, "个文件")
print("日志位置:", log_path)
def main():
args = sys.argv[1:]
if not args:
folder = pick_folder_dialog()
if folder:
process_pdf_files(collect_pdfs_from_folder(folder))
else:
print("未选择文件夹。")
input("\n按回车退出...")
return
all_pdfs = []
for raw in args:
p = raw.strip('"')
if os.path.isdir(p):
all_pdfs.extend(collect_pdfs_from_folder(p))
elif os.path.isfile(p) and p.lower().endswith(".pdf"):
all_pdfs.append(os.path.abspath(p))
else:
print(f"跳过: {p}")
if not all_pdfs:
print("未找到可处理的PDF文件。")
input("\n按回车退出...")
return
process_pdf_files(all_pdfs)
input("\n全部处理完成,按回车退出...")
if __name__ == "__main__":
main()
六、打包EXE流程
1.创建干净虚拟环境
python -m venv packenv .\packenv\Scripts\activate pip install -U pip pip install pyinstaller pdfplumber
2.打包命令
pyinstaller --onefile --console --clean --noconfirm rename_pdf_drag.py -n PDF重命名工具
3.输出结果
生成:
dist\PDF重命名工具.exe
七、常见问题及解决方案
1.拖拽无反应
原因:
- 使用管理员运行
- 拖拽方式错误
解决:
- 不要以管理员身份运行
- 直接拖文件/文件夹到exe图标
2.打包卡在 PKG 阶段
原因:
- 全局环境包含 torch / pandas / scipy 等大型库
解决:
- 使用干净 venv 打包
3. dist 文件夹为空
可能:
- 杀毒软件拦截
- 打包未完成
八、最终目录结构示例
某目录 ├── 原PDF文件 ├── 已重命名输出 │ ├── A290074561-李大强.pdf │ ├── 重命名日志.csv
九、安全与合规说明
- 本工具完全本地运行
- 不联网
- 不上传数据
- 适用于证券登记类敏感文件的内网处理
