在這份 metadata 截圖中,turndown 是四個受測函式庫裡 GitHub stars 最多的一個,擁有 11,386 顆星。針對四個共用的 HTML 測試樣本,它在核心預設設定下產生了 0 個 Markdown 表格,而且在相同輸入下,使用的 token 數量比 markdownify 多出 24.6%。
四個工具都完整保住了所有內容探針。差別完全在於結構被如何處理,以及這些結構在後續流程中會帶來多少成本。
測試了什麼,以及使用了哪些樣本
這份研究基礎原本就有一組針對 markitdown 的測試包,包含五個 HTML 樣本與預先註冊的探針字串——也就是會逐字檢查是否存活的精確字串,另外還有用來辨識頁面雜訊的 boilerplate 字串。整體比較採用的是四個所有四款轉換器都共同使用的樣本:書店型錄、引言網站、冰球統計表,以及 Wikipedia 上關於網頁爬蟲的文章。第五個樣本 Nothing but tables 只作為偏重表格的診斷用,不納入 24.6% 的總體統計。
四個工具分別是:turndown 7.2.4(Node)、markdownify 1.2.3、html2text 2025.4.15,以及 markitdown,後者直接使用其已公開的數據列。頁面包含:書店型錄、引言網站、冰球統計表,以及 Wikipedia 的網頁爬蟲條目。
下方每個指標名稱都與 markitdown 自己的產物欄位一一對應,因此這些列可以直接並排比較,不需要把兩種不同定義硬湊成同一個詞。
表格

| 轉換器 | 內容探針 | 輸出字元數 | Token 數量 (o200k) | 每 token 字節數 | Markdown 表格列數 | 連結數 |
|---|---|---|---|---|---|---|
| turndown | 16/16 | 95,188 | 26,236 | 3.63 | 0 | 611 |
| markdownify | 16/16 | 76,868 | 21,062 | 3.65 | 36 | 599 |
| html2text | 16/16 | 76,452 | 21,176 | 3.61 | 32 | 545 |
| markitdown | 16/16 | 76,995 | 21,336 | 3.61 | 36 | 598 |
四個樣本——也就是 markitdown 也有跑的那四個。各樣本完整數據可見 fiveway-scores.json。Token 使用 o200k_base 計算;markitdown 的表格列數則是從其自己儲存的 Markdown 重新統計,使用與其他工具相同的計數器。
內容保留打成平手。 四個樣本中共 16 個 body 探針,全部都在每個轉換器的輸出中保留下來。如果你只在意「文字會不會過去」,這四個答案都是會。
結構保留並不是平手。 在這四個共用樣本中,markdownify 與 markitdown 都輸出 36 行 Markdown 表格列;html2text 輸出 32 行;turndown 則是 0 行。至於獨立的「全表格」診斷樣本,markdownify 輸出 62 行、html2text 輸出 59 行;這些列數不納入上面的總計。
turndown 的 token 成本高出 24.6%,而且原因不是表格。我原本以為是表格造成的,但逐樣本數據說明並非如此——如下所示。
turndown 對表格做了什麼
以下是冰球統計樣本,同樣的資料列,三種呈現方式。
turndown:

