- 人工智能
- AI 应用
- MCP 服务
【免费下载链接】markitdown
Python tool for converting files and office documents to Markdown.
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 ExperienceAccount 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的核心逻辑可概括为:
- 取词与按行分组:
page.extract_words(keep_blank_chars=True, x_tolerance=3, y_tolerance=3)拿到每个词的坐标,再按 Y 坐标(容差 5)聚成行,行内按x0排序。 - 行类型初判:行宽超过页面宽度 55% 且文本超过 60 字符判为段落行;首个词匹配
^\.\d+$(MasterFormat 部分编号)判为列表项行;词起点按间距 50 聚成"列组",得到该行列数。 - 全局列结构学习:收集所有"列数 ≥ 3 且非段落"的行的 X 坐标,用自适应容差聚类出全局列边界:先统计相邻坐标间隙,取间隙的 70 百分位作为容差并夹紧到
[25, 50](数据不足时回退 35)。 - 形式判定(防误判):平均列宽 < 30 像素、列密度 > 10 列/英寸、或列数超过
max(15, 20 * page_width/612)时,直接判定为"不是表单",返回None交给 pdfminer——这正是学术论文多栏排版不会被误当成表格的原因。 - 行分类与表格区域还原:某行若与 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) # 旧版别名,等价于 markdownMarkItDown.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.
相关推荐
如何在CMake项目中集成reflect-cpp?完整配置与依赖管理教程
如何在CMake项目中集成reflect cpp?完整配置与依赖管理教程 reflect cpp是一个强大的C++反射库,支持JSON、XML、YAML等多种数
Windows防撤回神器RevokeMsgPatcher:让重要消息不再错过
Windows防撤回神器RevokeMsgPatcher:让重要消息不再错过 你是否曾经遇到过这样的场景?工作群里领导发的重要通知,你刚要点开查看,却看到"对方
桌面应用即时通讯knowledge-catalog OKF 订单表概念文档解析:以 acme_retail 的 Customer Orders 表为例理解 BigQuery 表语义治理
knowledge catalog OKF 订单表概念文档解析:以 acme_retail 的 Customer Orders 表为例理解 BigQuery 表
数据目录AI Agent人工智能知识管理示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考