拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MarkItDown 复杂表单式 PDF 转 Markdown 实战解析:以影院订场订单(movie-theater-booking-2024)为例

MarkItDown 复杂表单式 PDF 转 Markdown 实战解析:以影院订场订单(movie-theater-booking-2024)为例
  • 人工智能
  • AI 应用
  • MCP 服务

【免费下载链接】markitdown

Python tool for converting files and office documents to Markdown.

项目地址:https://gitcode.com/GitHub_Trending/ma/markitdown
点击查看免费下载

MarkItDown 是当前仓库提供的一个 Python 包与命令行工具,用于把各种文件与 Office 文档转换为 Markdown,供 LLM 与文本分析管线使用。本文以测试样例movie-theater-booking-2024.pdf(影院订场订单)及其期望输出 movie-theater-booking-2024.md 为主体,完整拆解这类"无边框表单 + 多区域表格"的 PDF 是如何被还原成带管道的结构化 Markdown 的。读完本文,你将掌握 MarkItDown 表单式 PDF 的转换结果形态、底层列检测算法原理,以及如何在本地复现并验证这类转换。

为什么"表单式 PDF"是表格提取的硬骨头

PDF 中的表格分为两类:一类是绘制了可见框线(单元格边框)的规则表格;另一类是只靠文字对齐、没有任何边框的"表单式"(form-style)布局——例如发票、订场单、库存盘点表、体检报告。后者的行列关系完全隐含在文字的 X/Y 坐标里,普通按文本流抽取的方式要么把单元格内容粘连成一行,要么丢失列结构。

movie-theater-booking-2024.pdf正是这类文档的代表:一份 STARLIGHT CINEMAS 的影院订场订单,包含订单头部、预订机构、客户、预订汇总、总计、账户代表、放映计划等多个区域,其中既有键值对式的表单,又有多列表格,还有行内注释文本。MarkItDown 的 PDF 转换器对它输出的期望结果被固化为测试基准,存入 expected_outputs/movie-theater-booking-2024.md。

完整转换输出逐段解析

下面这段 Markdown 就是 MarkItDown 对movie-theater-booking-2024.pdf的完整转换结果(与期望输出文件逐行一致),它揭示了表单式 PDF 的还原策略:表格区域用|分隔的 Markdown 表呈现,非表格内容保留为普通文本行。

订单头部与订单信息

BOOKING ORDER、Print Date、Page 1 of 1、影院品牌等头部文本按普通文本输出;紧随其后的Orders区域是一个键值对表单,被识别为一张表:

BOOKING ORDER Print Date 12/15/2024 14:30:22 Page 1 of 1 STARLIGHT CINEMAS Orders | Order / Rev: | 2024-12-5678 | | | Cinema: | | Downtown Multiplex | | ------------ | -------------- | --- | --- | ---------------- | --- | ------------------ | | Alt Order #: | SC-WINTER-2024 | | | Primary Contact: | | Sarah Johnson | Product Desc: Holiday Movie Marathon Package Location: NYC-01 | Estimate: | EST-456 | | | Region: | | NORTHEAST | | -------------------- | ----------------------- | --- | --- | ------- | --- | --------- | | Booking Dates: | 12/20/2024 - 12/31/2024 | | | | | | | Original Date / Rev: | 12/01/24 / 12/10/24 | | | | | | | Order Type: | Premium Package | | | | | |

注意几个关键细节:

  • 键(Order / Rev:)与值(2024-12-5678)被分配到不同单元格,第二列起还预留了空单元格用于对齐右侧的Cinema:/Downtown Multiplex;
  • Product Desc: Holiday Movie Marathon Package Location: NYC-01这一行没有进入表格,而是保持为普通文本——说明算法基于列对齐情况对每一行做了"表格行 / 非表格行"分类;
  • 每个表块都带有标准的---分隔行,这是 Markdown 表头语法的一部分。

预订机构(Booking Agency)与地址文本

