法律案例 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()
相关推荐
一个小废渣2 小时前
Flutter Web端网络请求跨域错误解决方法
前端·flutter
季布,2 小时前
本地Windows测试:钉钉群消息/文件传输到Python服务(完整教程)
windows·python·钉钉
zm-v-159304339862 小时前
最新AI-Python自然科学领域机器学习与深度学习技术
人工智能·python·机器学习
qwerasda1238522 小时前
Mask-RCNN右转交通标志识别训练与优化
python
郝学胜-神的一滴2 小时前
何友院士《人工智能发展前沿》全景解读:从理论基石到产业变革
人工智能·python·深度学习·算法·机器学习
阮松云2 小时前
a start job is running for Builds and install new kernel modules through DKMS
linux·centos
符文师2 小时前
css3 新特性
前端·css3
Maggie_ssss_supp3 小时前
Linux-MySQL权限管理
linux·运维·mysql
石像鬼₧魂石3 小时前
Kali Linux 内网渗透:深度工程实施手册
linux·运维·服务器