xberg-io/html-to-markdown · 上手攻略

  • 仓库:xberg-io/html-to-markdown
  • 链接:https://github.com/xberg-io/html-to-markdown
  • 分类:工具库 / HTML→Markdown 转换 / 多语言绑定
  • 作者:spark
  • 更新:2026-08-23

1. 是什么

xberg-io/html-to-markdown 是一个高性能、CommonMark 兼容的 HTML 到 Markdown 转换引擎。Rust 写核心,对外暴露 16 种语言绑定(Rust、Python、Node.js、WASM、Java、Go、C#、PHP、Ruby、Elixir、R、Dart、Kotlin Android、Swift、Zig、C ABI)。同一个 convert() 函数在每种语言里返回的结果是字节级别一致的——这是它最有辨识度的卖点。

背后的团队 Kreuzberg / Xberg 做的是"polyglot document intelligence"基础设施。这个仓库是他们产品线里的"HTML→Markdown"环节,与他们更大的"Xberg(101 格式内容智能引擎)""crawlberg(爬虫 + headless Chrome fallback)""alef(多语言绑定生成器)""liter-llm(165 提供商 LLM 客户端)"等仓库构成矩阵。仓库 MIT 协议。

2. 解决什么问题

市面上的 HTML→Markdown 转换库已经不少(html2text、Turndown、pandoc、markdownify…),但都有几类典型问题:

  • 脏 HTML 失稳:未闭合标签、CDATA、自定义元素、错误实体、嵌套表格、混合编码——这些一上,输出就丢内容。
  • 多语言绑定不一致:同一份 HTML 在 Python / Go / Node 里跑出来结果不一样。
  • 没有元数据:丢掉了 Open Graph / Twitter Card / JSON-LD / microdata / RDFa 这类 head 里的结构化数据。
  • 无法扩展:想自定义某些节点的输出方式?得 fork。

xberg-io/html-to-markdown 的差异化:

  • 三级分层调度(byte scanner → DOM walker → html5ever repair):从最快的字节扫描开始,发现不合法就上升到 DOM 解析,发现还是不合法就交给 html5ever 修复。三档输出字节级一致。
  • 真正脏 HTML 鲁棒:未闭合标签、CDATA、自定义元素、错误实体、嵌套表、混合编码都被默认处理,不用选解析策略
  • metadata 一并返回:head 中的 OG / Twitter / JSON-LD / microdata / RDFa / header hierarchy 在 convert() 一次调用里一起拿回来。
  • Djot 输出:除 CommonMark 外还能直接输出 Djot(轻量标记语言,typst / pandoc 都支持)。
  • Visitor API:feature-gated,能在 AST 阶段转换输出结果而不必 fork。
  • 19–116 MB/s(Wikipedia / mdream corpus 上),每个 PR 都有 per-group regression 阈值守门。

3. 快速安装

按你常用的语言挑一条(同一份 Rust 核心,结果一致):

3.1 Python

pip install html-to-markdown

3.2 Rust

cargo add html-to-markdown-rs

3.3 Node.js / TypeScript

npm install @xberg-io/html-to-markdown
# 或 WASM 版本
npm install @xberg-io/html-to-markdown-wasm

3.4 Go

go get github.com/xberg-io/html-to-markdown/packages/go/v3

3.5 Java(Maven Central)

<!-- groupId: io.xberg · artifactId: html-to-markdown -->
<!-- 具体 version 见仓库 packages/java/README.md -->

3.6 C#(NuGet)

dotnet add package XbergIo.HtmlToMarkdown

3.7 Ruby

gem install html-to-markdown

3.8 PHP(注意是原生 PHP 扩展,用 PIE 安装,不是 composer

pie install xberg-io/html-to-markdown

3.9 Elixir

# mix.exs
{:html_to_markdown, "~> 3.6"}

3.10 R

install.packages("htmltomarkdown", repos = "https://xberg-io.r-universe.dev")

3.11 Dart / Flutter

dart pub add h2m

3.12 Kotlin(Android)

// Maven Central: io.xberg:html-to-markdown-android
// 详见 packages/kotlin-android/README.md

3.13 Swift(Swift Package Manager)

// 详见 packages/swift/README.md

3.14 Zig

// 详见 packages/zig/README.md

3.15 C / C++(FFI)

// 从 GitHub Releases 下载预编译 .so / .dll / .dylib
// 详见 crates/html-to-markdown-ffi

3.16 CLI

# 方式 1:通过 cargo
cargo install html-to-markdown-cli
# 方式 2:cargo-binstall
cargo binstall html-to-markdown-cli
# 方式 3:Homebrew
brew install xberg-io/tap/html-to-markdown

⚠️ 版本号 / 仓库路径核验提示:上述 crates.io / pypi / npm / maven central 的具体 version tag 与各 packages 子目录的最终 packages/python/README.md 等链接,建议安装前到对应 registries 查最新版本——本指南撰写时(2026-08-23)crates.io 与 npm 已确认可访问,但 version 数字未在仓库 README 主页直接列出,必须到各子 README 取最新。

4. 核心用法(Python 为例,其他语言 API 对应一致)

4.1 最简转换

from html_to_markdown import convert

html = """
<html>
  <body>
    <h1>标题</h1>
    <p>这是 <strong>一段</strong> HTML。</p>
    <table>
      <thead><tr><th>A</th><th>B</th></tr></thead>
      <tbody><tr><td>1</td><td>2</td></tr></tbody>
    </table>
  </body>
</html>
"""

md = convert(html)
print(md)
# 输出:
# # 标题
# 
# 这是 **一段** HTML。
#
# | A | B |
# | --- | --- |
# | 1 | 2 |

4.2 拿 metadata(OG / Twitter / JSON-LD / header hierarchy)

from html_to_markdown import convert

html = '<html><head>...'  # 含 OG / Twitter / JSON-LD 的 HTML
result = convert(html, extract_metadata=True)

print(result.markdown)
print(result.metadata.open_graph)   # dict
print(result.metadata.twitter)      # dict
print(result.metadata.json_ld)      # list of dict
print(result.metadata.headers)      # header hierarchy

实际 API 字段名请以 packages/python/README.md 为准——本指南展示的是概念结构("convert 一次返回 markdown + 多个 metadata 通道")。

4.3 输出 Djot 而非 Markdown

md = convert(html, output_format="djot")

4.4 三档预处理预设

md_strict   = convert(html, preset="strict")    # 最严格
md_standard = convert(html, preset="standard")  # 默认
md_lenient  = convert(html, preset="lenient")   # 最宽松
# 或自定义
md_custom = convert(html, preset={"drop": ["script", "style"], ...})

4.5 Visitor API(feature-gated,转换 AST 阶段)

from html_to_markdown import convert, visit

def rewrite(node, ctx):
    if node.tag == "a" and node.attrs.get("href", "").startswith("http://"):
        node.attrs["href"] = node.attrs["href"].replace("http://", "https://")
    return node

md = convert(html, visitors=[rewrite])

4.6 inline 图片镜像(data URI / 远程图)

md = convert(html, inline_images=True, mirror_remote=True)

⚠️ mirror_remote=True 会触发网络 IO 拉图,务必在确认目标站点允许 / 自己有缓存策略时再用。

4.7 CLI 一次性转

html-to-markdown input.html > output.md
echo "<p>hello</p>" | html-to-markdown

5. 典型适用场景

  • RAG / LLM 喂料前的清洗:把抓来的 HTML 文档转成结构干净的 Markdown,附 metadata 喂给检索 / 生成 pipeline。一次转换拿 markdown + OG + JSON-LD,省得再跑一次 head parser。
  • 爬虫 / 内容聚合:搭配同团队 crawlberg,构成「HTTP 抓取 → 渲染兜底 → HTML→Markdown」的端到端方案。
  • CMS / 文档站导出:从富文本编辑器存下来的脏 HTML 转 Markdown 入库;未闭合标签、CDATA、自定义元素都不用预处理。
  • 跨语言微服务:上游 Python、下游 Go / Node / Rust,都对同一份 HTML 跑出字节级一致的 Markdown——做"内容契约"很合适。
  • 整库批处理:19–116 MB/s 的吞吐适合一次性处理百万级 HTML 页面。
  • 构建 Djot 文档:需要 typst / pandoc 链路时直接出 Djot,省一道转换。

不适用:

  • "我只是想渲染 markdown 给人看"——直接用 markdown 渲染器即可,没必要从 HTML 反向;
  • 需要 100% 保留 HTML 原结构(包括 inline style、复杂 attr)——Markdown 表达力有限,会丢;
  • 不能跑 Rust 编译产物的环境(如果只允许纯 JS / 纯 Python,且性能要求在 MB/s 内——可以用 WASM / 纯 Python 备选,但性能会大幅下降)。

6. 坑与注意

  • ⚠️ 不要把 Markdown 反向转 HTML 当主要用途:本仓库是单向 HTML→Markdown。要做反向,请看其他工具(如 markdown Python 库)。
  • ⚠️ PHP 用户必须用 PIE,不能 composer require——它是 Rust 写的原生扩展(ext-php-rs),composer 装不上。
  • ⚠️ GFM 表格的对齐与管道字符转义:库会按 CommonMark 规范转义 |;如果你的下游消费者有自定义的 GFM 解析器差异,最好先在 demo 页 docs.html-to-markdown.xberg.io/demo/ 验证。
  • ⚠️ mirror_remote=True 会阻塞:拉图是同步 IO,长跑建议先批量预下载再用 data: URI 注入。
  • ⚠️ 版本号在仓库 README 没集中列表:每个语言的最新 version 在对应 packages/<lang>/README.md 里,安装前最好去对应 registry(PyPI / crates.io / npmjs / Maven Central / NuGet / RubyGems / Packagist / hex.pm / pub.dev)确认最新版本号,避免锁到过期版本。
  • ⚠️ Djot 输出不是 Markdown:下游消费者如果是"只认 GFM / CommonMark"的渲染器(如 GitHub Markdown、Typora),把 Djot 当 Markdown 灌进去会渲染异常。
  • ⚠️ C/C++ FFI 链接:跨语言 ABI 调用要自己管内存(Rust 侧返回的字符串生命周期),建议直接看 crates/html-to-markdown-ffi 里的样例。
  • ⚠️ CLI brew tap 命名空间:tap 完整名是 xberg-io/tap/html-to-markdown,别忘了 owner。
  • ⚠️ 仓库未列 benchmark 复现脚本19–116 MB/s on the Wikipedia/mdream corpus 是 README 给的数字,但 corpus 与跑分脚本没在仓库主目录公开。要复测得自建测试集。

7. 与同类对比

  • vs Turndown.js(Node):Turndown 是 Node 生态的事实标准,插件体系丰富;本仓库是 Rust 核心 + 多语言绑定,性能高一档,但 Node 插件生态刚起步。
  • vs html2text(Python):html2text 古老、API 简单;本仓库在脏 HTML 鲁棒性 / metadata 提取 / 跨语言一致性三方面领先。
  • vs pandoc:pandoc 是文档格式转换瑞士军刀,HTML→Markdown 只是它几十种格式之一;本仓库专攻这一条,API 更简单、性能更聚焦、绑定更多。
  • vs Marked / markdown-it:这两个是 Markdown→HTML 的,方向相反,不可直接比较。
  • vs crawl4ai / markdownify:crawl4ai 偏 AI 抓取框架,HTML→Markdown 是它内部的一步;markdownify 是 Python 老牌库但更新慢。
  • vs 同团队其他仓库:crawlberg 是爬虫、Xberg 是更广的文档智能(101 格式 + OCR)、alef 是绑定生成器、liter-llm 是 LLM 客户端——本仓库是矩阵里的"HTML→Markdown"节点,不是孤立工具。

8. 一句话推荐

如果你有多语言栈 / 脏 HTML / 要 metadata / 要高性能 HTML→Markdown,xberg-io/html-to-markdown 是当前(2026-08)最值得装的方案——同一份 Rust 核心、字节级一致输出、16 种语言绑定,省去你在不同语言里维护多套转换逻辑的麻烦。

参考链接:

  • 仓库主页:https://github.com/xberg-io/html-to-markdown
  • 文档站:https://docs.html-to-markdown.xberg.io
  • 在线 Demo:https://docs.html-to-markdown.xberg.io/demo/
  • Releases(含 FFI 预编译 .so/.dll/.dylib):https://github.com/xberg-io/html-to-markdown/releases
  • Discord:https://discord.gg/xt9WY3GnKR
  • 同团队矩阵:
  • Xberg(101 格式内容智能引擎,MIT):https://github.com/xberg-io/xberg
  • crawlberg(爬虫 + headless Chrome 兜底):https://github.com/xberg-io/crawlberg
  • liter-llm(165 提供商 LLM 客户端):https://github.com/xberg-io/liter-llm
  • tree-sitter-language-pack:https://github.com/xberg-io/tree-sitter-language-pack
  • alef(多语言绑定生成器):https://github.com/xberg-io/alef
  • 商业版:Xberg Pro(自托管内容智能后端,单容器)、Xberg Enterprise(K8s 上的分布式治理平台):https://xberg.io

⚠️ 核验状态:仓库主 README 中各语言安装命令与 crates.io / PyPI / npmjs / Maven Central / NuGet / RubyGems / Packagist / hex.pm / pub.dev 的可访问性已确认;具体 version tag(除 Elixir 的 ~> 3.6 出现在 README 显式位置外)未在主 README 集中列出,建议安装前到对应 registry 取最新版本号。