教程 · HTML → Markdown

HTML 转 Markdown

HTML 能表达的东西比 Markdown 多得多。所以这次转换主要在处理一个问题:那些没有对应写法的东西怎么办。

三种答案,看标签是哪个:映射过去、去掉标签留下文字、两样都去掉。哪种答案由一张白名单决定,值得知道上面有什么。

去转换器

白名单,不是黑名单

只有在 Markdown 里有意义的标签能过第一道:标题、段落、列表、链接、图片、强调、引用、代码、表格,以及围着它们的那几个行内标签。别的都删掉,文字留下。

用白名单的理由是:黑名单要预判所有危险标签,而 HTML 还在不断加新的。漏一个就是一个洞。这样一来,对任何不认识的东西默认答案是「不」。

脚本、事件属性、javascript: 链接都会没;script、style、iframe、object、embed 这几个连内容一起删 —— 不只是标签。留下 <script> 里的文字,等于把它的代码当成正文贴进你的文档。

属性同样是白名单:只有 href、src、alt、title、colspan、rowspan、start。所以 class、id、style 永远到不了输出。这不只是安全 —— Markdown 也没地方放它们。

什么都不渲染,为什么还要净化

这一页从来不渲染你的 HTML,所以这里没有什么能执行。净化器存在的理由是它之后会发生什么。

一个写成 [click me](javascript:alert(1)) 的链接,会被 Markdown 转换器原样忠实地带过去,等到有人把这份 Markdown 发到一个会渲染它的站上,它就是一个能用的攻击。风险不落在我们身上,是被交给了用这份输出的人。

所以 URL 会按协议白名单查一遍 —— http、https、mailto、ftp,还有相对路径 —— 别的一律丢掉。有东西被删掉时输出会说删了什么,而不是背着你悄悄把你的输入收拾干净。

表格:留着,还是压平

默认表格变成 Markdown 的竖线表格。格子里的竖线转义掉,格子内的空白塌成单个空格,短行补齐到最宽那行的宽度,表格保持是个矩形。

压平是另一个选项,它是为那些本来就不是表格的表格准备的。用表格做定位排版的页面转出来是一张全是空格子的竖线表格;压平之后每行变成一行文字,格子之间用一个中点连起来,读着好得多。

两样东西怎么都留不下。<caption> 会被丢掉,因为竖线表格没地方放它 —— 需要的话自己把它抄成表格上方的一行。还有格子里的块级内容会塌掉:一格里的列表出来是几项连在一起,因为竖线表格的一行必须是一行。

HTML
<table><tr><th>Part</th><th>Qty</th></tr>
<tr><td>Bolt | M6</td><td>12</td></tr></table>
Markdown
| Part | Qty |
| --- | --- |
| Bolt \| M6 | 12 |

保留成 HTML 的那几个标签

上标和下标保留成 <sup> 和 <sub>。Markdown 没有这两种语法,而把 x² 写成 x2 会改变一个公式的意思 —— 裸 HTML 在 Markdown 里是合法的,这两个标签每个渲染器都认。

下划线没有这个待遇。它没有需要保留的意义:在网页上下划线就是链接,留着它是在误导人。带下划线的文字出来是纯文字。

删除线转成 ~~,这是 GitHub 风格的 Markdown 而不是原始规范,但现在通用到扔掉它才是更奇怪的选择。

列表、代码块,和那几个开关

列表项写成「- item」,一个空格。大多数 Markdown 工具链都这么写,而另一种常见写法 —— 标记后面三个空格 —— 在一个文件被两边轮流编辑时会让 diff 很吵。

嵌套列表按标记的宽度缩进;列表项里接着写的段落缩进到跟上面文字对齐,而不是跳出列表。<ol> 上的 start 属性会保留编号。

项目符号字符可以是 -、* 或 +,代码围栏可以是 ``` 或 ~~~。按这份文件要进的地方挑,功能上没有区别。标题也可以换成下划线式的,不过只有前两级有这种写法 —— 第三级往下不管怎么选都还是 # 号,选之前值得知道这件事。

HTML
<ol><li><p>First para</p>
<p>Still item one</p></li></ol>
Markdown
1. First para

   Still item one

两种输入,一条路

你可以把 HTML 源码贴进框里,也可以拖一个 .html 文件进来。两者处理方式完全一样,因为在代码看来它们是同一个东西:一段不可信的 HTML 字符串。

25 MB 的上限是按单次输入算的,比任何页面的源码都大得多。什么都不上传 —— 解析、净化、转换全在这个标签页里。

结果回来是空的,输出会说明。通常是输入里全是标记没有文字:一个页面的 <head>,或者一段只有样式的片段。

贴源码或者拖文件,决定表格是不是还当表格,然后把 Markdown 拷走。全程在你的浏览器里跑。

HTML → MD