Booking Agency | Name: | Premier Entertainment Group | | | | | | | ---------------- | --------------------------- | --- | --- | -------------- | --- | --------- | | | | | | Billing Type: | | Net 30 | | Contact: | Michael Chen | | | | | | | | | | | Payment Terms: | | Corporate | | Billing Contact: | accounting@premierent.com | | | | | | | | | | | Commission: | | 10% | 555 Broadway Suite 1200 New York, NY 10012

该区域展示了一个双栏表单:左栏是机构名称/联系人/开票邮箱,右栏是Billing Type、Payment Terms、Commission。两栏共用一张表、通过空单元格占位对齐;而机构地址555 Broadway Suite 1200与New York, NY 10012属于整页宽度的段落文本,被排除在表格之外。

客户(Customer)信息

Customer | Name: | Universal Studios Distribution | | | | | | | -------------- | ------------------------------ | --- | --- | --- | --- | --- | | Category: | Film Distributor | | | | | | | Contact Email: | bookings@universalstudios.com | | | | | | | Customer ID: | CUST-98765 | | | | | | | Revenue Code: | FILM-PREMIUM | | | | | |

预订汇总(Booking Summary)与总计(Totals)

Booking Summary | Start Date | End Date | # Shows | Gross Amount | Net Amount | | | | ---------- | -------- | ------- | ------------ | ---------- | --- | --- | | 12/20/24 | 12/31/24 | 48 | $12,500.00 | $11,250.00 | | | Totals | Month | # Shows | Gross Amount | | Net Amount | | Occupancy | | ------------- | ------- | ------------ | --- | ---------- | --- | --------- | | December 2024 | 48 | $12,500.00 | | $11,250.00 | | 85% | | Totals | 48 | $12,500.00 | | $11,250.00 | | 85% |

货币金额、占比、场次数字都被原样保留(如$12,500.00、85%、48),说明提取过程中单元格内容不做数值解析,仅做文本切分与对齐。

账户代表(Account Representatives)与放映计划(Show Schedule Details)

Account Representatives Representative Territory Region Start Date / End Date Commission % | Sarah Johnson | NYC Metro | NORTHEAST | 12/20/24 - 12/31/24 | | 100% | | | ------------- | --------- | --------- | ------------------- | --- | ---- | --- | Show Schedule Details Ln Screen Start End Movie Title Format Showtime Days Shows Rate Type Total 1 SCR-1 12/20/24 12/25/24 Holiday Spectacular IMAX 3D 7:00 PM Daily 12 $250 PM $3,000 (Runtime: 142 min); Holiday Season Premium 2 SCR-2 12/20/24 12/31/24 Winter Wonderland Standard 4:30 PM Daily 24 $150 MT $3,600 (Runtime: 98 min); Matinee Special 3 SCR-1 12/26/24 12/31/24 New Year Mystery 4DX 9:30 PM Daily 12 $300 PM $3,600 (Runtime: 116 min); Premium Experience

Account Representatives的表头行Representative Territory Region Start Date / End Date Commission %以普通文本出现(说明该行未被判定为表格行),数据行则进入表格。而Show Schedule Details区域比较特殊:表头与三行放映数据以空格分隔的纯文本形式保留(每行字段数量多且间距并不完全对齐),每部影片的补充说明如(Runtime: 142 min); Holiday Season Premium紧随其后独立成行——这正是"表格行 / 段落行"混合布局的典型结果。

Show Details 与总计行

Show Details | Show Screen | Date Range | Title | Showtime | Days Type | Rate | Revenue | | ----------- | ---------- | ----- | -------- | --------- | ---- | ------- | 1 SCR-1 12/20-12/25 Holiday Spectacular 7:00 PM Daily PM $250 $3,000 This booking order is subject to cinema availability and standard terms. 2 SCR-2 12/20-12/31 Winter Wonderland 4:30 PM Daily MT $150 $3,600 All showtimes are approximate and subject to change. 3 SCR-1 12/26-12/31 New Year Mystery 9:30 PM Daily PM $300 $3,600 | Total Revenue: | | | | | | $12,500.00 | | -------------- | --- | --- | --- | --- | --- | ---------- |

