You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询:基于Python实现带结构保留的PDF转Markdown方案

PDF转Markdown并保留层级结构的Python方案

关于PDFMinerSix的说明

PDFMinerSix本身仅专注于文本提取与布局信息解析,确实没有内置将文档结构映射为Markdown层级的功能。要实现结构保留,你需要基于它输出的布局数据(字体大小、文本位置、编号规则)自行判断标题层级,但有更高效的现成方案可选。

经过验证的Python库及用法

1. PyMuPDF(fitz)

这是处理PDF最常用的轻量库之一,能高效提取文本块的字体大小、位置等布局信息,结合文档的编号规则(如1./1.1/2.2.1)可精准识别标题层级,非常适合批量处理。
核心思路:

  • 提取每个文本块的字体大小,结合编号的点数量判断层级(比如1.对应1级标题,1.1对应2级)
  • 用正则匹配标题编号,双重验证确保层级准确

2. pdfplumber

API比PDFMinerSix更友好,同样能获取文本的字体属性、行位置等数据。适合对布局细节要求高的场景,可通过正则匹配标题编号+字体大小阈值来划分层级。

3. 先转HTML再转Markdown(pdf2htmlEX + markdownify)

如果文档结构复杂,可先用pdf2htmlEX工具将PDF转成带层级的HTML(该工具能保留标题、段落的原生结构),再用Python的markdownify库将HTML转为Markdown。批量处理时可通过subprocess调用pdf2htmlEX批量转换,再统一处理HTML文件。

批量处理最佳实践

  1. 提炼通用规则:先分析所有文档的共性,比如一级标题字体最大(如>16号)、二级标题次之(14-16号),编号规则统一为数字.数字....格式,写通用的正则匹配和字体大小判断逻辑,避免硬编码。
  2. 批量遍历处理:用glob或os模块遍历目标文件夹下的所有PDF,循环调用转换函数,将生成的Markdown文件保存到指定目录。
  3. 样本验证调优:批量处理前先拿3-5份样本测试,调整正则规则和字体大小阈值,确保结构准确率。

示例代码(PyMuPDF批量转换)

import fitz
import re
import glob
import os

def pdf_to_markdown(pdf_path, md_path):
    doc = fitz.open(pdf_path)
    md_content = ""
    # 从样本文档统计的字体大小阈值,可根据实际调整
    font_size_levels = {1: 16, 2: 14, 3: 12}

    for page in doc:
        blocks = page.get_text("dict")["blocks"]
        for block in blocks:
            if block["type"] != 0:
                continue
            lines = block["lines"]
            for line in lines:
                spans = line["spans"]
                text = "".join([s["text"] for s in spans]).strip()
                if not text:
                    continue
                
                # 匹配标题编号(如1.、1.1、2.3.1)
                num_match = re.match(r"^(\d+\.)+", text)
                if num_match:
                    # 优先通过编号点数量判断层级,再用字体大小验证
                    level_by_num = num_match.group().count(".")
                    font_size = spans[0]["size"]
                    
                    if font_size > font_size_levels[1]:
                        final_level = 1
                    elif font_size > font_size_levels[2]:
                        final_level = 2
                    elif font_size > font_size_levels[3]:
                        final_level = 3
                    else:
                        final_level = level_by_num
                    
                    md_content += f"{'#' * final_level} {text}\n\n"
                else:
                    # 普通段落直接追加
                    md_content += f"{text}\n\n"
    
    with open(md_path, "w", encoding="utf-8") as f:
        f.write(md_content)

# 批量处理逻辑
pdf_dir = "./source_pdfs"
md_dir = "./output_markdowns"
os.makedirs(md_dir, exist_ok=True)

for pdf_file in glob.glob(os.path.join(pdf_dir, "*.pdf")):
    base_name = os.path.splitext(os.path.basename(pdf_file))[0]
    md_file = os.path.join(md_dir, f"{base_name}.md")
    pdf_to_markdown(pdf_file, md_file)

内容的提问来源于stack exchange,提问作者Guido

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:42:54