咨询:基于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文件。
批量处理最佳实践
- 提炼通用规则:先分析所有文档的共性,比如一级标题字体最大(如>16号)、二级标题次之(14-16号),编号规则统一为
数字.数字....格式,写通用的正则匹配和字体大小判断逻辑,避免硬编码。 - 批量遍历处理:用
glob或os模块遍历目标文件夹下的所有PDF,循环调用转换函数,将生成的Markdown文件保存到指定目录。 - 样本验证调优:批量处理前先拿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
相关产品推荐
相关产品推荐