Team Name
Year
Wins
Losses
Boston Bruins
1990
44
24
markdownify:
| Team Name | Year | Wins | Losses | ... |
| --- | --- | --- | --- | --- |
| Boston Bruins | 1990 | 44 | 24 | ... |
html2text:
Team Name | Year | Wins | Losses | ...
---|---|---|---|---
Boston Bruins | 1990 | 44 | 24 | ...
turndown 的轉換沒有把任何數值弄丟,所以它在探針上可以拿到 16/16。真正沒有留下來的是每個數值對應哪一欄。如果直接看 turndown 的輸出,44 只是一個孤零零的數字;你無法知道它是 Boston 的勝場數,除非自己去數位置,還得祈禱中間沒有空白儲存格。這個樣本裡偏偏就有空白儲存格,所以光靠數位置也行不通。
對模型來說,這代表的是:它看到的是一個可以回答問題的表格,還是只會猜測的一串數字。
這不算是 bug,比較像是已知的設計邊界——turndown 核心本來就不處理表格,而 turndown-plugin-gfm 就是用來補上這個功能的。不過預設安裝並不包含這個插件,而 11,386 顆星也表示很多人確實是直接用預設版。
token 差距真正從哪裡來
我上面那段話原本寫得很肯定,認為 24.6% 的 token 差距是因為表格被攤平成文字。後來我改成逐樣本檢查,結果不是。
| 樣本 | turndown tokens ÷ markdownify tokens |
|---|---|
| 引言網站(沒有表格) | 0.99× |
| 書店型錄 | 1.06× |
| 冰球統計(單一大型表格) | 1.37× |
| Wikipedia(以散文為主,含 9 行表格) | 1.29× |
| 全表格樣本 | 0.78× |
在那個全都是表格的樣本裡,turndown 反而便宜 22%——因為 Markdown 的 pipe 骨架本身也會吃 token,而 turndown 完全不會輸出這些東西。單純把表格攤平,並不必然代表 token 成本更高。
Wikipedia 那個樣本佔整體的 74%,而且只有 9 行表格。它那 15,378 個字元的差距不可能是表格造成的。真正的原因是這些內容:
(function(){var className="client-js vector-feature-language-in-header-enabled…
.mw-parser-output cite.citation{font-style:inherit;word-wrap:break-word}…
(RLQ=window.RLQ||[]).push(function(){mw.config.set({"wgHostname":"mw-web…
turndown 不會移除 <script> 和 <style> 內容,但 markdownify 與 html2text 會。 以只會出現在這些元素中的標記來統計:turndown 的輸出在各樣本中帶有 10 個 script 標記和 84 個 style 標記;另外兩個工具兩者都為 0。在 Wikipedia 頁面上,MediaWiki 內嵌的 JavaScript 設定與 CSS 共 8 行,就佔了 14,644 個字元——也就是整體差距的 95% (script-style-stripping.json)。
這才是我真正在意、也真的會採取行動的結論。攤平表格是你看得見的結構問題;但 Markdown 裡混進一大段 JavaScript 設定,則是毫無資訊、純粹浪費的成本,而在真實世界頁面上,它帶來的負擔遠遠超過這次比較中的其他因素。
html2text 會輸出你一開始可能認不出來的表格
html2text 的表格列數是 32,低於 markdownify 和 markitdown 的 36,而我一開始的計數器只算出 1。
問題不在函式庫,而在我的計數規則。html2text 會輸出 Team Name | Year | Wins 但沒有前後的管線符號——這是常見的 Markdown 表格形式,只是用要求 ^\|.*\|$ 的 regex 是看不出來的。我當時就是這樣寫 regex、這樣跑,還差點直接報告 html2text 不支援表格。
其實它有。修正後的計數器改用一個啟發式:一串連續包含 pipe 的行,中間夾著一行分隔列。用這個規則,html2text 的表格列數就從 1 變成 32。這不是完整的 Markdown parser,因此這些列數應該被解讀為「在有記錄的計數器下量到的結果」,而不是絕對的渲染真理。
如果你會用自己的 regex 去後處理 Markdown,這件事很值得記住:這四個函式庫裡,有兩個會輸出外層 pipe,有一個不會。
很少人提到的授權問題
| 轉換器 | 授權 | 安裝內容 | 冷啟動匯入 | Stars | 最後發布 |
|---|---|---|---|---|---|
| turndown | MIT | 3 個 npm 套件,8.8 MiB | 0.056 s | 11,386 | 2026-04-03 |
| markdownify | MIT | 5 個套件,1.8 MiB | 0.046 s | 2,235 | 2026-06-30 |
| html2text | GPL-3.0-or-later | 1 個套件,0.2 MiB | 0.077 s | 2,168 | 2025-04-15 |
| markitdown | 依其套件 metadata 為準 | 本次安裝未測量 | 未測量 | — | — |
install-and-import.json。每個函式庫都安裝在自己獨立的空白環境中。授權資訊分別從三個地方確認:registry metadata、GitHub repo,以及已安裝套件自己的 METADATA 檔,其中寫著 License-Expression: GPL-3.0-or-later。
在這份安裝比較裡最輕量的函式庫——1 個套件、0.2 MiB——是 GPL-3.0-or-later。這會不會影響專案,要看軟體如何組合與散布。把它當成授權負責人需要確認的選擇節點就好;本文不是法律意見。markitdown 在這裡顯示為未測量,是因為這份產物沒有捕捉到它的安裝與授權資訊。
這種取捨很容易被忽略,因為授權是 benchmark 裡看不見的屬性。
不過,這三者都比同類別中的文章擷取器輕得多——那些通常會落在 21 到 70 MiB。轉換器本來就比擷取器小很多,依賴預算要把兩者分開看。
在這張表成立之前,我必須先修正兩個混淆因子
上面的數字其實已經是第三版。前兩版有問題,而且那兩個問題都很容易重現。
五個樣本對四個樣本。 markitdown 只跑了這五個檔案中的四個,另外三個工具則全跑五個。把每個工具自己的測試集加總,會得到 markdownify 98 行表格列、markitdown 36 行,看起來像是能力差距。若只看相同的四個樣本,則是 36 對 36——完全平手。第五個樣本偏偏就是表格最多的那個,因此這個混淆會往最糟的方向偏移,讓新進工具看起來比既有工具多出將近三倍。

兩個計數器,一個欄位。 markitdown 公開的 md_table_rows 是來自它自己的程式碼,而我當時還沒讀過。拿那個數字去跟我的計數器比,可能是在比較兩個計數方式,而不是兩個轉換器。它的 Markdown 輸出有存到磁碟,因此修正方式就是用同一個計數器跑完四個樣本——而當我這麼做後,markitdown 的重算結果與它公開的每個樣本數值完全一致(0、0、27、9)。定義是一致的;只是我一開始不查就不會知道。
這兩個錯誤都不會從輸出表面看出來,但都會產生一張看似很有把握、其實完全錯誤的表格。
誰適合用哪一個
你要把頁面內容餵給模型,或是儲存像這類頁面的結構化內容? 先從 markdownify 開始。它在這個計數器下的表格列數與 markitdown 打平,token 數也落在最低的一群,授權是 MIT,安裝體積只有 1.8 MiB。先用你自己的頁面形狀驗證,再決定要不要標準化。
你的依賴預算是以 KB 計,且不需要對外散布? 選 html2text。1 個套件、0.2 MiB,表格也還在。先確認 GPL 相關問題,另外也要注意它的最後發布時間是 2025 年 4 月。
你本來就在 Node 技術棧裡? 那就用 turndown,並搭配 turndown-plugin-gfm;但在交給它之前,先把 HTML 裡的 <script> 和 <style> 移除,因為 turndown 不會幫你做。這兩個缺口會讓 token 多出四分之一,也會讓你的表格結構直接消失,而且在看輸出之前你根本不會知道。
你原本就會轉其他文件格式? markitdown 也能處理 PDF、Office 等格式,而它的 HTML 輸出與專門的轉換器相比也很有競爭力。少一個依賴,就值得考慮。
代管 API 的位置在哪裡
上面這四個工具都只處理你已經拿到的 HTML。它們不會幫你抓頁面、不會渲染 JavaScript,也不會處理反機器人層;而對很多真實目標來說,真正更難的其實是後半段。
我們在 Thunderbit 的開發者工具鏈就是負責這一段。POST /distill 接收一個 URL,會回傳乾淨、適合 LLM 使用的 Markdown,抓取與渲染都已處理好;POST /extract 則會依照你提供的 JSON Schema,回傳 AI 對齊的結構化 JSON,輸出形狀又不同了——是列資料,而不是你之後還得自己解析的 Markdown 表格。這兩個功能都可透過 MCP server 與 CLI(npx @thunderbit/thunderbit-cli)使用。價格可見 Thunderbit 定價頁。
比較務實的說法是:如果你手上已經有 HTML,而且只是想轉成 Markdown,那 markdownify 免費、表現也很好,而這張表也告訴你它的競品中還有哪些同樣適合。如果你要的是抓頁面,或是結構化列資料而不是散文,那就是另一種採購決策了。
若你想看更完整的領域整理,我們的 web scraping API 總覽 介紹了代管方案,而 開源爬蟲主文 則涵蓋自架式工具。用 Python 把 HTML 轉成 Markdown 是實作導讀,而 llms.txt 想標準化的是什麼 則說明了這個類別的下一步走向。
結論
就這個四樣本工作負載來說,markdownify 是最強的預設選擇:在共用計數器下,它的表格列數與 markitdown 打平,token 數與其他高效率轉換器相比也只差 1.3% 以內,授權是 MIT,安裝大小只有 1.8 MiB。
這個「受歡迎程度」和「實際表現」之間的落差,才是這篇文章真正的重點。turndown 是個很棒的函式庫,很多人都已經安裝,但沒有搭配能處理表格的插件;這個缺口會反映成多出四分之一的 token,以及一個不再存在的表格結構。在你真的去計算之前,這兩件事都不會在任何地方顯示出來。
只要記住一件事:在把輸出交給模型之前,先確認你的轉換器到底怎麼處理表格。這四個工具裡,有三個會做出合理結果。最受歡迎的那個不會,除非你有特別告訴它。
免費試用 Thunderbit 網頁資料擷取 Get Started Free
常見問題
turndown 真的不支援表格嗎?
它的核心不支援。表格是由 turndown-plugin-gfm 這個獨立套件提供的,而單純執行 npm install turndown 並不會包含它。沒有這個插件時,每個儲存格都只會以自己的段落形式保留下來——值還在,但列與欄的關係不見了。四個樣本加總後,這會變成 0 個 Markdown 表格列,且在相同內容下比 markdownify 多出 24.6% 的 token。
為什麼一開始看起來 html2text 沒有表格?
因為我的計數器要求有前後 pipe,但 html2text 不會輸出那種格式。Team Name | Year | Wins 是合法的 Markdown,而且能正常渲染;只是它和 | Team Name | Year | 這種風格不同。修正後的計數器會像 parser 一樣,去找一串包含 pipe 的行以及其中的分隔列,於是 html2text 就從 1 行變成 32 行。如果你自己用 regex 後處理 Markdown,這個差異會直接影響結果。
html2text 的 GPL 授權真的是問題嗎?
要看你如何組合與散布軟體而定。GPL-3.0-or-later 可能會帶來 MIT 不會有的義務,所以在把它用到會對外發布的產品前,務必先和負責授權的人確認。這是一個合規檢查點,不是法律意見;這個授權資訊已從 registry metadata、repository,以及已安裝套件的 METADATA 中確認。
這些 token 數對其他頁面也有參考價值嗎?
24.6% 的差距大多來自 turndown 把 <script> 和 <style> 內容保留下來,所以它會隨頁面含有多少這類內容而變動——在現代 CMS 頁面上通常很多,在靜態頁面上則接近零。表格則會把差距拉向另一邊:在那個全表格樣本裡,turndown 反而便宜 22%,因為它不輸出 pipe 骨架。四個工具的 bytes per token 都落在 3.61 到 3.65 之間,所以輸出密度大致一致,差別主要在數量。如果這個數字會影響預算,就用你自己的語料來量。
這裡沒有測到什麼?
真實世界的多樣性——四個樣本畢竟就是四個樣本。巢狀清單、定義清單、註腳與數學公式。格式不完整的 HTML,這一直是轉換器最容易分歧的地方。把 Markdown 再轉回 HTML 的往返測試。docling,它在磁碟上也有相同樣本,但公開的執行結果沒有回報這些欄位,因此這裡是缺席,不是估算。還有設定:html2text 是用 body_width=0 跑的,因為它預設的 78 會把每一行都硬換行,這樣會改變表格裡每一個字元與 token 的計數。


