法律案例 PDF 批量转 TXT 工具代码

法律案例 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__))
  1. def get_script_directory()::定义函数get_script_directory,无入参,用于获取当前 Python 脚本文件所在的绝对目录。
  2. """获取脚本所在目录""":文档字符串(docstring),说明函数功能,方便后续查阅和自动生成文档。
  3. 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)")
  1. def extract_cases_from_pdf(pdf_path)::定义 PDF 案例提取入口函数,入参pdf_path是目标 PDF 文件的完整路径。
  2. 文档字符串:说明函数核心功能 ------ 从 PDF 提取法律案例,支持两种 PDF 处理库,提高兼容性。
  3. 第一个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 库)。
  4. 第二个try...except块(PyPDF2 尝试):
    • 逻辑与上一个块一致,尝试导入PyPDF2,导入成功则调用extract_with_pypdf2函数处理 PDF。
  5. raise ImportError(...):若两个库都导入失败(都未安装),主动抛出导入异常,提示用户安装任一库,终止函数执行。
  6. 核心设计:兼容两种 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)
  1. def extract_with_pymupdf(pdf_path)::定义 PyMuPDF 的 PDF 处理函数,入参pdf_path是目标 PDF 完整路径。
  2. 文档字符串:说明函数使用的库和功能。
  3. import fitz:再次导入fitz(虽然入口函数已导入,但此处单独导入,保证函数的独立性,即使单独调用该函数也能正常运行)。
  4. doc = fitz.open(pdf_path):调用fitz.open()打开 PDF 文件,返回 PDF 文档对象doc,后续用于遍历页面、提取文本。
  5. all_text = "":初始化空字符串,用于存储 PDF 中提取的所有文本内容。
  6. for page in doc::遍历 PDF 文档的每一个页面对象pagedoc是可迭代对象,直接遍历即可获取所有页面)。
  7. all_text += page.get_text() + "\n"
    • page.get_text():提取当前页面的所有文本内容(PyMuPDF 的文本提取效果优于 PyPDF2,支持更多 PDF 格式,提取的文本排版更规整)。
    • + "\n":每页文本后添加换行符,避免不同页面的文本粘连,方便后续解析。
    • 拼接至all_text,最终得到整个 PDF 的完整文本。
  8. doc.close():关闭 PDF 文档对象,释放系统资源(避免文件句柄泄露)。
  9. 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)
  1. def extract_with_pypdf2(pdf_path)::定义 PyPDF2 的 PDF 处理函数,入参pdf_path是目标 PDF 完整路径。
  2. 文档字符串:说明函数使用的库和功能。
  3. import PyPDF2:再次导入PyPDF2,保证函数独立性。
  4. with open(pdf_path, 'rb') as file:
    • 使用with语句打开 PDF 文件,自动管理文件句柄(执行完毕后自动关闭文件,无需手动close(),避免资源泄露)。
    • 打开模式'rb'二进制只读模式 (PDF 是二进制文件,必须用rb模式打开,否则会报错)。
    • file:打开后的文件对象,供后续PyPDF2读取。
  5. pdf_reader = PyPDF2.PdfFileReader(file):创建 PDF 读取器对象pdf_reader,用于读取 PDF 文件的内容和属性。
  6. all_text = "":初始化空字符串,存储完整 PDF 文本。
  7. for page_num in range(pdf_reader.numPages):
    • pdf_reader.numPages:获取 PDF 文档的总页数
    • 遍历页码(从 0 到总页数 - 1),用于逐个获取页面。
  8. page = pdf_reader.getPage(page_num):根据页码page_num获取对应的页面对象page
  9. all_text += page.extractText() + "\n"
    • page.extractText():提取当前页面的文本内容(PyPDF2 的提取效果稍差,对复杂排版 PDF 的支持不足,可能出现文本错乱)。
    • 拼接至all_text,添加换行符分隔页面。
  10. 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
  1. def extract_cases_from_text(all_text)::定义文本解析函数,入参all_text是从 PDF 中提取的完整文本内容,核心是按固定格式提取法律案例。
  2. 文档字符串:说明函数功能 ------ 按法律案例标准格式从文本中提取案例。
  3. cases = []:初始化空列表,用于存储最终提取并清理后的案例数据。
  4. 正则表达式模式pattern:这是案例提取的核心,用于匹配符合格式的法律案例:
    • 正则表达式:r'([^\n]*\n------[^\n]*\n【案件基本信息】.*?)(?=^[^\n]*\n------[^\n]*\n【案件基本信息】|\Z)'
    • 分段解析:
      • ([^\n]*\n------[^\n]*\n【案件基本信息】.*?):捕获组(最终要提取的内容),匹配「任意非换行字符 + 换行 + 破折号开头的行 + 换行 +【案件基本信息】+ 后续任意内容(非贪婪匹配)」。
      • (?=...):正向先行断言(不捕获内容,只用于界定匹配边界),用于确定案例的结束位置:要么是下一个案例的开始(与捕获组开头格式一致),要么是文本末尾(\Z)。
    • 匹配目标:精准匹配以「------」开头、包含「【案件基本信息】」的法律案例,且能自动分割多个连续案例。
  5. matches = re.findall(pattern, all_text, re.DOTALL | re.MULTILINE)
    • re.findall(...):查找文本中所有符合正则模式的内容,返回匹配结果列表。
    • re.DOTALL:让.匹配包括换行符在内的所有字符(默认.不匹配换行符,保证案例中的多行内容能被完整匹配)。
    • re.MULTILINE:让^$匹配每一行的开头和结尾(默认只匹配整个文本的开头 / 结尾,保证先行断言中的^能匹配每个案例的行开头)。
    • 返回值:matches是所有符合格式的案例原始列表(未清理)。
  6. 案例清理循环:
    • 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列表。
  7. 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}")
  1. def save_cases_to_txt(cases, pdf_filename, relative_path, output_dir)::定义案例保存函数,入参说明:
    • cases:待保存的案例列表;
    • pdf_filename:原始 PDF 文件名(用于构建 TXT 文件名);
    • relative_path:原始 PDF 的相对路径(用于保持目录结构);
    • output_dir:输出根目录。
  2. 文档字符串:说明函数功能 ------ 将案例保存为 TXT,保持原有文件夹结构。
  3. output_subdir = os.path.join(output_dir, relative_path)
    • os.path.join(...):拼接输出子目录路径(输出根目录 + 原始 PDF 的相对路径),实现「保持原有文件夹结构」的核心。
    • 例如:原始 PDF 在pdfs/subdir/下,输出目录为cases_output,则输出子目录为cases_output/subdir/
  4. os.makedirs(output_subdir, exist_ok=True)
    • 创建拼接后的输出子目录,exist_ok=True表示「若目录已存在则不报错」,避免重复创建目录的异常。
  5. 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 文件名。
  6. 案例循环保存(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 系统中文件名非法字符<>:"/\|?*)。
      • 用下划线_替换非法字符,避免创建文件时报错。
    • 构建输出文件路径:
      • 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
  1. def find_all_pdfs(root_folder)::定义递归查找 PDF 函数,入参root_folder是查找的根目录,核心是遍历所有子目录,找到所有 PDF 文件。
  2. 文档字符串:说明函数功能 ------ 递归查找所有 PDF,返回「PDF 完整路径 + 相对路径」的元组列表。
  3. pdf_files = []:初始化空列表,用于存储找到的 PDF 文件信息(元组形式)。
  4. for root, dirs, files in os.walk(root_folder):
    • os.walk(root_folder):递归遍历root_folder及其所有子目录,返回一个生成器,每次迭代返回三个值:
      • root:当前正在遍历的目录路径;
      • dirs:当前目录下的子目录列表;
      • files:当前目录下的文件列表。
    • 该方法是实现「多层级目录遍历」的核心,无需手动递归,简化代码。
  5. for file in files::遍历当前目录下的所有文件。
  6. if file.lower().endswith('.pdf')::判断文件是否为 PDF:
    • file.lower():将文件名转为小写(避免大小写问题,如.PDF.PdF都能被识别)。
    • endswith('.pdf'):判断文件扩展名是否为.pdf
  7. 构建 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)):将「完整路径 + 相对路径」以元组形式加入列表。
  8. 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)}")
  1. def process_all_pdfs(input_folder, output_folder)::定义批量处理 PDF 的核心函数,入参是输入 / 输出目录,负责串联「查找→提取→保存」的完整流程。
  2. 文档字符串:说明函数功能 ------ 处理根目录及子目录下的所有 PDF 文件。
  3. script_dir = get_script_directory():调用之前的函数,获取脚本所在目录。
  4. 默认路径设置:
    • if not input_folder::若输入目录为空字符串,设置默认输入目录为「脚本目录 /pdfs」。
    • if not output_folder::若输出目录为空字符串,设置默认输出目录为「脚本目录 /cases_output」。
    • 优点:用户无需手动指定目录,降低使用门槛,默认目录结构清晰。
  5. os.makedirs(..., exist_ok=True):创建输入 / 输出目录(若不存在),避免后续查找 / 保存时出现目录不存在的异常。
  6. 打印输入 / 输出目录:方便用户确认当前的目录配置,提高透明度。
  7. pdf_files_info = find_all_pdfs(input_folder):调用查找函数,获取所有 PDF 文件信息列表。
  8. 无 PDF 文件判断:若列表为空,打印提示信息并返回,终止函数执行。
  9. 打印找到的 PDF 数量:让用户了解处理进度。
  10. 遍历处理每个 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()
  1. def main()::定义程序主函数,负责初始化程序、打印提示信息、调用批量处理函数。
  2. 打印工具标题和注意事项:
    • 打印工具名称和分隔线,提高可读性。
    • 打印所需安装的 PDF 库及对应命令,方便用户准备环境。
  3. current_dir = get_script_directory():获取脚本所在目录并打印,让用户了解软件的安装 / 运行目录。
  4. 初始化输入 / 输出目录为空字符串:表示使用默认路径(后续在process_all_pdfs中会自动设置)。
  5. process_all_pdfs(input_folder, output_folder):调用批量处理函数,启动完整的 PDF 处理流程。
  6. 打印处理完成提示:让用户知晓任务已结束。
  7. if __name__ == "__main__"::Python 程序入口判断:
    • 当该脚本被直接运行 时,__name__的值为__main__,会执行main()函数,启动程序。
    • 当该脚本被作为模块导入 时,__name__的值为脚本名,不会执行main()函数,方便后续复用其中的函数。

总结

  1. 这份代码的核心流程查找所有PDF(递归)→ 提取PDF文本(两种库兼容)→ 正则解析案例 → 保持目录结构保存TXT,逻辑闭环,实用性强。
  2. 关键亮点:兼容两种 PDF 库保持目录结构中文防乱码异常容错,适合批量处理法律案例 PDF。
  3. 核心依赖:正则表达式(案例提取)、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()
相关推荐
寻星探路3 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
ValhallaCoder6 小时前
hot100-二叉树I
数据结构·python·算法·二叉树
wdfk_prog6 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
盟接之桥7 小时前
盟接之桥说制造:引流品 × 利润品,全球电商平台高效产品组合策略(供讨论)
大数据·linux·服务器·网络·人工智能·制造
猫头虎7 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven