Ming ToMarkdown
将 PDF、Word、Excel、PowerPoint、图片和文本文件转换为 Markdown 的 Python 包,支持终端命令和 Python 调用。
安装包名为 ming-tomarkdown,终端命令和 Python 导入名仍为 tomarkdown。GitHub 仓库名称保持 ToMarkdown。
默认开启图片 OCR:正文文字直接提取,图片交给视觉模型识别,再合并为 Markdown。OCR 请求默认最多 4 个并发,保留原有图片尺寸,不缩图、不截短输出。支持单文件和混合格式文件夹,批量转换会保留输入文件夹名称。
安装
需要 Python 3.12 或更高版本。在已有虚拟环境中,从 PyPI 安装:
uv pip install ming-tomarkdown
也可以使用 pip:
python -m pip install ming-tomarkdown
尚未创建虚拟环境时,先执行 uv venv --python 3.12 并激活环境。macOS / Linux 使用 source .venv/bin/activate,Windows PowerShell 使用 .venv\Scripts\Activate.ps1。
也可以直接安装 GitHub 发布的 wheel,无需 Git:
uv pip install "https://github.com/MingWangD/ToMarkdown/releases/download/v0.1.7/ming_tomarkdown-0.1.7-py3-none-any.whl"
也可以通过 pip 从 GitHub 源码安装:
python -m pip install "ming-tomarkdown @ git+https://github.com/MingWangD/ToMarkdown.git@v0.1.7"
这种安装方式需要本机已有 Git。也可以从仓库的 Releases 下载 .whl,直接安装,无需 Git:
python -m pip install ./ming_tomarkdown-0.1.7-py3-none-any.whl
仅使用终端命令,也可以用 uv 安装:
uv tool install ming-tomarkdown
旧版 GitHub 包的安装名为 tomarkdown,升级前请在对应环境运行 uv pip uninstall tomarkdown(或 python -m pip uninstall tomarkdown),再安装新包。两者提供相同的导入模块和命令,不应同时安装。
快速开始
无需模型配置即可先验证普通转换:
tomarkdown "/路径/文档.pdf" --no-ocr
配置好下一节的 OCR 服务后,直接运行:
# 单个文件
tomarkdown "/路径/论文.pdf"
# 混合格式文件夹:PDF、Word、Excel 等
tomarkdown "/路径/资料"
终端命令不需要先 import。如果终端找不到 tomarkdown,可以用安装该包的 Python 运行:
python -m tomarkdown "/路径/资料"
通用 OCR 配置
使用支持图片输入、兼容 OpenAI Chat Completions 接口的模型服务。在运行命令的目录创建 .env:
OPENAI_API_KEY=服务提供的_API_Key
OPENAI_MODEL=服务支持的视觉模型名称
OPENAI_BASE_URL=服务提供的_OpenAI_兼容接口基础地址
OPENAI_ 是变量名称,不限定模型提供商。密钥、模型和接口地址需要对应同一个服务;接口地址不要追加 /chat/completions。也可以直接设置系统环境变量,同名系统环境变量优先于 .env。
配置优先级:
- 模型:显式参数 →
MODEL→OPENAI_MODEL→GEMINI_MODEL→gemini-3.8-flash-high。 - 密钥:Python 调用的
api_key参数 →GEMINI_API_KEY→OPENAI_API_KEY。 - 接口:显式参数 →
OPENAI_BASE_URL→http://127.0.0.1:8045/v1。
最后的模型名和接口地址沿用原项目默认值,不代表你的服务一定提供该模型。使用通用配置时,应填写实际服务信息,并清除不再使用的 MODEL、GEMINI_API_KEY 等旧值。
一个具体例子
假设本地服务已在 http://127.0.0.1:8045/v1 启动,提供名为 vision-model 的视觉模型。在当前目录的 .env 中填写:
OPENAI_API_KEY=你的本地服务密钥
OPENAI_MODEL=vision-model
OPENAI_BASE_URL=http://127.0.0.1:8045/v1
vision-model 和密钥需要替换为实际值。然后运行:
tomarkdown "/路径/论文.pdf"
结果保存在当前目录下的 results/论文.md。
在 Python 中调用
from tomarkdown import convert
# 默认开启 OCR,读取当前工作目录的 .env
result = convert("/路径/资料")
for path in result.files:
print("已保存:", path)
for source, error in result.errors.items():
print("转换失败:", source, error)
也可以直接传入配置:
import os
from tomarkdown import convert
result = convert(
"/路径/论文.pdf",
api_key=os.environ["OPENAI_API_KEY"],
model="你的视觉模型名称",
base_url="http://127.0.0.1:8045/v1",
show_progress=True,
)
关闭 OCR:
result = convert("/路径/报告.docx", ocr=False)
convert() 返回 ConversionResult:files 是成功输出的 Path 列表,errors 是输入路径到错误信息的字典。单个文件转换失败后会继续处理其他文件;无效输入路径和无效性能参数会直接抛出异常。只有实际请求图片 OCR 时才初始化模型客户端和检查密钥;缺少密钥会记录为该文件失败。纯文字表格保留默认 ocr=True 也不需要 API Key。
用于审查材料接入:来源记录
result = convert("/路径/资料", evidence=True, show_progress=True)
for source, metadata in result.evidence_files.items():
print("原材料:", source, "来源记录:", metadata)
命令行使用 --evidence。该模式输出 报告.pdf.md 和 报告.pdf.evidence.json,保留输入扩展名,避免同目录的 报告.pdf 与 报告.docx 相互覆盖。相同输入名称的再次转换仍会替换已有结果,不是历史版本存储。
JSON 记录原文件 SHA256、输出 SHA256、转换器及解析依赖版本、OCR 模型/提示词摘要、覆盖范围与待复核项,不保存 API Key。读取方必须校验哈希,并按 result.errors 处理失败。来源记录最后发布,写入失败不会把不匹配的 Markdown/JSON 当作成功结果;这不替代多进程任务存储与事务系统。
XLSX 直接按原始网格转换,以行号和 Excel 列号定位,不猜测哪一行是表头。保留工作表、单元格原值、类型、数字格式、公式及已有缓存、批注、隐藏状态和合并范围;合并延续显示 ↳ 原坐标,真正空白保持空白,正文竖线转义、换行显示为 <br>。合并区域下仍可能存有不显示的原值,这些底层值会另外保留并标注 covered_by_merge,存在差异时提示复核,不擅自选用。
开启 OCR 时,XLSX 内支持的嵌入光栅图片按原字节识别;未识别、无文本返回或不支持的媒体明确列为待复核。媒体仅定位到压缩包内文件部分,暂未关联工作表中的显示锚点,不能直接用于业务图片计数。公式不重新计算;一般 Excel 显示格式、图表、形状及真实性不在验证范围内。
XLS 也按相同的行列网格和来源格式输出,保留原值、单元格地址、类型、数字格式、合并范围、隐藏状态和可读取的批注/超链接。旧 BIFF 的公式只读取保存的缓存值,不能可靠恢复公式表达式或判断缓存是否最新;嵌入图片、图表和形状暂未提取/OCR,均明确提示复核,不能宣称媒体完整。
XLS/XLSX 来源定位到单元格;其他格式当前只承诺文件级来源。OCR PDF 的 Markdown 仍有页码标题,但 JSON 尚无字段/表格框坐标。converted_requires_review 只表示完成转换且仍需复核,不表示材料合规或业务审查通过。项目一的适用范围及验收建议见 接入评估。
ocr=True 是允许图片识别,不是强制整份文档 OCR。XLSX 单元格由 openpyxl 读取,XLS 由 xlrd 读取;图片以外的表格内容不发给模型。其他格式按各自解析器/MarkItDown 路径读取。来源 JSON 的 configuration.ocr 表示这个开关,不表示确实发生模型调用;整个调用的模型请求数查看 result.ocr_requests。Agent 接入示例见 材料读取约定。
Python 调用默认不显示终端进度,可用 show_progress=True 开启;也可通过 results_dir="/路径/results" 指定结果根目录。
输出规则
安装包后,命令及 Python 调用默认将结果写入当前工作目录的 results/,不会写进包的安装目录。所需目录会自动创建。
单文件输入:/路径/论文.pdf
输出: results/论文.md
文件夹输入:/路径/资料/
├── 论文.pdf
├── 报告.docx
└── 数据.xlsx
输出: results/资料/
├── 论文.md
├── 报告.md
└── 数据.md
保留源文件名,只把扩展名改为 .md,例如 报告.v2.pdf → 报告.v2.md。同一输出目录下的同名结果会覆盖,报告.pdf 与 报告.docx 也会写入同一个 报告.md。
文件夹只处理当前层,按文件名排序读取,不进入子文件夹,跳过不支持的扩展名。开启 OCR 时最多同时处理 4 个文件,仍按文件名顺序写入,保留上述同名覆盖规则。终端显示文件序号、总耗时、OCR 请求数、复用次数及成功/失败数量,不显示页数或百分比;存在转换失败时命令以非零状态退出。
命令参数
| 参数 | 说明 |
|---|---|
file |
文件或文件夹路径,必填 |
--model |
指定视觉模型,覆盖环境变量 |
--base-url |
指定 OpenAI 兼容接口基础地址,覆盖环境变量 |
--no-ocr |
关闭 OCR,不调用模型;默认开启 OCR |
--workers |
全部文件与 PDF 图片共享的 OCR 请求并发上限,默认 4,范围 1–32;1 为串行 |
--timeout |
单次请求超时秒数,默认 600;不是整份文档的时间限制 |
--max-retries |
SDK 对可重试请求的重试上限,默认 2;0 为不重试 |
-h、--help |
查看帮助 |
tomarkdown "/路径/文档.pdf" --model "你的视觉模型名称"
tomarkdown "/路径/资料" --no-ocr
tomarkdown --help
OCR 提速与完整性
# 多页 PDF:并发识别图片,保持页序和图文顺序
tomarkdown "/路径/论文.pdf" --workers 4
# 文件夹:文件并发与 PDF 内图片共用同一个请求上限
tomarkdown "/路径/资料" --workers 4
# 服务限流或排查问题时,退回串行
tomarkdown "/路径/资料" --workers 1
Python 调用使用相同参数:convert(path, workers=4, timeout=600, max_retries=2)。
- PDF 只打开一次,顺序提取图片及版面,仅模型请求并发执行;任意请求完成后立即补入后续图片,不等待排在前面的慢请求。PDF 渲染在整个进程内串行,避免渲染库的线程安全问题。
- 单个 DOCX 内的图片也会并发识别,仍保留原插件的图文顺序;PDF、DOCX 和文件夹共享全局请求上限。
- 同一次
convert()中,相同模型、提示词和图片请求共享结果;同时到达的重复请求也只发送一次。最多保留 128 个成功响应,失败不缓存。缓存不写磁盘,下次运行重新识别。 - 独立图片按原字节发送,不缩小、不重新压缩、不跳过小图;PDF 延用插件的图片提取策略,无法提取的区域按既有策略渲染,整页后备识别使用 300 DPI。保持完整提取提示词,不人为限制输出长度,不自动切换模型。
- 文件和 DOCX 调度最多保留两倍
workers个未输出结果;PDF 限制未完成 OCR 任务数,已完成的文本等待按原顺序合并。单页图片仍一次性提取,因此高分辨率、多图片文件的内存占用取决于页内容;网络并发上限不等于驻留图片上限。 - 单张首次识别仍取决于服务和模型响应;并发不会缩短单个请求的推理时间。优先保证输入信息完整,不以缩图换速度。LLM 识别内容是否完整仍需核对原文。
ConversionResult 增加 ocr_requests、ocr_cache_hits 和 elapsed_seconds。请求数按 SDK 调用计数,不含 SDK 内部重试;总耗时包含文件处理。默认 4 是起点,服务只支持单请求或出现明显限流时使用 --workers 1。
验证方式:
uv run python -m unittest discover -s tests -v
uv run python tests/benchmark_ocr.py
第二条命令用固定延迟的假模型验证调度与输出一致性,不访问外部服务。实际审查及本次实测见 OCR 优化审查报告。
仍然等待很久时
先单独转换目标文件,区分单文件耗时与整个目录耗时。目录包含的 Word 文档也可能有大量图片需要识别。
并发无法缩短单个模型请求自身的耗时。某些高推理配置会产生大量思考 token;如果服务支持较低推理配置,可在核对识别内容后通过 model= 或 --model 显式选择。程序不会自动替换模型,也不会缩图或限制输出长度。
timeout=90, max_retries=0 可用于诊断长时间无响应,超时会报错而不是省略该图片继续宣称成功;它不是提速手段。默认仍为 timeout=600, max_retries=2。
使用 uv 项目时,除了更新环境中的包,还应更新项目锁文件:uv lock --upgrade-package ming-tomarkdown。Notebook 或交互窗口升级后需重启内核,让已导入的模块重新加载。
0.1.5 的调度修复及实际样本诊断见补充报告。
支持的内容与处理流程
支持 PDF、Word(.docx)、Excel(.xlsx、.xls)、PowerPoint(.pptx)、图片、HTML,以及 TXT、CSV、JSON、XML 等文本格式。旧版 Word .doc 不在批量处理范围内。
这一页 PDF
├── 文字层 → 直接提取
└── 图片 → LLM 识别
↓
合并成 Markdown
扫描件中的正文通常属于图片,需要 OCR。复杂排版、表格和公式的还原取决于文件内容、MarkItDown 插件及模型。JSON、XML 等文本主要保留原文。模型请求异常、明确拒答或输出截断会计入该文件失败,不写入新的不完整结果;其他文件继续处理。合法无字图片可以返回空文本。插件内部的图片提取/解析错误仍可能影响内容完整性,模型本身也可能漏识。
从源码运行与构建
git clone https://github.com/MingWangD/ToMarkdown.git
cd ToMarkdown
uv sync
# 安装包后的命令入口
uv run tomarkdown "/路径/资料"
# 保留原有脚本入口:读取项目 .env,结果固定保存到项目 results/
uv run python to_md.py "/路径/资料"
# 生成 wheel 和源码包,输出到 dist/
uv build --no-sources
核心代码位于 src/tomarkdown/。根目录的 to_md.py 和 ui.py 保留为原有用法的兼容入口。发布包不包含 .env、转换结果或本地样例文件。
维护者:首次发布到 PyPI
仓库提供 .github/workflows/publish.yml,通过 GitHub Actions 可信发布,无需配置 PyPI API Token。
首次使用时,登录 PyPI 并打开账号的 Publishing 页面,添加 GitHub 待发布项目,填写:
| 字段 | 值 |
|---|---|
| PyPI Project Name | ming-tomarkdown |
| Owner | MingWangD |
| Repository name | ToMarkdown |
| Workflow name | publish.yml |
| Environment name | pypi |
保存后,在 GitHub Actions 的 Publish to PyPI 流程中选择 Run workflow,勾选 Publish to PyPI after building 即可发布;不勾选时仅检查构建。此后发布新的 GitHub Release 也会自动触发 PyPI 发布。发布新版本前需先更新 pyproject.toml 中的版本号及依赖锁定文件。
首次发布成功后,其他人就可以用 uv pip install ming-tomarkdown 安装。
Metadata
Release files for ming-tomarkdown 0.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ming_tomarkdown-0.1.7.tar.gz | 23.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ming_tomarkdown-0.1.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.9 kB
Release files / ming_tomarkdown-0.1.7.tar.gz
| Download URL | ming_tomarkdown-0.1.7.tar.gz |
|---|---|
| Size | 23.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f1d8deb46cdb754f2055feb76db347e305db4e4503b508dac2299d1c84f7c85c
|
|
BLAKE2b-256 checksum How to use checksums |
b899bf6ddde6c54a40131e35df41a75f91a615026990730dcd4ea7e1668dfe46
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / ming_tomarkdown-0.1.7-py3-none-any.whl
| Download URL | ming_tomarkdown-0.1.7-py3-none-any.whl |
|---|---|
| Size | 27.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9d183e2ebdf732dfe8819a0798e82b857d6d22b029f105dca5e4ee74ed61f075
|
|
BLAKE2b-256 checksum How to use checksums |
16398c931fb0c78be459ced6d10ab93936cf60e946d0566992cf2d38db393297
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for ming-tomarkdown 0.1.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.