MarkItDown 常常被歸到網頁爬蟲那一類,但這個分類其實不對。它沒有爬蟲、沒有 JavaScript 引擎,也不能抓取網址後再把頁面裡的雜訊內容清掉。它真正做的事,是把你手上已經有的檔案——像 PDF、Word 文件、試算表、簡報——轉成可供語言模型閱讀的 Markdown。
我花了幾週時間,在一台 Mac 上把 Microsoft 的 MarkItDown 放進一系列真實文件裡測試,逐一比對每個表格是否符合我事前寫好的清單,並且記錄每次轉換所花的時間。簡單來說:在乾淨的輸入下,它速度快、轉換也忠實;但套件包裝卻暗藏了一個你未必會想要的 73 MB 機器學習執行環境,而且它處理表格時會出現一些問題——那些錯誤乍看之下能通過「文字有沒有保住?」的檢查,卻會在「資料有沒有落在正確欄位?」這件事上失手。以下就是完整結果與數據。
MarkItDown 到底是什麼
MarkItDown 是 Microsoft 推出的 Python 工具,可把檔案與 Office 文件轉成適合 LLM 使用的 Markdown。你可以拿 PDF、.docx、.xlsx、.pptx、圖片、HTML 檔,或其他幾種格式丟給它,它就會回傳 Markdown。它提供三種使用方式:命令列介面(markitdown file.pdf -o out.md,或從 stdin 管道輸入)、Python API(MarkItDown().convert(...)),以及可選的 MCP 伺服器,方便 agent 工作流程使用。

