三個步驟把 HTML 轉成 Markdown

重點摘要
  • 文字、標題、清單、連結、圖片與引用,都會完整活著出來。
  • class、行內樣式、id、表單與 iframe 不會,因為 Markdown 沒有這些詞彙。
  • 巢狀或合併儲存格的表格會保留成原始 HTML,這才是正確結果。
  • turndown.js 在你的分頁裡執行,標記不會離開你的電腦。

輸出框裡已經是你的 Markdown 了。接下來要做的是檢查有什麼沒跟上,因為這個方向一定會掉東西。這一頁就是告訴你會掉哪些。

回到純文字的三個步驟

  1. 把 HTML 貼進輸入框。來源可以是瀏覽器另存的網頁、從 CMS 編輯器複製的片段、匯出的客服文章、電子報範本,或只是一個你想改成管線語法的 <table>。從線上頁面抓的話,只複製你要的那個元素,不要連整頁框架一起帶進來。
  2. 在輸出框讀 Markdown。標題縮成 #、粗體變成 **、連結變成 [文字](網址),外層的 div 直接消失。花幾秒掃過一次,這正是你發現什麼被壓平的時刻。
  3. 複製或下載結果。「複製」把 Markdown 放進剪貼簿,可以用在 README、wiki、靜態網站文章或 Obsidian 筆記;「下載」則存成 .md 檔,可以直接 commit 進版本庫。

turndown.js 在做什麼

引擎是 turndown.js,許多編輯器裡「貼上為 Markdown」功能背後就是它。瀏覽器會先把你貼進來的標記建成 DOM,turndown 再走過這棵樹,把每個節點改寫成對應的 Markdown。

整個過程沒有任何東西送到伺服器。輸出沒問題之後,首頁的免費 Markdown 編輯器是很自然的下一站,可以邊看即時預覽邊整理內容。

插圖:密密麻麻的角括號 HTML 標籤被剝除,只留下輕盈好讀的純文字 Markdown

實例:HTML 進去,Markdown 出來

下面是一段很寫實的片段,連真實 CMS 會塞給你的外層 div 和行內樣式都一併保留:

<div class="post">
  <h2>Release Notes</h2>
  <p>We shipped <strong>dark mode</strong> and fixed the <em>export</em> bug.</p>
  <ul>
    <li>Faster preview</li>
    <li>Smaller <code>bundle.js</code></li>
  </ul>
  <p style="color:#888">See the <a href="https://example.com/changelog">changelog</a>.</p>
</div>

轉出來的 Markdown

十一行標記變成六行文字,而且不必開瀏覽器就讀得懂:

## Release Notes

We shipped **dark mode** and fixed the *export* bug.

- Faster preview
- Smaller `bundle.js`

See the [changelog](https://example.com/changelog).

悄悄不見的東西

內容完整保留,但有兩樣東西不見了,找找看:

  • <div class="post"> 消失了。turndown 會把容器拆開,只留下裡面的文字。
  • style="color:#888" 消失了。Markdown 沒有描述顏色的詞彙,那段灰字現在就是普通文字。

這不是錯誤,而是你降到較簡單格式時必須接受的交易。輸出全程依照 GitHub 風格 Markdown 的慣例:標題用 # 的 ATX 寫法、項目符號用 -、程式碼用圍籬區塊而不是四個空白縮排。

這些預設值讓結果貼進 README、GitLab wiki 或任何靜態網站產生器都不會出問題。對兩種格式的差異還不熟的話,Markdown 與 HTML 比較說明兩者各自的用途,什麼是 Markdown 則從頭介紹這個格式。

常見 HTML 標籤與對應的 Markdown 寫法

這是轉換器套用的對照規則。真正有價值的資訊在「說明」那一欄,因為它告訴你途中悄悄改變了什麼:

HTML 標籤Markdown 對應寫法說明
<h1><h6>#######標題層級完整保留
<p>後面接一個空行的文字classstyle 一律丟棄
<strong><b>**文字**兩個標籤合併成同一種語法
<em><i>*文字*兩者之間的語意差別會消失
<a href>[文字](網址)title 保得住,targetrel 保不住
<img>![替代文字](路徑)widthheightloading 都會被丟掉
<ul><li>- 項目巢狀層級靠縮排保留
<ol><li>1. 項目start 屬性會遺失,編號從 1 重新開始
<code>用反引號包住的文字內含反引號時會自動加長圍籬
<pre><code class="language-js">標註 js 的圍籬區塊只有語言寫在可辨識的 class 裡才抓得到
<blockquote>> 文字巢狀引用會變成 >>
<hr>---單純對應,不會損失任何資訊
<br>行尾兩個空白再換行在原始碼裡看不見,很容易被誤刪
<del><s>~~文字~~僅限 GFM,原始 Markdown 沒有刪除線
<table>管線符號表格只支援單純的格狀資料,第一列會變成表頭
<div><span><section>不留痕跡,只保留內容容器被拆開,屬性一併消失
<script><style><noscript>整段移除標籤與內容都不會留下
<iframe><form><input><video>保留成原始 HTML 或直接丟棄Markdown 根本沒有對應語法

有兩列值得看兩次

  • <br> 那一列。硬換行會變成行尾空白,而多數編輯器存檔時會自動清掉行尾空白,所以它是轉換後最脆弱的東西。
  • 容器那一列。它解釋了幾乎所有「我的版面跑去哪了」的疑問:版面靠容器和 class 撐起來,Markdown 兩樣都沒有。

語法速查表讓你一眼看完目標語法,完整 Markdown 指南則把每個元素講清楚。要轉的東西是格狀資料嗎?先讀表格指南,管線語法比看起來嚴格。

哪些東西轉不乾淨,以及為什麼

HTML 是比 Markdown 大得多的語言,所以這個方向本質上就會失真。清楚知道損失發生在哪裡,就能事先安排對策。

Markdown 沒有詞彙可用的東西

  • 巢狀與合併儲存格的表格。用了 colspanrowspan,或儲存格內含巢狀清單,管線語法完全無法表達。turndown 會把它壓成歪掉的格子,或乾脆把 <table> 原樣留成 HTML。
  • 行內樣式、class 與 id。style="color:red"class="callout"id="section-3" 全都會消失,因為 Markdown 表達的是結構而不是外觀。
  • 表單、iframe 與多媒體嵌入。<form><input><iframe><video><audio> 完全沒有對應語法,只能保留成原始 HTML 或直接丟棄。

複雜表格留成 HTML 是解法,不是失敗:Markdown 渲染器會把區塊層級的 HTML 原樣輸出,表格照樣正常顯示。如果 id="section-3" 這種錨點很重要,就在 Markdown 裡手動補一小段 <span id="section-3"></span>。另外,GitHub 渲染 README 時本來就會濾掉 <iframe>,所以影片嵌入即使轉換時活下來,發佈時還是可能消失。

會悄悄改變的東西

  • 依賴定位的版面。多欄格線、浮動元素、絕對定位的區塊,都會依原始碼順序壓成一條線性文字流,而原始碼順序常常不等於閱讀順序。
  • 看不見的字元。不斷行空白與零寬字元會安靜地留在檔案裡,幾週後製造莫名其妙的 diff。
  • 實體與跳脫。&nbsp; 會被解碼成真正的字元,而文字中原有的 *_# 會被加上反斜線,以免變成格式符號。

注意:版面型頁面轉換後,一定要從頭到尾讀過一次再發佈。在 Markdown 裡,原始碼順序就等於閱讀順序,而多欄版面正是兩者分道揚鑣的地方。

幫你省麻煩的習慣

轉換完之後,先在預覽裡完整讀過一遍再送出。把輸出貼進編輯器,和原始頁面對照,修掉那幾處跑掉的地方就好。

回頭路是 Markdown 轉 HTML 轉換器,而那個方向不會有任何損失。常見問題裡的如何把 Markdown 轉成 HTML 說明了為什麼兩個方向並不對稱。

這個工具、Pandoc,還是在 Node 跑 turndown?

能做這件事的工具有三種,適用情境其實差很多。選錯就是白白浪費一個下午。

這個頁面:一次一份文件

零安裝、立即出結果,在什麼都不准裝的公司筆電上也能用,標記從頭到尾不離開你的電腦。多數真實任務都屬於這一類:把文章從 CMS 裡救出來、把複製來的表格改成管線語法、把一封信整理成 wiki 頁面。

Pandoc:大批舊檔案與特殊格式

Pandoc 是命令列文件轉換器,支援的格式數量無人能及。一行指令就能完成:

pandoc -f html -t gfm page.html -o page.md

它處理定義清單、註腳和複雜表格比瀏覽器函式庫優雅,也讀得懂 DOCX、LaTeX、EPUB,所以當輸入根本不是 HTML 時,它才是正解。代價是要在本機安裝,而且正規化很有主見,可能把輸出重排成你不想要的樣子。搭配一行 shell 迴圈掃過資料夾,它就變成批次工具。

在 Node 跑 turndown:可重複的大規模搬遷

從舊 CMS 搬出幾百頁,或每次改版都要重新匯出的文件網站,答案就是它:

npm install turndown turndown-plugin-gfm

因為引擎和這個頁面完全相同,輸出會跟你在這裡預覽到的一致。走 Node 路線的真正理由是自訂規則:把每個 <div class="callout"> 轉成引用區塊,或清掉所有連結的追蹤參數。放進 CI 執行,一千頁每次都轉出一模一樣的結果。

小提醒:兩種併用最省力。先在這裡拿三份代表性頁面驗證效果,再把摸索出來的規則寫成 Node 腳本,套用到剩下的九百九十七頁。本來就只需要轉一頁的話,做完第一步就收工。

這一頁沒講到的問題,常見問題大多有答案;想確認轉好的檔案渲染是否符合預期,用 Markdown 檢視器打開最快。

相容性資料驗證日期

常見問題

HTML 轉 Markdown 會失真嗎?

會,而且無法避免。Markdown 沒有 class、行內樣式、id、表單、iframe 或合併儲存格的語法,這些只能被丟棄或保留成原始 HTML。文字、標題、清單、連結、圖片、引用和單純的表格則會完整保留。

我的 HTML 會離開瀏覽器嗎?

不會。turndown.js 以一般 JavaScript 在你的分頁內執行,轉換全在本機完成。不上傳、不記錄,所以內部頁面和客戶專案都可以放心使用,頁面載入後即使離線也照常運作。

為什麼我的表格轉出來還是原始 HTML?

因為它用了管線表格表達不出來的東西,通常是 colspanrowspan,或儲存格裡塞了巢狀清單。保留成 HTML 才是正確結果:Markdown 渲染器會把區塊 HTML 原樣輸出,表格照常顯示。管線語法的極限請見表格指南

可以一次轉換整個網站或很多檔案嗎?

這個瀏覽器工具不行,它一次處理一份文件。要批次處理,請用 Node 腳本跑 turndown,或用命令列的 Pandoc - 上面的比較段落有完整說明。

怎麼把 Markdown 再轉回 HTML?

用另一個工具 Markdown 轉 HTML 轉換器,或直接用編輯器的「複製 HTML」與 .html 按鈕。那個方向不會失真,因為 Markdown 本來就是 HTML 表達能力的子集。

延伸閱讀

立即使用編輯器

開啟編輯器