如何使用Python代码将复杂PDF转换为XML/JSON格式
复杂内容PDF转XML/JSON Python实现方案
你之前测试的所有库都属于单功能解析组件,本身就没有覆盖全类型PDF元素的设计,不要尝试用单库解决所有需求,按模块拆分处理是目前唯一可行的本地落地方案:
分模块工具选型
- 基础文本、内嵌图片、Logo/矢量标签提取:用
PyMuPDF(fitz),你之前大概率只用到了它的纯文本导出接口,它原生支持提取带坐标的文本块、内嵌图片、矢量图形边界框,能先搭好整个结构化输出的骨架。 - 表格数据提取:放弃Tabula、Camelot这类传统表格提取库,换
PP-StructureV2,不管是有线表、无线表、跨页混排表,都能输出带合并单元格信息、单元格坐标的结构化结果,对非标准格式表格的识别准确率远高于传统库。 - 数学公式、化学方程式提取:用
Pix2Text,本地部署即可识别印刷/手写公式、化学结构式/方程式,直接输出带坐标的LaTeX格式结果,不需要调用付费接口。 - 非结构化零散内容补全:如果是扫描件无原生文本层,搭配
PaddleOCR做通用文本识别即可。
落地处理流程
- 用PyMuPDF逐页加载PDF,优先提取原生可解析内容:
- 文本块记录坐标、字体、字号、内容
- 内嵌图片记录坐标、二进制内容,可转base64存入JSON/XML,也可存本地后写入引用路径
- 矢量Logo、标签元素记录坐标、形状、颜色属性
- 将单页PDF导出为300DPI高清图片,送入PP-StructureV2识别表格,将识别到的表格区域和第一步提取的原生内容做坐标匹配,删除落在表格区域内的零散文本块,替换为结构化表格数据,避免内容重复。
- 同一张页图送入Pix2Text检测公式、化学方程式,将识别到的LaTeX结果、坐标存入对应节点,同样做坐标去重,删除公式区域内的零散误识别文本。
- 所有元素按从上到下、从左到右的阅读顺序排序,按照自定义Schema导出为XML或JSON即可。
关键避坑点
- 可编辑PDF不要上来就全页OCR,原生内容层的准确率远高于OCR结果,OCR仅用来补原生层无法解析的表格、公式、扫描内容
- 注意坐标体系转换:PyMuPDF默认坐标原点在页面左下角,OCR类模型输出坐标原点在页面左上角,不做转换会出现元素位置错位
- 导出时务必保留每个元素的bbox坐标信息,后续做内容校验、格式修正时可以直接对应到PDF原位置
- 图片提取时注意过滤页面背景、装饰性小图标,避免把无意义的碎图存入结果
基础代码参考(PyMuPDF初始骨架提取)
import fitz import json import base64 doc = fitz.open("target.pdf") result = {"pages": []} for page_idx, page in enumerate(doc): page_info = { "page_no": page_idx + 1, "page_width": page.rect.width, "page_height": page.rect.height, "texts": [], "images": [], "vector_shapes": [] } # 提取文本块 text_data = page.get_text("dict", flags=fitz.TEXT_PRESERVE_WHITESPACE) for block in text_data["blocks"]: if block["type"] == 0: for line in block["lines"]: for span in line["spans"]: if span["text"].strip(): page_info["texts"].append({ "bbox": list(span["bbox"]), "content": span["text"], "font": span["font"], "font_size": round(span["size"], 2) }) elif block["type"] == 1: # 提取图片转base64 img_b64 = base64.b64encode(block["image"]).decode("utf-8") page_info["images"].append({ "bbox": list(block["bbox"]), "content": img_b64, "ext": block["ext"] }) result["pages"].append(page_info) # 此处追加表格、公式识别结果的坐标去重、合并逻辑 with open("pdf_structured.json", "w", encoding="utf-8") as f: json.dump(result, f, ensure_ascii=False, indent=2)
内容的提问来源于stack exchange,提问作者Raj P
相关产品推荐
相关产品推荐