最關鍵的區別在於它不做什麼。這不是 README 的誤導,而是我在測試中確認過的事實:它不會爬網、不會渲染 JS、不會順著連結往下抓、不會翻頁,也不會像 Readability 那樣抽取主內容。它做的是整份文件的轉換。你提供資料;它幫你標準化。這個差異會直接決定它適不適合放進你的技術堆疊裡,所以我後面還會反覆提到。
就 GitHub 的表面數字來看,這個 repo 算是重量級:截至 2026 年 7 月中旬,已有 165,282 顆星與 11,790 個 fork,採 MIT 授權,最新版本是 2026-05-26 發布的 v0.1.6。不過,這種星星數更多反映的是 Microsoft 旗下 repo 搭上 LLM 工具熱潮,不能直接等於轉換內部機制已經成熟。它目前還有 833 個 open issues,其中有幾個在你安裝之前就很值得知道(後面會說)。
HTML 轉 Markdown:快、完整,但也把版面雜訊一起帶進來
因為我這個爬蟲評測系列其餘部分也都使用同一組四個網頁樣本,所以我把相同的本地 HTML 檔餵給 MarkItDown——不是把它當爬蟲來打分,而是想看看它的 HTML 轉 Markdown 能力到底有多好。對於標記良好的頁面,它表現確實不錯。
四個頁面都能在核心安裝下直接轉換,不需要額外套件,而且所有正文內容都保留下來了。Wikipedia 的「Web scraping」條目(226 KB)輸出後,標題結構也完整對上了:一個 h1、七個 h2、十二個 h3,和原文的章節層級一致,並保留了 418 個連結,格式正確地寫成 [文字](url)。Scrape This Site forms 頁面 上 26×9 的冰球統計表,則被轉成一個乾淨的 27 列 GFM pipe table(表頭 + 分隔列 + 26 列資料),連空白儲存格都保住了。速度方面也沒什麼問題:小型 quotes 頁面中位數只要 48 ms,Wikipedia 那頁 226 KB 也只有 352 ms。
但這裡有個重點,而且這是設計選擇,不是 bug。MarkItDown 不會幫你清掉頁面模板雜訊。它會把整個 <body> 轉出去,所以網站的導覽列、頁尾、提示訊息都會一起混進結果裡;而這些殘留內容會隨著頁面有多少版面元素而增加。
| 頁面 | 輸出字元數 | 標題數量 (h1/h2/h3) | 連結數 | 網站版頭/版尾行數 |
|---|---|---|---|---|
| Books to Scrape | 10,478 | 1 / 0 / 0 | 94 | 0.6% (1/159) |
| Quotes to Scrape | 2,973 | 1 / 1 / 0 | 55 | 1.2% (1/86) |
| ScrapeThisSite forms | 3,385 | 1 / 0 / 0 | 31 | 6.7% (5/75) |
| Wikipedia Web scraping | 60,159 | 1 / 7 / 12 | 418 | 12.4% (42/338) |
在幾乎沒有版面雜訊的 Books 首頁上,輸出行只有 0.6% 是網站雜訊。到了 Wikipedia,就變成 12.4%——338 行非空白輸出裡,有 42 行是「跳至內容」、「切換目錄」、「22 種語言」、「摘錄自」、Cookie 與授權頁尾之類的內容。那些 Wikipedia 的維護橫幅(像是「這篇條目需要更多引用」)甚至會被忠實地轉成雙欄 pipe table,於是在沒有真正資料表的頁面上,憑空多出九列表格。
這不是 MarkItDown 做錯了什麼,而是它本來就不是摘要抽取器:忠實地把 HTML 轉成 Markdown,和把文章主體乾淨抽出來,是兩種不同工作。Trafilatura 或 Firecrawl 類工具的目標是只回傳主內容;MarkItDown 回傳的是整個頁面。從底層來看,它的 _html_converter.py 會先移除 <script> 和 <style>,接著把整個 body 交給 markdownify,流程中完全沒有主內容判斷。若你只想要文章正文,那這層工具就用錯了。
它真正擅長的主場:PDF、DOCX、XLSX、PPTX
文件才是 MarkItDown 的主戰場。我拿實際公開文件測試它:有可抽取文字層的 arXiv 論文、Bitcoin 白皮書、一份我自行渲染成沒有文字層的掃描 PDF,以及 MarkItDown 自家測試套件裡的 DOCX/XLSX/PPTX 檔案(我另外加了 UUID 標記,方便偵測是否有靜默遺失內容)。
| 文件 | 輸入大小 | 輸出字元數 | 檢查點 | 中位數時間 | 備註 |
|---|---|---|---|---|---|
| arXiv 1706.03762(含文字層 PDF) | 2.2 MB | 40,174 | 7/7 | 3.7 秒(warm) | title、"Transformer"、"BLEU"、"References" 都有 |
| Bitcoin 白皮書(9 頁 PDF) | 184 KB | 22,485 | 6/6 | 1.4 秒 | "Satoshi Nakamoto"、"proof-of-work"、"Conclusion" 都有 |
| 掃描 PDF(無文字層) | 89 KB | 0 | 0/4 | 15 ms | 輸出空白,無錯誤,無 OCR |
| DOCX(test.docx) | 136 KB | 4,651 | — | 70 ms | 標題 + GFM table;UUID 標記保留 |
| 含公式的 DOCX | 15 KB | 240 | — | 101 ms | Office Math 以 LaTeX 形式保留 |
| XLSX(test.xlsx) | 12 KB | 808 | — | 57 ms | 每個工作表 → ## SheetName + GFM table |
| PPTX(test.pptx) | 278 KB | 2,047 | — | 52 ms | 投影片編號標記、表格、圖表 → 表格 |
在含文字層的 PDF 上,文字召回率非常高——arXiv 的「Attention Is All You Need」論文預先設定的 7 個檢查點全過,Bitcoin 白皮書的 6 個也全過;而 Office 檔案沒有漏掉任何 UUID 標記,所以在維護者自家的回歸測試樣本裡,也沒有出現靜默內容損失。還有一個值得稱讚的窄幅優勢:DOCX 路徑(透過 mammoth)能把 Office Math 方程式保留成 LaTeX,將 equations.docx 轉成真正的 $$...$$ 數學區塊。如果你要把大量數學內容的 Word 文件餵給 LLM,這是一個很實際、但也很特定的優勢,我在其他地方也沒看到有人特別提到。
但這個主場有兩個發現特別值得注意,因為它們最容易踩到你。
會直接消失的掃描 PDF
如果丟給 MarkItDown 一份只有影像、沒有文字層的 PDF,它會回傳空字串。零字元、沒有例外、也沒有警告——因為沒東西可抽,所以大概 15 ms 就結束了。MarkItDown 的 PDF 路徑只做文字抽取(底層是 pdfminer 和 pdfplumber),核心安裝與任何 pip extra 都不包含 OCR。
這在批次處理時很重要。假設開發者處理一整個 PDF 資料夾,其中有些其實是掃描檔,那些檔案就會被靜默地變成空結果,而且完全不會提示你有東西被略過。我也特別確認過,不是測試檔壞掉:我直接用 pdfminer 的 extract_text 去抽,結果同樣是零字元、沒有文字層,證明這份樣本本來就是如此;因此,MarkItDown 在真實掃描 PDF 上輸出空白,確實是它本身的行為。這也重現了 upstream 長期存在的 OCR fallback 缺口 (#1268)。官方文檔建議的路徑是使用可選的 Azure Document Intelligence 後端或插件;這些都不在預設安裝裡。
PDF 會變成純文字,結構不見了
在這兩份含文字層的 PDF 裡,MarkItDown 產生的 Markdown 完全沒有任何標題標記。PDF 本來就沒有語意化的 heading 標籤,而 MarkItDown 也不會根據字體大小去推斷,所以每一行都只會以正文層級輸出。文字保留得很好;結構則是平的。
這不只是我的觀察。第三方公開基準測試顯示,MarkItDown 的 PDF 標題階層準確率大約是 0.0,表格保真度約 0.27,明顯低於 Docling 在 TableFormer 支援下約 0.88 的成績(可參考 MarkItDown vs Docling vs Marker 比較 與 READoc benchmark)。我的樣本結果和他們一致,這反而是證據的優點——我的數據與外部來源相符。同一批基準也指出,MarkItDown 的速度大約比 Docling 快 100 倍,這和我測得的秒級而非分鐘級時間也對得上,因為 layout-model 工具往往要花好幾分鐘處理同樣的文件。結論很簡單:MarkItDown 給你的是乾淨、快速的 PDF 文字;不是 PDF 的結構。如果標題和表格必須完整保留,那就應該用像 Docling 或 Marker 這類 layout-model 工具。
表格:內容通常能留,結構就不一定了
表格正是「文字有沒有保住?」和「資料能不能用?」開始分岔的地方。所以我設計了一個 13 個案例的矩陣——每個案例各自對應一個 <table>,並依照測試前寫好的清單打分——來精準找出哪些表格形狀能穩住,哪些會壞掉。

結論先講:MarkItDown 從來沒有丟掉任何表格內容。13 個案例裡,預先登記的 token 都 100% 保留了下來。不過,結構保真度則分成三種情況:13 個裡有 7 個輸出成格式正常的 GFM grid(普通表格、表頭 colspan、24 欄超寬表格、無表頭表格、空白儲存格、表格內區塊、以及從右到左的阿拉伯文表格);4 個變得歪斜,因為 Markdown 本來就沒有合併儲存格的概念,所以 rowspan、colspan 與來源格式不完整時,就會出現短列;另外 2 個則是明顯壞掉了。
這兩個壞掉的案例值得點名。一個是巢狀表格(<td> 裡再放 <table>),會被直接攤平成內文,把自己的 pipes 和分隔列一起塞進父儲存格,最後生成一列 14「欄」的垃圾資料。另一個是儲存格裡的字面 | 沒有被跳脫——像 a | b 會被拆成兩欄,x || y 會變成三欄——所以原本兩欄的表格,輸出列會變成兩欄、三欄、四欄混在一起,後續任何 Markdown parser 都會讀錯邊界。反而儲存格中的星號與反引號是有被跳脫的;只有 pipe 沒有。根本原因在於 MarkItDown 的 HTML 路徑使用了 markdownify 的預設表格處理,而它自訂的子類別有覆寫連結、圖片和標題,卻沒有覆寫儲存格。這類 pipe 跳脫 bug,在 CSV converter 也有一個 公開 issue (#2019),只是那個修正不會影響我這次實測的 HTML 路徑。
最微妙、也是我最希望資料工程師看到的,是 rowspan 的問題。t03 這個案例不只是格式歪掉而已,而是會悄悄把資料對錯欄。rowspan=2 的標籤("Fruit")只輸出一次,下面那列就變成一列只有兩欄的短列(| Banana | 8 |),因此「Banana」會跑到 Group 欄,而不是 Item 欄。每個 token 都還在,但如果下游系統只是粗暴地「讀第二欄」,拿到的就會是錯的值。這種 bug 會通過文字保留檢查,卻悄悄污染資料集。
這個 span 限制本身就是一個已知且被追蹤的設計約束(#1211、#1248):平面的 GFM pipe grid 本來就無法表達合併儲存格或巢狀結構,所以這個轉換器是用結構去換內容完整性。不過,裡面也有做得不錯的地方:沒有表頭的表格會自動補上一列空白表頭(因此不會把資料靜默升級成標題),空白儲存格會保留,而且 <caption> 也會以表格上方的一行文字形式保留下來。
安裝與啟動:這種「輕量工具」不會先提醒你的成本
這部分最讓我意外,也正是「輕量級 Python 工具」這個說法最容易過度承諾的地方。

第一,不要裝 pip install 'markitdown[all]'。在 Python 3.14 上,它會悄悄回溯到 markitdown 0.0.2——一個兩年前的版本——這點我在乾淨的 venv 裡實際重現過。為什麼會這樣,從 pin 就看得出來:pip install 'markitdown[all]==0.1.6' 會直接報錯,因為 [all] 這個 extra 會把 youtube-transcript-api~=1.0.0 鎖住,而在目前的 PyPI 上,那個版本區間裡的每個 build 都被限制在 Python <3.14,只有能支援 3.14 的 build 又不符合這個 pin。結果 resolver 只能一路往回找,直到找到最後一個能滿足依賴條件的版本。這也和一個 open upstream issue (#2179) 的描述一致。解法很直接:先固定版本,再分別安裝 extras——pip install 'markitdown==0.1.6',接著再裝 pip install 'markitdown[pdf,docx,pptx,xlsx,xls]==0.1.6'。這些單獨安裝都能正常解;只有合併的 [all] 套件包有這個有毒的 pin。(這個陷阱還會受 Python 版本影響——如果是 Python 3.13 或更早,限制可能不會踩到,[all] 的解析結果就可能不同。)
第二是體積。核心安裝就已經有 161 MB(13 MB 的空 venv + 148 MB 套件內容)。其中 onnxruntime 佔 73 MB、numpy 佔 34 MB,兩者合計 107 MB,等於整個核心體積的 66%,而它們都是由單一硬性依賴拉進來的:Google 的機器學習檔案類型偵測器 magika。也就是說,一個文字轉換器,在你還沒加任何文件 extras 之前,就已經自帶一個 73 MB 的 ONNX 推論執行環境。再把文件相關 extras 裝上去,整個 venv 會長到 310 MB。這比 headless browser 堆疊輕很多,但如果你原本以為它只是個「pip install 完就好」的小工具,那要知道它其實還會帶著 ONNX runtime 一起來。
第三點,也是我整套測試裡唯一一個通過所有新穎性檢查的發現:即使是乾淨安裝,import markitdown 在這台機器上也要花大約 3.35 秒。這個成本幾乎全都發生在 import 階段:markitdown._markitdown 會主動載入整個 converter registry(累計 2.56 秒,佔總時間的 76%),進而拉進 pandas(594 ms,透過 XLSX converter)、python-pptx(427 ms)、magika(354 ms)與 requests(270 ms)——不管你最後有沒有轉那些格式,這些都會先被載入。對長時間運行的服務來說,這個 import 成本可以攤平,影響不大;但對 CLI 一次性呼叫或 serverless 冷啟動來說,這就是實打實的每進程成本,而「輕量工具」這個說法完全看不出來。(公平地說:這只是單次 profile 的結果,我把它當作一個觀察點,而不是多次重複後的分布。)

規模測試:不會崩,但 PDF 要預算 CPU,試算表要預算 RAM
我把四個大型案例丟進去,每個都用獨立 process 跑,避免先前測試污染 peak memory。沒有任何一個崩潰,但成本分布很不平均。

| 主題 | 輸入大小 | 輸出字元數 | 中位數時間 | Peak RSS 增量 |
|---|---|---|---|---|
| NIST SP 800-53r5(492 頁 PDF) | 5.9 MB | 1,625,365 | 192.5 秒 | +40 MB |
| XLSX 50,000 列 × 8 欄 | 2.1 MB | 3,722,955 | 62.1 秒 | +374 MB |
| arXiv 1706.03762(約 15 頁 PDF) | 2.2 MB | 40,174 | 12.6 秒 | +25 MB |
| XLSX 200 列 × 64 欄 | 46 KB | 120,129 | 2.9 秒 | +22 MB |
492 頁的 NIST PDF 中位數花了 192.5 秒——大約 3.2 分鐘,也就是 0.39 秒/頁——因為 pdfplumber 會對每一頁做 word-position form-detection。Peak RSS 只增加了 40 MB,所以這是 CPU 綁定,不是記憶體綁定。就連那份 15 頁的 arXiv PDF,在獨立 process 裡也花了 12.6 秒,大約是同一份文件在我的文件套件中 warm 狀態下 3.7 秒的 3.4 倍。這個差距就是冷啟動成本,也證明每頁處理才是主因,而不是原始檔大小。如果你想替那份 PDF 找一個能移植的單一數字,就用獨立測試的 12.6 秒。
試算表路徑則完全相反。2.1 MB、50,000 列的 XLSX 膨脹到 +374 MB 的 peak RSS(以及 370 多萬個輸出字元),因為轉換器會把整張 sheet 一次載入,然後組成一個超大的 Markdown 字串。所以實務建議很直接:大型 PDF 要預留幾分鐘 CPU;大型試算表要預留數百 MB RAM。這些都是在 macOS arm64 與 Python 3.14 上單機測得的數字,頁面與列數的常數也會受平台影響——但「PDF 很慢而且吃 CPU、XLSX 很吃記憶體、而且都不會崩」這個趨勢是可移植的。
Thunderbit 適合放在哪裡——以及不適合放哪裡
這是最容易講過頭的一段,所以我會把界線畫清楚。MarkItDown 和 Thunderbit 解的是相鄰問題,不是同一個問題。
MarkItDown 轉的是你已經手上的檔案。Thunderbit 則是先抓取頁面。Thunderbit 的 /distill 端點 會把即時網頁轉成乾淨、適合 LLM 的 Markdown——它能處理 JS 渲染、反機器人機制與動態內容,這些 MarkItDown 完全沒有能力處理;而它的 /extract 端點則會回傳符合 schema 的結構化 JSON,而不只是原始 Markdown。對開發者來說,這些能力都可以透過同一個 AI 引擎對外提供:API(POST /distill / POST /extract)、MCP server,以及 CLI(npx @thunderbit/thunderbit-cli),也就是 10 萬+ 使用者擴充功能 背後的那套核心。
所以它們唯一重疊的地方,就是都可以輸出「適合 LLM 的 Markdown」;但輸入來源不一樣:Thunderbit 的 distill 是吃公開網路上的 URL,MarkItDown 則是吃本地檔案。它們不是可互相替代的工具,我也不會假裝它們是。實際可行的技術堆疊,通常會兩者都用:先用 Thunderbit(或類似 Firecrawl 的服務)抓網頁,再用 MarkItDown 把你手上的混合型本地文件——PDF、簡報、試算表——標準化。前者處理網路;後者處理檔案櫃。
優點與缺點
優點
- 在乾淨 HTML 上能完整保留正文內容(4/4 頁),標題樹與連結都能忠實輸出
- PDF / DOCX 文字召回率高(arXiv 7/7 檢查點、Bitcoin 6/6),且在維護者自家的 Office 樣本上沒有靜默漏內容
- Office Math 方程式能保留成 LaTeX——這點很少見,而且很實用
- 不管文件大小,從 492 頁 PDF 到 5 萬列 XLSX 都沒有崩潰
- 使用方式簡單:CLI、
convert()、stdin 管道,以及可選的 MCP server - MIT 授權、由 Microsoft 積極維護、issue tracker 回應也算快
缺點
- 會把版面雜訊一起留下來——Wikipedia 上最多有 12.4% 是網站雜訊;它不是文章抽取器
- 表格在 span、巢狀結構與儲存格內 pipe 上會出問題(13 個案例中 2 個壞掉、4 個歪掉),而且 rowspan 可能悄悄把資料對錯欄
- 掃描/純影像 PDF 會直接輸出空白,沒有 OCR,也不報錯
- PDF 輸出完全沒有標題結構(這也和公開 benchmark 一致)
- 核心安裝就有 161 MB,還帶著 73 MB 的 ONNX runtime;冷啟動 import 約 3.35 秒
[all]extra 在 Python 3.14 上會悄悄回溯到兩年前的 0.0.2
誰適合用,誰不適合用
如果你的工作是把一堆混合型本地文件——Word、Excel、PowerPoint、含文字層的 PDF——統一轉成 Markdown,餵進 LLM 流程,而且你更在意文字完整而不是結構保留,那 MarkItDown 很適合你。當它是批次工作的最後一哩,把乾淨文字送進模型時,它快、忠實,而且免費。
但如果你的任務符合以下任一種,就該跳過它,或搭配別的工具一起用:你只需要網頁的主文章內容(應該用 readability 或 Firecrawl 類工具);你需要 PDF 的標題與表格完整保留(那是 Docling 或 Marker 的地盤);或者你的輸入包含需要 OCR 的掃描文件(你需要 Azure 後端,或根本換別的工具)。如果你原本以為自己在找的是爬蟲——那種會抓取、會爬網的工具——那 MarkItDown 完全不是。
我用一套偏爬蟲的評分標準做了初步打分,MarkItDown 只有 60/100;這個分數低,主要是因為拿轉換器去考爬蟲的題目,本來就不公平。若只看它自己的主場,它的文字保真度其實很高;弱點主要在結構層面(表格、PDF 標題)以及包裝層面(體積、import、[all] 陷阱),不是文字品質。只要把它當作它真正的角色——檔案轉 Markdown 轉換器——它就是一個穩定、維護良好、但有幾個尖銳邊角需要先知道的工具,拿去接生產環境前值得先做好心理準備。
常見問題
MarkItDown 是網頁爬蟲嗎?
不是。它沒有爬蟲、沒有 JavaScript 渲染、不能順著連結抓取,也不支援翻頁。它把你已經擁有的檔案與文件——PDF、DOCX、XLSX、PPTX、圖片、HTML——轉成 Markdown。如果你需要抓取並爬行即時網頁,你應該用 Thunderbit 或 Firecrawl 這類爬蟲工具;MarkItDown 是後面的那一步,負責把抓下來的內容或本地檔案轉成乾淨 Markdown。
為什麼 pip install markitdown[all] 會安裝舊版本?
在 Python 3.14 上,[all] extra 會把 youtube-transcript-api~=1.0.0 鎖住,而那個版本範圍裡的每個 build 都限制在 Python 3.14 以下。resolver 無法滿足這個 pin,所以它會悄悄回溯到 markitdown 0.0.2,也就是兩年前的版本。解法是先固定版本,再分別安裝 extras:pip install 'markitdown==0.1.6',然後再加上 'markitdown[pdf,docx,pptx,xlsx,xls]==0.1.6'。這個問題已追蹤在 issue #2179。
MarkItDown 會對掃描 PDF 做 OCR 嗎?
預設安裝不會。它的 PDF 路徑只做文字抽取,所以沒有文字層的純影像 PDF 會直接回傳空字串——不報錯,也不警告。若要 OCR,必須使用可選的 Azure Document Intelligence 後端或插件,而這些都不是預設安裝的一部分。這是一個長期追蹤中的缺口(issue #1268)。
MarkItDown 處理表格的能力怎麼樣?
就內容來說,很好——我 13 個案例測下來,每一個都保住了 100% 的表格內容。就結構來說,得看表格長什麼樣:簡單表格、超寬表格、沒有表頭的表格、空白儲存格表格,都能乾淨地轉成 GFM grid;但 rowspan 和 colspan 會變得歪斜,而且 rowspan 還可能悄悄把資料對錯欄;巢狀表格會被攤平變成亂碼列;儲存格中的字面 pipe | 也不會被跳脫。Markdown 這種平面表格格式,本來就無法完整表示合併儲存格或巢狀結構。
MarkItDown 適合處理大型文件嗎?
它不會因為大檔案就崩潰,但你要按文件類型預留資源。492 頁 PDF 大約花 3.2 分鐘(約 0.39 秒/頁),因為它會逐頁做 form-detection,而且主要吃 CPU;50,000 列的試算表大約一分鐘就能完成,但會多吃 374 MB RAM,因為它會在記憶體裡組出一整個大型 Markdown 字串。大型 PDF 請預留幾分鐘 CPU;大型試算表請預留數百 MB RAM。
使用 Thunderbit 進行網頁資料擷取 Get Started Free


