法律案例 PDF 批量转 TXT 工具代码
这份代码是一个多层级法律案例 PDF 批量提取并转换为 TXT 的工具,核心功能是递归查找 PDF 文件、用两种 PDF 库提取文本、按固定格式解析法律案例、保持目录结构保存为 TXT。下面逐行(按函数模块划分,兼顾逻辑连贯性)进行详细说明:
一、导入模块(第 1-2 行)
python
运行
import os
- 功能:导入 Python 内置的
os模块,用于文件路径操作、目录创建、递归遍历文件夹等核心文件系统交互(后续所有路径相关逻辑都依赖该模块)。 - 用途:后续获取脚本目录、拼接文件路径、创建输出目录、遍历 PDF 文件等都需要用到
os模块的方法。
python
运行
import re
- 功能:导入 Python 内置的
re模块(正则表达式模块),用于按法律案例的固定格式匹配、提取文本内容,以及清理非法字符。 - 用途:后续匹配案例格式、清理文件名、去除多余空白行等都依赖正则表达式。
二、获取脚本所在目录函数(第 4-7 行)
python
运行
def get_script_directory():
"""获取脚本所在目录"""
return os.path.dirname(os.path.abspath(__file__))
def get_script_directory()::定义函数get_script_directory,无入参,用于获取当前 Python 脚本文件所在的绝对目录。"""获取脚本所在目录""":文档字符串(docstring),说明函数功能,方便后续查阅和自动生成文档。return os.path.dirname(os.path.abspath(__file__)):__file__:Python 内置变量,代表当前脚本文件的路径(包含文件名本身)。os.path.abspath(__file__):将__file__转换为绝对路径(避免相对路径带来的歧义,无论在哪个目录运行脚本都能获取准确路径)。os.path.dirname(...):提取绝对路径中的目录部分(去掉文件名,只保留脚本所在的文件夹路径)。- 返回值:当前脚本所在的文件夹绝对路径,后续用于设置默认的输入 / 输出目录。
三、PDF 案例提取入口函数(第 9-24 行)
python
运行
def extract_cases_from_pdf(pdf_path):
"""
从PDF文件中提取案例,按照法律案例的标准格式进行识别
尝试使用不同的PDF库
"""
# 尝试使用 PyMuPDF (fitz)
try:
import fitz
return extract_with_pymupdf(pdf_path)
except ImportError:
pass
# 尝试使用 PyPDF2
try:
import PyPDF2
return extract_with_pypdf2(pdf_path)
except ImportError:
pass
# 如果都没有安装,抛出异常
raise ImportError("需要安装 PyMuPDF (pip install PyMuPDF) 或 PyPDF2 (pip install PyPDF2)")
def extract_cases_from_pdf(pdf_path)::定义 PDF 案例提取入口函数,入参pdf_path是目标 PDF 文件的完整路径。- 文档字符串:说明函数核心功能 ------ 从 PDF 提取法律案例,支持两种 PDF 处理库,提高兼容性。
- 第一个
try...except块(PyMuPDF 尝试):try::尝试执行后续代码,捕获导入异常。import fitz:导入fitz(PyMuPDF 库的核心模块,注意安装命令是pip install PyMuPDF,导入时用fitz)。return extract_with_pymupdf(pdf_path):若导入成功,调用专门的extract_with_pymupdf函数处理 PDF,并返回提取结果。except ImportError::捕获「未安装 PyMuPDF」的导入异常。pass:捕获异常后不做任何处理,继续执行后续代码(尝试下一个 PDF 库)。
- 第二个
try...except块(PyPDF2 尝试):- 逻辑与上一个块一致,尝试导入
PyPDF2,导入成功则调用extract_with_pypdf2函数处理 PDF。
- 逻辑与上一个块一致,尝试导入
raise ImportError(...):若两个库都导入失败(都未安装),主动抛出导入异常,提示用户安装任一库,终止函数执行。- 核心设计:兼容两种 PDF 库,无需用户手动选择,降低使用门槛,提高工具的可用性。
四、PyMuPDF 实现 PDF 文本提取(第 26-42 行)
python
运行
def extract_with_pymupdf(pdf_path):
"""使用PyMuPDF提取文本"""
import fitz
# 打开PDF文档
doc = fitz.open(pdf_path)
# 提取所有文本
all_text = ""
for page in doc:
all_text += page.get_text() + "\n"
# 关闭文档
doc.close()
return extract_cases_from_text(all_text)
def extract_with_pymupdf(pdf_path)::定义 PyMuPDF 的 PDF 处理函数,入参pdf_path是目标 PDF 完整路径。- 文档字符串:说明函数使用的库和功能。
import fitz:再次导入fitz(虽然入口函数已导入,但此处单独导入,保证函数的独立性,即使单独调用该函数也能正常运行)。doc = fitz.open(pdf_path):调用fitz.open()打开 PDF 文件,返回 PDF 文档对象doc,后续用于遍历页面、提取文本。all_text = "":初始化空字符串,用于存储 PDF 中提取的所有文本内容。for page in doc::遍历 PDF 文档的每一个页面对象page(doc是可迭代对象,直接遍历即可获取所有页面)。all_text += page.get_text() + "\n":page.get_text():提取当前页面的所有文本内容(PyMuPDF 的文本提取效果优于 PyPDF2,支持更多 PDF 格式,提取的文本排版更规整)。+ "\n":每页文本后添加换行符,避免不同页面的文本粘连,方便后续解析。- 拼接至
all_text,最终得到整个 PDF 的完整文本。
doc.close():关闭 PDF 文档对象,释放系统资源(避免文件句柄泄露)。return extract_cases_from_text(all_text):将提取到的完整文本传入extract_cases_from_text函数(专门的文本解析函数),返回解析后的案例列表。
五、PyPDF2 实现 PDF 文本提取(第 44-59 行)
python
运行
def extract_with_pypdf2(pdf_path):
"""使用PyPDF2提取文本"""
import PyPDF2
with open(pdf_path, 'rb') as file:
pdf_reader = PyPDF2.PdfFileReader(file)
# 提取所有文本
all_text = ""
for page_num in range(pdf_reader.numPages):
page = pdf_reader.getPage(page_num)
all_text += page.extractText() + "\n"
return extract_cases_from_text(all_text)
def extract_with_pypdf2(pdf_path)::定义 PyPDF2 的 PDF 处理函数,入参pdf_path是目标 PDF 完整路径。- 文档字符串:说明函数使用的库和功能。
import PyPDF2:再次导入PyPDF2,保证函数独立性。with open(pdf_path, 'rb') as file::- 使用
with语句打开 PDF 文件,自动管理文件句柄(执行完毕后自动关闭文件,无需手动close(),避免资源泄露)。 - 打开模式
'rb':二进制只读模式 (PDF 是二进制文件,必须用rb模式打开,否则会报错)。 file:打开后的文件对象,供后续PyPDF2读取。
- 使用
pdf_reader = PyPDF2.PdfFileReader(file):创建 PDF 读取器对象pdf_reader,用于读取 PDF 文件的内容和属性。all_text = "":初始化空字符串,存储完整 PDF 文本。for page_num in range(pdf_reader.numPages)::pdf_reader.numPages:获取 PDF 文档的总页数。- 遍历页码(从 0 到总页数 - 1),用于逐个获取页面。
page = pdf_reader.getPage(page_num):根据页码page_num获取对应的页面对象page。all_text += page.extractText() + "\n":page.extractText():提取当前页面的文本内容(PyPDF2 的提取效果稍差,对复杂排版 PDF 的支持不足,可能出现文本错乱)。- 拼接至
all_text,添加换行符分隔页面。
return extract_cases_from_text(all_text):将完整文本传入文本解析函数,返回案例列表(与 PyMuPDF 的处理流程保持一致,保证后续逻辑统一)。
六、文本中提取法律案例(第 61-80 行)
python
运行
def extract_cases_from_text(all_text):
"""
从文本中提取案例,按照法律案例的标准格式进行识别
"""
cases = []
# 匹配案例格式:标题 + 案例标识 + 案件信息
# 匹配模式:任意字符直到换行 + 破折号 + 案名 + 【案件基本信息】 + 后续内容直到下一个案例开始
pattern = r'([^\n]*\n------[^\n]*\n【案件基本信息】.*?)(?=^[^\n]*\n------[^\n]*\n【案件基本信息】|\Z)'
matches = re.findall(pattern, all_text, re.DOTALL | re.MULTILINE)
# 清理匹配结果
for match in matches:
# 去除多余的空白行和空格
cleaned_match = re.sub(r'\n\s*\n', '\n\n', match.strip())
if cleaned_match:
cases.append(cleaned_match)
return cases
def extract_cases_from_text(all_text)::定义文本解析函数,入参all_text是从 PDF 中提取的完整文本内容,核心是按固定格式提取法律案例。- 文档字符串:说明函数功能 ------ 按法律案例标准格式从文本中提取案例。
cases = []:初始化空列表,用于存储最终提取并清理后的案例数据。- 正则表达式模式
pattern:这是案例提取的核心,用于匹配符合格式的法律案例:- 正则表达式:
r'([^\n]*\n------[^\n]*\n【案件基本信息】.*?)(?=^[^\n]*\n------[^\n]*\n【案件基本信息】|\Z)' - 分段解析:
([^\n]*\n------[^\n]*\n【案件基本信息】.*?):捕获组(最终要提取的内容),匹配「任意非换行字符 + 换行 + 破折号开头的行 + 换行 +【案件基本信息】+ 后续任意内容(非贪婪匹配)」。(?=...):正向先行断言(不捕获内容,只用于界定匹配边界),用于确定案例的结束位置:要么是下一个案例的开始(与捕获组开头格式一致),要么是文本末尾(\Z)。
- 匹配目标:精准匹配以「------」开头、包含「【案件基本信息】」的法律案例,且能自动分割多个连续案例。
- 正则表达式:
matches = re.findall(pattern, all_text, re.DOTALL | re.MULTILINE):re.findall(...):查找文本中所有符合正则模式的内容,返回匹配结果列表。re.DOTALL:让.匹配包括换行符在内的所有字符(默认.不匹配换行符,保证案例中的多行内容能被完整匹配)。re.MULTILINE:让^和$匹配每一行的开头和结尾(默认只匹配整个文本的开头 / 结尾,保证先行断言中的^能匹配每个案例的行开头)。- 返回值:
matches是所有符合格式的案例原始列表(未清理)。
- 案例清理循环:
for match in matches::遍历所有原始匹配结果。re.sub(r'\n\s*\n', '\n\n', match.strip()):清理文本格式:match.strip():去除案例前后的首尾空白字符(空格、换行符等)。re.sub(r'\n\s*\n', '\n\n', ...):将「换行 + 任意空白字符 + 换行」替换为「双换行」,去除多余的空白行,让案例排版更规整。cleaned_match:清理后的案例文本。
if cleaned_match::过滤空内容(避免无效数据加入列表)。cases.append(cleaned_match):将清理后的有效案例加入cases列表。
return cases:返回最终的案例列表,每个元素是一个格式规整的法律案例文本。
七、案例保存为 TXT 函数(第 82-110 行)
python
运行
def save_cases_to_txt(cases, pdf_filename, relative_path, output_dir):
"""
将提取的案例保存为txt文件,保持原有的文件夹结构
"""
# 创建输出目录(如果不存在),保持原有的子文件夹结构
output_subdir = os.path.join(output_dir, relative_path)
os.makedirs(output_subdir, exist_ok=True)
# 获取基础文件名(不含扩展名)
base_name = os.path.splitext(os.path.basename(pdf_filename))[0]
for i, case in enumerate(cases, start=1):
# 从案例内容中提取案名作为文件名的一部分
lines = case.split('\n')
case_title = "未知案例"
# 寻找破折号行来确定案名
for line in lines:
if line.startswith('------'):
case_title = line[1:].strip() # 去掉破折号
break
# 清理文件名中的非法字符
safe_case_title = re.sub(r'[<>:"/\\|?*]', '_', case_title)
# 构建输出文件名
output_filename = f"{base_name}_案例{i}_{safe_case_title}.txt"
output_path = os.path.join(output_subdir, output_filename)
# 写入txt文件
with open(output_path, 'w', encoding='utf-8') as txt_file:
txt_file.write(case)
print(f"已保存: {output_path}")
def save_cases_to_txt(cases, pdf_filename, relative_path, output_dir)::定义案例保存函数,入参说明:cases:待保存的案例列表;pdf_filename:原始 PDF 文件名(用于构建 TXT 文件名);relative_path:原始 PDF 的相对路径(用于保持目录结构);output_dir:输出根目录。
- 文档字符串:说明函数功能 ------ 将案例保存为 TXT,保持原有文件夹结构。
output_subdir = os.path.join(output_dir, relative_path):os.path.join(...):拼接输出子目录路径(输出根目录 + 原始 PDF 的相对路径),实现「保持原有文件夹结构」的核心。- 例如:原始 PDF 在
pdfs/subdir/下,输出目录为cases_output,则输出子目录为cases_output/subdir/。
os.makedirs(output_subdir, exist_ok=True):- 创建拼接后的输出子目录,
exist_ok=True表示「若目录已存在则不报错」,避免重复创建目录的异常。
- 创建拼接后的输出子目录,
base_name = os.path.splitext(os.path.basename(pdf_filename))[0]:os.path.basename(pdf_filename):提取 PDF 文件名(包含扩展名,如案例集.pdf)。os.path.splitext(...):将文件名分割为「文件名主体」和「扩展名」,返回元组(如('案例集', '.pdf'))。[0]:取元组的第一个元素,即不含扩展名的 PDF 文件名主体,用于构建 TXT 文件名。
- 案例循环保存(
for i, case in enumerate(cases, start=1)):enumerate(cases, start=1):遍历案例列表,同时获取案例的索引(从 1 开始,用于区分同一个 PDF 中的多个案例)。lines = case.split('\n'):将单个案例文本按换行符分割为行列表,用于提取案名。case_title = "未知案例":初始化案名默认值,避免提取失败时文件名缺失。- 提取案名循环:遍历案例的每一行,找到以「------」开头的行,去掉破折号后作为案名,找到后立即
break终止循环。 safe_case_title = re.sub(r'[<>:"/\\|?*]', '_', case_title):- 正则表达式匹配 Windows 系统中文件名非法字符 (
<>:"/\|?*)。 - 用下划线
_替换非法字符,避免创建文件时报错。
- 正则表达式匹配 Windows 系统中文件名非法字符 (
- 构建输出文件路径:
output_filename:拼接 TXT 文件名(PDF 主体名 + 案例序号 + 安全案名 +.txt),可读性强,便于区分。output_path:拼接完整的 TXT 文件路径(输出子目录 + 文件名)。
- 写入 TXT 文件:
with open(output_path, 'w', encoding='utf-8') as txt_file::以「写入模式(w)」打开文件,指定编码为utf-8(避免中文乱码),自动管理文件句柄。txt_file.write(case):将清理后的案例文本写入 TXT 文件。
print(f"已保存: {output_path}"):打印保存成功的提示,方便用户查看进度。
八、递归查找所有 PDF 文件(第 112-128 行)
python
运行
def find_all_pdfs(root_folder):
"""
递归查找所有PDF文件及其相对路径
返回 (pdf_path, relative_path) 的列表
"""
pdf_files = []
for root, dirs, files in os.walk(root_folder):
for file in files:
if file.lower().endswith('.pdf'):
pdf_path = os.path.join(root, file)
# 计算相对于root_folder的路径
relative_path = os.path.relpath(root, root_folder)
if relative_path == '.':
relative_path = '' # 根目录的情况
pdf_files.append((pdf_path, relative_path))
return pdf_files
def find_all_pdfs(root_folder)::定义递归查找 PDF 函数,入参root_folder是查找的根目录,核心是遍历所有子目录,找到所有 PDF 文件。- 文档字符串:说明函数功能 ------ 递归查找所有 PDF,返回「PDF 完整路径 + 相对路径」的元组列表。
pdf_files = []:初始化空列表,用于存储找到的 PDF 文件信息(元组形式)。for root, dirs, files in os.walk(root_folder)::os.walk(root_folder):递归遍历root_folder及其所有子目录,返回一个生成器,每次迭代返回三个值:root:当前正在遍历的目录路径;dirs:当前目录下的子目录列表;files:当前目录下的文件列表。
- 该方法是实现「多层级目录遍历」的核心,无需手动递归,简化代码。
for file in files::遍历当前目录下的所有文件。if file.lower().endswith('.pdf')::判断文件是否为 PDF:file.lower():将文件名转为小写(避免大小写问题,如.PDF、.PdF都能被识别)。endswith('.pdf'):判断文件扩展名是否为.pdf。
- 构建 PDF 文件信息:
pdf_path = os.path.join(root, file):拼接当前 PDF 文件的完整绝对路径,用于后续打开处理。relative_path = os.path.relpath(root, root_folder):计算当前目录root相对于查找根目录root_folder的相对路径,用于后续保持输出目录结构。if relative_path == '.' : relative_path = '':若当前目录是根目录,os.path.relpath返回.,将其改为空字符串,避免输出目录出现多余的.目录。pdf_files.append((pdf_path, relative_path)):将「完整路径 + 相对路径」以元组形式加入列表。
return pdf_files:返回所有找到的 PDF 文件信息列表,供后续批量处理。
九、批量处理所有 PDF 函数(第 130-166 行)
python
运行
def process_all_pdfs(input_folder, output_folder):
"""
处理文件夹及子文件夹下的所有PDF文件
"""
# 获取脚本所在目录
script_dir = get_script_directory()
# 设置默认路径
if not input_folder:
input_folder = os.path.join(script_dir, "pdfs")
if not output_folder:
output_folder = os.path.join(script_dir, "cases_output")
# 确保目录存在
os.makedirs(input_folder, exist_ok=True)
os.makedirs(output_folder, exist_ok=True)
print(f"PDF输入目录: {input_folder}")
print(f"TXT输出目录: {output_folder}")
# 查找所有PDF文件(包括子文件夹)
pdf_files_info = find_all_pdfs(input_folder)
if not pdf_files_info:
print(f"在 {input_folder} 及其子文件夹中未找到PDF文件")
print("请将PDF文件放入pdfs文件夹或其子文件夹中")
return
print(f"找到 {len(pdf_files_info)} 个PDF文件")
for pdf_path, relative_path in pdf_files_info:
pdf_filename = os.path.basename(pdf_path)
print(f"\n正在处理: {relative_path + '/' if relative_path else ''}{pdf_filename}")
try:
# 提取案例
cases = extract_cases_from_pdf(pdf_path)
if cases:
print(f"提取到 {len(cases)} 个案例")
# 保存案例,保持原有的文件夹结构
save_cases_to_txt(cases, pdf_filename, relative_path, output_folder)
else:
print("未提取到案例")
except Exception as e:
print(f"处理 {relative_path + '/' if relative_path else ''}{pdf_filename} 时出错: {str(e)}")
def process_all_pdfs(input_folder, output_folder)::定义批量处理 PDF 的核心函数,入参是输入 / 输出目录,负责串联「查找→提取→保存」的完整流程。- 文档字符串:说明函数功能 ------ 处理根目录及子目录下的所有 PDF 文件。
script_dir = get_script_directory():调用之前的函数,获取脚本所在目录。- 默认路径设置:
if not input_folder::若输入目录为空字符串,设置默认输入目录为「脚本目录 /pdfs」。if not output_folder::若输出目录为空字符串,设置默认输出目录为「脚本目录 /cases_output」。- 优点:用户无需手动指定目录,降低使用门槛,默认目录结构清晰。
os.makedirs(..., exist_ok=True):创建输入 / 输出目录(若不存在),避免后续查找 / 保存时出现目录不存在的异常。- 打印输入 / 输出目录:方便用户确认当前的目录配置,提高透明度。
pdf_files_info = find_all_pdfs(input_folder):调用查找函数,获取所有 PDF 文件信息列表。- 无 PDF 文件判断:若列表为空,打印提示信息并返回,终止函数执行。
- 打印找到的 PDF 数量:让用户了解处理进度。
- 遍历处理每个 PDF:
for pdf_path, relative_path in pdf_files_info::遍历每个 PDF 的完整路径和相对路径。pdf_filename = os.path.basename(pdf_path):提取 PDF 文件名(用于打印和保存 TXT)。- 打印当前处理的文件名:方便用户查看处理进度。
try...except块:捕获单个 PDF 处理过程中的异常,避免一个 PDF 处理失败导致整个批量任务终止。cases = extract_cases_from_pdf(pdf_path):调用提取函数,获取当前 PDF 的案例列表。- 案例保存判断:若提取到案例,打印案例数量并调用保存函数;否则打印未提取到案例的提示。
except Exception as e:捕获所有异常,打印错误信息,继续处理下一个 PDF。
十、主函数与程序入口(第 168-189 行)
python
运行
def main():
print("多层级法律案例PDF转TXT批量处理工具")
print("=" * 50)
print("注意:需要安装以下任一PDF处理库之一:")
print("1. PyMuPDF: pip install PyMuPDF")
print("2. PyPDF2: pip install PyPDF2")
print()
# 软件启动时显示当前工作目录
current_dir = get_script_directory()
print(f"软件所在目录: {current_dir}")
# 自动设置默认路径
input_folder = "" # 空字符串表示使用默认路径
output_folder = "" # 空字符串表示使用默认路径
# 开始处理
process_all_pdfs(input_folder, output_folder)
print("\n处理完成!")
if __name__ == "__main__":
main()
def main()::定义程序主函数,负责初始化程序、打印提示信息、调用批量处理函数。- 打印工具标题和注意事项:
- 打印工具名称和分隔线,提高可读性。
- 打印所需安装的 PDF 库及对应命令,方便用户准备环境。
current_dir = get_script_directory():获取脚本所在目录并打印,让用户了解软件的安装 / 运行目录。- 初始化输入 / 输出目录为空字符串:表示使用默认路径(后续在
process_all_pdfs中会自动设置)。 process_all_pdfs(input_folder, output_folder):调用批量处理函数,启动完整的 PDF 处理流程。- 打印处理完成提示:让用户知晓任务已结束。
if __name__ == "__main__"::Python 程序入口判断:- 当该脚本被直接运行 时,
__name__的值为__main__,会执行main()函数,启动程序。 - 当该脚本被作为模块导入 时,
__name__的值为脚本名,不会执行main()函数,方便后续复用其中的函数。
- 当该脚本被直接运行 时,
总结
- 这份代码的核心流程 :
查找所有PDF(递归)→ 提取PDF文本(两种库兼容)→ 正则解析案例 → 保持目录结构保存TXT,逻辑闭环,实用性强。 - 关键亮点:兼容两种 PDF 库 、保持目录结构 、中文防乱码 、异常容错,适合批量处理法律案例 PDF。
- 核心依赖:正则表达式(案例提取)、
os模块(文件 / 目录操作)、PyMuPDF/PyPDF2(PDF 文本提取)。
完整代码
python
import os
import re
def get_script_directory():
"""获取脚本所在目录"""
return os.path.dirname(os.path.abspath(__file__))
def extract_cases_from_pdf(pdf_path):
"""
从PDF文件中提取案例,按照法律案例的标准格式进行识别
尝试使用不同的PDF库
"""
# 尝试使用 PyMuPDF (fitz)
try:
import fitz
return extract_with_pymupdf(pdf_path)
except ImportError:
pass
# 尝试使用 PyPDF2
try:
import PyPDF2
return extract_with_pypdf2(pdf_path)
except ImportError:
pass
# 如果都没有安装,抛出异常
raise ImportError("需要安装 PyMuPDF (pip install PyMuPDF) 或 PyPDF2 (pip install PyPDF2)")
def extract_with_pymupdf(pdf_path):
"""使用PyMuPDF提取文本"""
import fitz
# 打开PDF文档
doc = fitz.open(pdf_path)
# 提取所有文本
all_text = ""
for page in doc:
all_text += page.get_text() + "\n"
# 关闭文档
doc.close()
return extract_cases_from_text(all_text)
def extract_with_pypdf2(pdf_path):
"""使用PyPDF2提取文本"""
import PyPDF2
with open(pdf_path, 'rb') as file:
pdf_reader = PyPDF2.PdfFileReader(file)
# 提取所有文本
all_text = ""
for page_num in range(pdf_reader.numPages):
page = pdf_reader.getPage(page_num)
all_text += page.extractText() + "\n"
return extract_cases_from_text(all_text)
def extract_cases_from_text(all_text):
"""
从文本中提取案例,按照法律案例的标准格式进行识别
"""
cases = []
# 匹配案例格式:标题 + 案例标识 + 案件信息
# 匹配模式:任意字符直到换行 + 破折号 + 案名 + 【案件基本信息】 + 后续内容直到下一个案例开始
pattern = r'([^\n]*\n------[^\n]*\n【案件基本信息】.*?)(?=^[^\n]*\n------[^\n]*\n【案件基本信息】|\Z)'
matches = re.findall(pattern, all_text, re.DOTALL | re.MULTILINE)
# 清理匹配结果
for match in matches:
# 去除多余的空白行和空格
cleaned_match = re.sub(r'\n\s*\n', '\n\n', match.strip())
if cleaned_match:
cases.append(cleaned_match)
return cases
def save_cases_to_txt(cases, pdf_filename, relative_path, output_dir):
"""
将提取的案例保存为txt文件,保持原有的文件夹结构
"""
# 创建输出目录(如果不存在),保持原有的子文件夹结构
output_subdir = os.path.join(output_dir, relative_path)
os.makedirs(output_subdir, exist_ok=True)
# 获取基础文件名(不含扩展名)
base_name = os.path.splitext(os.path.basename(pdf_filename))[0]
for i, case in enumerate(cases, start=1):
# 从案例内容中提取案名作为文件名的一部分
lines = case.split('\n')
case_title = "未知案例"
# 寻找破折号行来确定案名
for line in lines:
if line.startswith('------'):
case_title = line[1:].strip() # 去掉破折号
break
# 清理文件名中的非法字符
safe_case_title = re.sub(r'[<>:"/\\|?*]', '_', case_title)
# 构建输出文件名
output_filename = f"{base_name}_案例{i}_{safe_case_title}.txt"
output_path = os.path.join(output_subdir, output_filename)
# 写入txt文件
with open(output_path, 'w', encoding='utf-8') as txt_file:
txt_file.write(case)
print(f"已保存: {output_path}")
def find_all_pdfs(root_folder):
"""
递归查找所有PDF文件及其相对路径
返回 (pdf_path, relative_path) 的列表
"""
pdf_files = []
for root, dirs, files in os.walk(root_folder):
for file in files:
if file.lower().endswith('.pdf'):
pdf_path = os.path.join(root, file)
# 计算相对于root_folder的路径
relative_path = os.path.relpath(root, root_folder)
if relative_path == '.':
relative_path = '' # 根目录的情况
pdf_files.append((pdf_path, relative_path))
return pdf_files
def process_all_pdfs(input_folder, output_folder):
"""
处理文件夹及子文件夹下的所有PDF文件
"""
# 获取脚本所在目录
script_dir = get_script_directory()
# 设置默认路径
if not input_folder:
input_folder = os.path.join(script_dir, "pdfs")
if not output_folder:
output_folder = os.path.join(script_dir, "cases_output")
# 确保目录存在
os.makedirs(input_folder, exist_ok=True)
os.makedirs(output_folder, exist_ok=True)
print(f"PDF输入目录: {input_folder}")
print(f"TXT输出目录: {output_folder}")
# 查找所有PDF文件(包括子文件夹)
pdf_files_info = find_all_pdfs(input_folder)
if not pdf_files_info:
print(f"在 {input_folder} 及其子文件夹中未找到PDF文件")
print("请将PDF文件放入pdfs文件夹或其子文件夹中")
return
print(f"找到 {len(pdf_files_info)} 个PDF文件")
for pdf_path, relative_path in pdf_files_info:
pdf_filename = os.path.basename(pdf_path)
print(f"\n正在处理: {relative_path + '/' if relative_path else ''}{pdf_filename}")
try:
# 提取案例
cases = extract_cases_from_pdf(pdf_path)
if cases:
print(f"提取到 {len(cases)} 个案例")
# 保存案例,保持原有的文件夹结构
save_cases_to_txt(cases, pdf_filename, relative_path, output_folder)
else:
print("未提取到案例")
except Exception as e:
print(f"处理 {relative_path + '/' if relative_path else ''}{pdf_filename} 时出错: {str(e)}")
def main():
print("多层级法律案例PDF转TXT批量处理工具")
print("=" * 50)
print("注意:需要安装以下任一PDF处理库之一:")
print("1. PyMuPDF: pip install PyMuPDF")
print("2. PyPDF2: pip install PyPDF2")
print()
# 软件启动时显示当前工作目录
current_dir = get_script_directory()
print(f"软件所在目录: {current_dir}")
# 自动设置默认路径
input_folder = "" # 空字符串表示使用默认路径
output_folder = "" # 空字符串表示使用默认路径
# 开始处理
process_all_pdfs(input_folder, output_folder)
print("\n处理完成!")
if __name__ == "__main__":
main()