这里可以看到 MarkItDown 对"列结构存在但行内容宽窄不一"的页面采取的策略:表头被格式化为带---的 Markdown 表头,但具体数据行因跨列文本过长被当作普通文本行;免责声明This booking order is subject to cinema availability and standard terms.与All showtimes are approximate and subject to change.则作为段落文本穿插其间,最后的总计行Total Revenue: ... $12,500.00又被还原成表格。

源码级原理:_pdf_converter.py的表单式表格提取算法

上述输出并非偶然,而是 _pdf_converter.py 中一套完整的"基于词坐标的列检测"流程的产物。核心入口是PdfConverter.convert(见_pdf_converter.py#L520-L589)与_extract_form_content_from_words(见_pdf_converter.py#L120-L395)。

逐页双路径:表单页走 pdfplumber,纯文本页走 pdfminer

PdfConverter.convert用pdfplumber.open打开文件后逐页调用_extract_form_content_from_words(page):

  • 若该页被判定为表单式布局,返回带|表格的 Markdown 片段;
  • 否则记录为普通页,用page.extract_text()提取纯文本;
  • 每页处理完毕立即page.close(),释放 pdfplumber 的缓存对象,使内存占用不随页数增长;
  • 若整份文档没有任何表单页,则回退到pdfminer.high_level.extract_text处理全文(因为它对散文类文本的间距还原更好);
  • 任何异常或空结果也会回退到 pdfminer,保证转换不中断。

最后还有一步后处理_merge_partial_numbering_lines,用于合并 MasterFormat 风格的.1、.2部分编号与后续文本行。

表单检测的五步流水线

_extract_form_content_from_words的核心逻辑可概括为:

  1. 取词与按行分组:page.extract_words(keep_blank_chars=True, x_tolerance=3, y_tolerance=3)拿到每个词的坐标,再按 Y 坐标(容差 5)聚成行,行内按x0排序。
  2. 行类型初判:行宽超过页面宽度 55% 且文本超过 60 字符判为段落行;首个词匹配^\.\d+$(MasterFormat 部分编号)判为列表项行;词起点按间距 50 聚成"列组",得到该行列数。
  3. 全局列结构学习:收集所有"列数 ≥ 3 且非段落"的行的 X 坐标,用自适应容差聚类出全局列边界:先统计相邻坐标间隙,取间隙的 70 百分位作为容差并夹紧到[25, 50](数据不足时回退 35)。
  4. 形式判定(防误判):平均列宽 < 30 像素、列密度 > 10 列/英寸、或列数超过max(15, 20 * page_width/612)时,直接判定为"不是表单",返回None交给 pdfminer——这正是学术论文多栏排版不会被误当成表格的原因。
  5. 行分类与表格区域还原:某行若与 2 个及以上全局列对齐则标记为表格行;连续表格行构成表区域;表格区域内逐行按列边界把词分配到单元格,输出时先格式化表头行与---分隔行,再输出数据行,最后用ljust对齐各列宽度;表区域外的行按普通文本输出。若整个页面表格行占比不足 20%,同样返回None。

另外,_pdf_converter.py 还提供了_extract_tables_from_words作为补充路径,其列聚类容差固定为 20、要求列数在 3–10 之间、行需横跨多列、且超过 30% 的单元格文本长于 30 字符就放弃——这些启发式共同保证了"只有真正的表才转成表"。

接受条件与依赖

PdfConverter.accepts接受.pdf扩展名以及application/pdf、application/x-pdf两种 MIME 前缀(见_pdf_converter.py#L70-L75)。它依赖pdfplumber与pdfminer.six,在 pyproject.toml 中对应[pdf]可选依赖组:pdfminer.six>=20251230、pdfplumber>=0.11.9;若未安装,会抛出MissingDependencyException。

测试如何锁死这份期望输出

movie-theater-booking-2024.md之所以能作为"期望输出"存在,是因为 test_pdf_tables.py 中有两组测试在守护它:

  • test_movie_theater_booking_pdf_extraction(test_pdf_tables.py#L654-L719):断言输出包含管道符|,并逐一校验订单号2024-12-5678、产品描述Holiday Movie Marathon Package、预订日期区间、备用订单号SC-WINTER-2024、影院品牌,以及机构名Premier Entertainment Group、联系人Michael Chen、客户名Universal Studios Distribution、CUST-98765、金额$12,500.00/$11,250.00、影片标题与费率等关键字段。
  • TestPdfFullOutputComparison.test_movie_theater_full_output(test_pdf_tables.py#L730-L777):直接把MarkItDown().convert()的实时输出与这份期望文件逐行对比——行数差不超过 2、管道符数量 > 80、---分隔行数量 > 8、表格行数量 > 15,并要求五个关键片段全部命中。这种"全量输出对比 + 关键字段校验"的双层设计,保证算法升级不会悄悄破坏既有输出结构。

同目录下的RECEIPT、SPARSE、REPAIR、MEDRPT等期望文件与测试还覆盖了无表小票、无边框表、多页发票、扫描件等场景,其中扫描件(无文本层)的期望输出为空字符串(test_pdf_tables.py#L951-L978),说明当前离线转换依赖 PDF 文本层,扫描件需借助 OCR 插件(仓库中另有markitdown-ocr包)处理。

本地复现与验证

安装

MarkItDown 要求 Python ≥ 3.10。安装全部可选依赖或仅安装 PDF 相关依赖均可:

pip install 'markitdown[all]' # 或只装 PDF 支持 pip install 'markitdown[pdf]'

也可从仓库源码安装(见 packages/markitdown/README.md):

git clone https://gitcode.com/GitHub_Trending/ma/markitdown cd markitdown pip install -e 'packages/markitdown[all]'

命令行转换

markitdown packages/markitdown/tests/test_files/movie-theater-booking-2024.pdf > booking.md markitdown packages/markitdown/tests/test_files/movie-theater-booking-2024.pdf -o booking.md cat packages/markitdown/tests/test_files/movie-theater-booking-2024.pdf | markitdown

从标准输入读取时可用-x/--extension、-m/--mime-type、-c/--charset提供格式提示(见 __main__.py)。将生成的booking.md与 expected_outputs/movie-theater-booking-2024.md 对比即可验证一致性。

Python API

from markitdown import MarkItDown md = MarkItDown() result = md.convert("packages/markitdown/tests/test_files/movie-theater-booking-2024.pdf") print(result.markdown) # DocumentConverterResult.markdown print(result.text_content) # 旧版别名,等价于 markdown

MarkItDown.convert会自动区分本地路径、URL 与二进制流:字符串路径按 scheme 分发到convert_uri或convert_local,requests.Response走convert_response,可读流走convert_stream(见 _markitdown.py)。结果对象DocumentConverterResult(见 _base_converter.py)除markdown外还支持title元数据与__str__。

运行测试

仓库测试目录位于packages/markitdown/tests,可直接运行 PDF 表格相关测试:

python -m pytest packages/markitdown/tests/test_pdf_tables.py -k "movie_theater"

适用边界与注意事项

  • 依赖与文本层前提:离线 PDF 转换依赖pdfplumber/pdfminer.six;对无文本层的扫描件,期望输出为空,需要 OCR(如markitdown-ocr插件)才能提取内容。
  • 输出定位:MarkItDown 面向 LLM 与文本分析,输出以可读、可检索为准,并非高保真排版还原;不同布局下表格行与文本行的判定(如Show Schedule Details区域)会因列对齐情况而不同。
  • 安全边界:MarkItDown 以当前进程权限执行 I/O,与open()、requests.get()类似;在不可信环境中应消毒输入,并尽量调用最窄的convert_*方法(如convert_stream、convert_local),详见 README.md 的安全说明。

借助movie-theater-booking-2024这份样例,你可以把"表单式 PDF → 结构化 Markdown"从黑盒变成白盒:既能看到算法输出的真实形态,也能在 _pdf_converter.py 中逐行追踪列检测、行分类与表格还原的实现细节,还能通过 test_pdf_tables.py 的断言理解质量保障手段。

  • 人工智能
  • AI 应用
  • MCP 服务

【免费下载链接】markitdown

Python tool for converting files and office documents to Markdown.

项目地址:https://gitcode.com/GitHub_Trending/ma/markitdown
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表