Docling 将 XLSM 宏工作簿解析为结构化 Markdown 表格:xlsx_02_sample_sales_data groundtruth 深度解析
Docling 将 XLSM 宏工作簿解析为结构化 Markdown 表格xlsx_02_sample_sales_data groundtruth 深度解析【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling导读本文以仓库测试数据中的黄金基准文件tests/data/xlsx/groundtruth/xlsx_02_sample_sales_data.xlsm.md为主线拆解 Docling面向 gen AI 的文档解析项目如何把一个带宏的.xlsm工作簿转换为可进入大模型/RAG 管线的结构化结果工作表被还原成带语义标注的表格日期、数值、表头等单元格语义被逐项保留并以 Markdown 表格形式稳定输出。读完本文你将掌握 Excel/XLSM 文件在 Docling 中的完整处理链路、groundtruth 三重产物的验证机制以及如何在本地复现与定制这一转换流程。这份 groundtruth 是什么XLSM 宏工作簿的标准转换答案在 Docling 仓库中tests/data/xlsx/目录下存放着一组 Excel 端到端测试样本每个样本同时保留输入文件与期望输出形成可回归验证的黄金标准输入sources/xlsx_02_sample_sales_data.xlsm一个宏启用macro-enabled的 Excel 工作簿期望输出三份同源基准产物groundtruth/xlsx_02_sample_sales_data.xlsm.md —— Markdown 文本导出groundtruth/xlsx_02_sample_sales_data.xlsm.itxt —— 缩进式文档结构树groundtruth/xlsx_02_sample_sales_data.xlsm.json —— DoclingDocument 全量 JSON 序列化。也就是说这篇文章解析的.md并非普通文档而是 Docling 官方回归测试为这个销售数据工作簿锁定的标准 Markdown 答案。它由测试 test_backend_msexcel.py 在端到端转换后通过verify_export与转换结果逐字节比对任何后端行为变动都会在此暴露。值得注意的另一点.xlsm并不需要单独注册格式。在 base_models.py 中InputFormat.XLSX的扩展名列表就是[xlsx, xlsm]因此宏工作簿天然归入 XLSX 处理通道从 JSON 基准文件 的origin.mimetype也能看到它最终被识别为application/vnd.openxmlformats-officedocument.spreadsheetml.sheet。逐行解读.md基准一张销售表的语义还原.md基准文件的核心内容如下为便于阅读原样呈现其全部 20 行数据## SalesData | Product | Date | Quantity | Revenue | | - | - | - | - | | Widget A | 2024-01-01 00:00:00 | 5 | 5000 | | Widget B | 2024-01-02 00:00:00 | 10 | 12000 | | Widget C | 2024-01-03 00:00:00 | 3 | 3000 | | Widget D | 2024-01-04 00:00:00 | 8 | 8000 | | Widget A | 2024-01-05 00:00:00 | 7 | 7000 | | Widget B | 2024-01-06 00:00:00 | 6 | 6000 | | Widget C | 2024-01-07 00:00:00 | 12 | 15000 | | Widget D | 2024-01-08 00:00:00 | 9 | 9000 | | Widget A | 2024-01-09 00:00:00 | 4 | 4000 | | Widget B | 2024-01-10 00:00:00 | 11 | 11000 | | Widget C | 2024-01-11 00:00:00 | 5 | 5000 | | Widget D | 2024-01-12 00:00:00 | 8 | 8500 | | Widget A | 2024-01-13 00:00:00 | 6 | 6200 | | Widget B | 2024-01-14 00:00:00 | 7 | 7100 | | Widget C | 2024-01-15 00:00:00 | 10 | 10500 | | Widget D | 2024-01-16 00:00:00 | 3 | 3200 | | Widget A | 2024-01-17 00:00:00 | 9 | 9400 | | Widget B | 2024-01-18 00:00:00 | 12 | 12500 | | Widget C | 2024-01-19 00:00:00 | 6 | 6100 | | Widget D | 2024-01-20 00:00:00 | 8 | 8900 |这份输出虽然简短却在编码丰富的结构信息可归纳为四层语义第一层工作簿 → 文档标题。JSON 基准中顶层文档name为xlsx_02_sample_sales_data取自源文件名去除扩展名后的 stem见 msexcel_backend.py 中DoclingDocument(nameself.file.stem...)。第二层工作表 → 二级标题。输出以## SalesData开头SalesData就是工作簿中唯一工作表的名字。Docling 对 Excel 采取一表一页、一页一组的建模转换时每个工作表先建一页page再在该页下建立一个labelGroupLabel.SHEET、name工作表名的分组msexcel_backend.pyMarkdown 序列化器再把这个分组渲染成二级标题。第三层连续单元格区域 → 一张表格。标题之下是标准的 GFM 管道表格表头行被单独列出。.itxt基准文件用一行点出它的结构层级item-0 at level 0: unspecified: group _root_ item-1 at level 1: sheet: group SalesData item-2 at level 2: table with [21x4]21x4表示21 行 × 4 列即 1 行表头 20 行数据、4 个数据列与.md、.json完全自洽。JSON 基准中该表的prov记录显示它位于第 1 页、包围盒为l0, t0, r4, b21——包围盒单位正是单元格索引再次印证表覆盖了 4 列 21 行的完整矩形区域。第四层单元格级语义标注。打开 JSON 基准文件 的tables[0].data.table_cells每个单元格都被建模为TableCell携带start_row_offset_idx/end_row_offset_idx、start_col_offset_idx/end_col_offset_idx精确行列区间且首行单元格的column_header被标记为true——这是表头语义被显式保留的直接证据也是后续面向 LLM 的问答、表格检索能正确识别列含义的前提。Date 列格式的秘密为什么是2024-01-01 00:00:00基准数据中日期以YYYY-MM-DD HH:MM:SS文本形式呈现这并非 Excel 的原生显示格式而是转换管线的一个可复现细节后端加载工作簿时使用openpyxl.load_workbook(..., data_onlyTrue)msexcel_backend.py拿到的是公式计算后的缓存值而非公式本身工作簿中 Date 列的底层类型是datetime对象后端在抽取单元格文本时直接调用str(cell.value)msexcel_backend.pyPython 的datetime.__str__因此产出2024-01-01 00:00:00这种日期 零时分秒的形态。理解这一细节有助于处理真实业务文件若你在自己的表格中看到相似的时间戳样式说明源单元格存储的是 datetime 类型值。反过来Quantity/Revenue两列是纯数值如5、5000无千分位也被原样字符串化没有引入任何千分位或货币格式化——Docling 保存的是单元格的逻辑值而不是其在 UI 上套用数字格式后的显示外观。从工作簿到 Markdown背后的后端实现链路要真正看懂这份基准需要把视线投向负责解析 Excel 的后端MsExcelDocumentBackenddocling/backend/msexcel_backend.py。它是声明式后端 分页后端的组合处理步骤可以概括为依赖校验解析依赖openpyxl若缺失会抛出带安装提示的ImportError提示执行pip install docling-slim[format-xlsx]msexcel_backend.py。顺带一提若输入是旧版二进制.xls后端会先经 LibreOffice 转换为.xlsx再解析msexcel_backend.py。工作簿 → 文档骨架convert()创建DoclingDocument写入文件名、MIME 类型与二进制哈希作为DocumentOrigin。逐工作表转页_convert_workbook遍历全部工作表每一页记录page.size其宽高由该页所有条目的包围盒边界推导单位即单元格索引。工作表内容抽取_convert_sheet依次执行找表格 → 找图片 → 找原生图表最后按包围盒的top坐标对组内子元素排序保证视觉顺序与导出顺序一致。表格检测_find_data_tables先从_find_true_data_bounds得到真实数据边界而不是sheet.max_row——该值会被格式刷或幽灵格式撑到百万行再对每个非空单元格执行泛洪填充BFS把四邻域连通的非空区域合并为一张表。这正是 4 列数据被识别为一张连续21x4表而非多张小表的原因。单元格语义化遍历矩形包围盒内每个单元格首行row 0自动打上column_headerTruemsexcel_backend.py合并单元格会换算为row_span/col_span。若数据区左侧上方出现一个横跨多列的独立标题单元格_split_leading_section_label还会把它拆出来单独作为正文文本而不是并入表格。因此本文的.md基准可以看作整条链路的可视化结果## SalesData对应步骤 3/4 的分组21x4表对应步骤 5 的连通区域识别表头行对应步骤 6 的表头标注。三重基准如何互相印证测试代码视角Docling 用一份输入、三种断言来锁定该文件的转换行为相关逻辑全部位于 test_backend_msexcel.py 的test_e2e_excel_conversionspred_md: str MsExcelMarkdownDocSerializer( docdoc, paramsMarkdownParams(compact_tablesTrue, layersDEFAULT_CONTENT_LAYERS), ).serialize().text assert verify_export(pred_md, str(gt_path) .md, GENERATE), export to md pred_itxt: str doc._export_to_indented_text(max_text_len70, explicit_tablesFalse) assert verify_export(pred_itxt, str(gt_path) .itxt, GENERATE), export to indented-text assert verify_document(doc, str(gt_path) .json, GENERATE, fuzzyTrue), document document其中GENERATE来自test_data_gen_flag用于控制直接生成基准还是严格比对基准。三种断言分别覆盖Markdown.md校验面向消费端RAG、LLM 上下文的文本形态使用 Excel 专用的MsExcelMarkdownDocSerializer并开启compact_tablesTrue紧凑表格布局这正是零额外字符、纯管道表格输出风格的来源缩进文本.itxt校验文档结构树root → sheet 分组 → table [21x4]JSON.json以模糊比对方式校验 DoclingDocument 的完整数据模型分组、表格单元格、表头标记、provenance 包围盒。测试夹具documents会扫描 sources 目录 下所有*.xlsx与*.xlsm自动剔除~$开头的 Excel 临时锁文件逐一经DocumentConverter(allowed_formats[InputFormat.XLSX])转换再以同名文件拼接得到 groundtruth 路径test_backend_msexcel.py。你可以把这个夹具当作如何成批转换 Excel 并做回归断言的现成范本。在本地复现这份输出你可以用与测试完全一致的 API 在本地复现.md基准。先确认依赖就绪docling-slim[format-xlsx]或完整docling安装均可它们都带openpyxl随后执行from docling.document_converter import DocumentConverter from docling.datamodel.base_models import InputFormat from docling_core.transforms.serializer.markdown import MarkdownParams from docling_core.transforms.serializer.markdown_excel import ( MsExcelMarkdownDocSerializer, ) from docling_core.types.doc.document import DEFAULT_CONTENT_LAYERS converter DocumentConverter(allowed_formats[InputFormat.XLSX]) result converter.convert( tests/data/xlsx/sources/xlsx_02_sample_sales_data.xlsm ) serializer MsExcelMarkdownDocSerializer( docresult.document, paramsMarkdownParams(compact_tablesTrue, layersDEFAULT_CONTENT_LAYERS), ) print(serializer.serialize().text)输出即上文展示的## SalesData表格。layersDEFAULT_CONTENT_LAYERS的含义值得说明Excel 中的隐藏工作表会被归入ContentLayer.INVISIBLE见 msexcel_backend.py默认导出层不含 INVISIBLE因而隐藏表不会污染 Markdown而单元格批注位于NOTES层需要显式包含该层才会进入导出。如果不想手写序列化代码也可以使用官方 CLI 一次性完成转换与多种格式导出仓库 docling/cli/main.py 中即包含document.save_as_markdown(...)的导出逻辑。批量处理多个工作簿时可参考 examples/batch_convert.py 的循环转换思路。通过 Backend Options 定制输出真实业务里你未必想要每个单元格区域都是一张表的默认行为。Docling 为 Excel 后端提供了MsExcelBackendOptions定义于 docling/datamodel/backend_options.py可通过DocumentConverter的format_options传入参数默认值作用treat_singleton_as_textFalse把孤立的 1×1 单元格周边全空当作TextItem而非TableItem输出避免单格伪表格泛滥parse_chartsTrue解析工作表中嵌入的原生图表柱状/折线/饼图/散点等每个图表输出为带类型分类与底层数据表重建的PictureItemrender_chart_imagesFalse是否调用 LibreOffice 把图表栅格化为图片附加到PictureItem依赖外部安装且会放大输出体积故默认关闭gap_tolerance0允许跨过多少个空行/空列把邻近的数据簇并入同一张表默认为严格模式0sheet_namesNone只转换名称匹配大小写敏感的工作表None表示转换全部工作表例如只想导出名为SalesData的工作表、并把 1×1 孤立单元格降级为普通文本时from docling.document_converter import DocumentConverter from docling.datamodel.base_models import InputFormat from docling.datamodel.backend_options import MsExcelBackendOptions from docling.document_converter import ExcelFormatOption options MsExcelBackendOptions( sheet_names[SalesData], treat_singleton_as_textTrue, ) converter DocumentConverter( allowed_formats[InputFormat.XLSX], format_options{InputFormat.XLSX: ExcelFormatOption(backend_optionsoptions)}, ) result converter.convert(tests/data/xlsx/sources/xlsx_02_sample_sales_data.xlsm)其中gap_tolerance在底层直接驱动_find_table_bounds的 BFS 扩展步长代码会在四方向上跳读最多GAP_TOLERANCE步去找连通单元格msexcel_backend.py因此调大它能让被空列隔开的区块合并成一张大表。对应地仓库测试也覆盖了xlsx_07_gap_tolerance_*、xlsx_08_one_cell_anchor等专门样本验证这些开关的实际效果。局限与注意点结合本样本的延伸思考基于源码与基准文件有几点工程化使用时值得记住宏不会被执行.xlsm仅是带宏的 XLSX其 VBA 代码不会被 Docling 解析或执行data_onlyTrue读取的是工作簿中缓存的公式结果。若目标单元格的值由宏在打开时计算生成且未落盘转换结果可能为空此时需先由 Excel 端打开并另存刷新缓存值。日期/数字格式化不保留 UI 外观基准中的2024-01-01 00:00:00说明输出是单元格逻辑值datetime 的字符串形式而非工作表的显示格式。货币符号、千分位等展示样式同样不会出现在导出的 Markdown 中。无图无损输入输出本样本不包含图片、图表与批注因此.md/.itxt/.json都相当干净一旦工作簿混入图片含 openpyxl 无法解码的 EMF/WMF、原生图表或单元格批注输出结构会显著复杂化Docling 也为此准备了图片抽取、LibreOffice 桥接和NOTES内容层等独立机制对应测试样本为xlsx_emf、xlsx_03_chartsheet、xlsx_comments。总结一份 20 行的销售数据groundtruth浓缩了 Docling Excel 通道的完整设计.xlsm归入 XLSX 格式族工作表被建模为页 sheet 分组连通单元格区域经泛洪填充聚合成21x4表首行自动获得表头语义datetime 值以字符串形式稳定呈现最终通过 Excel 专用 Markdown 序列化器输出## SalesData表格。以该基准为参照你既能理解真实工作簿在转换中可能出现的各种细节时间戳文本、隐藏层、表头标注也能通过MsExcelBackendOptions与多重序列化精确控制交付给下游 LLM/RAG 系统的最终形态。【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考