想把 Newsletter 内容存进笔记、把供应商发来的变更通知搬进技术文档、或者把邮件往来喂给 LLM 当上下文——理由不管是什么,一打开 HTML 邮件的源码就会遇到比普通网页脏得多的标记:层层嵌套的布局表格、每个元素都挂着 style 属性、看不见的追踪像素图混在正文里。这篇文章用实测数据说明"从哪个位置复制"会让同一封邮件的转换结果完全不同,然后带你看 Gmail、Outlook、.eml 文件各自的提取方式,以及转换后的清理步骤。
先想清楚要保留什么 — 这决定了路径
决定路径的不是工具,而是你要把邮件里的什么留下来。
- 只保留正文文本,对象是手头打开的几封邮件 — 浏览器内转换最快。复制正文块的 HTML,粘贴到 HTML to Markdown 即可。免费、无需注册,粘贴的内容不会被发送到任何外部服务器
- 需要保留发件人、主题、日期、引用回复链、附件列表,或者有几十封 .eml 文件 — 适合用能直接解析 .eml 的 CLI 工具(后文的 eml2md 系列)
- 想搭一条定期拉取邮件并加工的自动化管道 — 用 Python 的
email模块提取指定 MIME 部分再转换
两个前提先说清楚。第一,浏览器转换工具处理的是"提取出来的 HTML 文本",不是 .eml 文件本身。.eml 是 MIME 多部分容器,不是纯 HTML 文档,直接粘贴无法转换。第二,大多数邮件使用 multipart/alternative 格式,同时包含 HTML 版和 text/plain 版。如果纯文本够用,可能根本不需要转换。如果确实要转给 LLM 当上下文,输入用 Markdown 还是 HTML 哪种更合适,有实测对比(LLM 输入用 Markdown 还是 HTML)。
实测 — 复制范围不同,结果天差地别
我们用一封模拟典型 Newsletter 结构的样例邮件(手工构造的 fixture,非真实发送统计)跑了一遍与 FormatArc 转换工具完全相同配置的 Turndown 7.2.4。测量日期 2026-07-17,复现脚本、样例和原始数据在仓库的 scripts/benchmarks/html-email-to-markdown/ 目录下。以下所有数字都来自这次实测。
粘贴全文源码会塌成 12 列表格
样例邮件的完整源码是 6,491 字节。其中包含 8 个嵌套的 <table> 元素(全部用于布局)、33 个 style 属性、3 个 MSO 条件注释、1 个 1x1 的追踪像素、4 个点击追踪重定向链接。
直接转换,输出会塌成一张 12 列的管道表格(含分隔行共 10 行)。所有嵌套表格的行被压平到同一张表里,正文的同一段话在多个单元格里重复出现——作为表格没法用,作为文章也没法读。更糟的是,<head> 里的 <title> 文本和 <style> 块中的 380 字节 CSS 会作为正文泄漏到输出最前面。
这种表格塌陷是因为转换规则把表格的所有行合并成一张管道表。对正常的数据表没问题,但对布局用途的嵌套表格是灾难。不管你用什么工具,把包含布局表格的全文源码直接丢进去转换,结果都不会好。
只取正文块就是 477 字干净的 Markdown
同一封邮件,只提取正文区域包裹元素的 HTML(1,086 字节)来转换,结果完全不同。输出是 477 字的干净 Markdown:标题变成 #、段落保持段落、链接保持链接。8 个 style 属性全部被移除。style 属性、<span> 这类装饰性标记的去除,可参考 HTML 粘贴 Markdown 化 里针对网页、Word 粘贴内容的处理。
但点击追踪的重定向 URL 会原样保留为链接目标。这属于后处理范畴(下文会讲)。核心结论很简单:不要粘贴全文,只复制正文块。
邮件 HTML 为什么这么脏
邮件 HTML 和普通网页 HTML 在结构上不同,有具体原因。
- 布局全靠嵌套表格,因为邮件客户端的 CSS 支持非常有限。Windows 版经典 Outlook 使用 Microsoft Word 的 HTML 渲染引擎来显示邮件,Microsoft 官方文档有明确说明在新标签页中打开,该文档的不支持列表里列出了
float、position、max-width。没有表格就没别的布局手段 <!--[if mso]>开头的 MSO 条件注释是专门给 Outlook 看另一套标记的机制。转换时被当作 HTML 注释处理,输出中自动消失- 很多邮件客户端忽略外部样式表或
<style>标签,所以所有视觉样式都直接内联在每个元素的 style 属性上 - 打开追踪用的 1x1 透明图片和点击追踪用的重定向链接(
https://click.示例.com/track?dest=...这种形式)直接混在正文标记里
从邮件客户端提取 HTML 正文
转换质量取决于你如何提取正文 HTML。按客户端分别看。
最可靠的方法:用开发者工具复制正文块
Gmail、网易邮箱、QQ 邮箱、Outlook 网页版——所有网页邮件都能用。这就是实测中"只取正文块"对应的操作。
- 在浏览器中打开邮件,在正文文本上右键,选择"检查"(Inspect)
- 在开发者工具(Elements 面板)中,把选区往上移一到两级,直到覆盖包裹整个正文的父容器元素(包含所有段落、标题、图片)
- 右键该元素,选择"复制" > "Copy outerHTML"
这样剪贴板里就只有解码后的纯正文 HTML,不含邮箱界面和头部区域。
"显示原始邮件"的坑 — quoted-printable 编码
Gmail 的"显示原始邮件"或 Thunderbird 的源码视图中显示的是完整 MIME 消息。其中的 HTML 部分通常使用 Content-Transfer-Encoding: quoted-printable 编码,直接复制会得到损坏的 HTML:
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
<td style=3D"padding:24px 32px 12px 32px; font-family:Arial, Helvetica, san=
s-serif;">
这里 =3D 是 = 的编码表示,行末单独的 = 是软换行(解码时会与下一行拼接)。RFC 2045 第 6.7 节在新标签页中打开 定义的 quoted-printable 规则不止这一条,在编辑器里简单替换 =3D 无法正确还原。解码应该在 MIME 部分级别进行,所以与其从原始源码里抠,不如用开发者工具的方法,或者走下面 CLI / Python 路线。
Outlook、Apple Mail、Thunderbird
- Windows 版经典 Outlook:在消息正文上右键或通过菜单打开源码查看器,会显示已解码的 HTML。从这里复制就不存在 quoted-printable 的问题
- Apple Mail 和 Thunderbird:可以把消息拖出或用"另存为"导出为 .eml 文件。.eml 是 MIME 容器,与其在文本编辑器里找 HTML 部分,不如直接交给 CLI 工具处理,更快更准确
用 FormatArc 转换 — 浏览器内处理,不上传
拿到正文块的 HTML 后,转换只需三步:
- 打开 HTML to Markdown 转换工具
- 把复制的 HTML 粘贴到左侧输入区
- 点击转换按钮,右侧立即输出 Markdown


所有转换都在浏览器内的 JavaScript 引擎中完成,粘贴的文本和邮件数据不会发送到任何外部服务器。当你处理的邮件涉及客户信息、内部通知或敏感内容时,这就是它与上传型在线转换网站的核心区别。判断一个在线转换工具是否值得粘贴敏感数据,可以参考 在线工具安全检测 里粘贴前的 5 项验证。
转换后的清理检查清单
即使只转换了正文块,邮件特有的残留物仍会出现在 Markdown 里。保存或分享前检查以下 5 点:
- 还原追踪链接:
https://click.示例.com/track?dest=https%3A%2F%2F...这类重定向 URL,对dest=参数做 URL 解码还原真实目标,或者直接删掉链接。不要在浏览器中批量打开这些 URL 来"验证",那会触发打开和点击追踪计数 - 删除追踪像素:alt 为空的 1x1 图片引用(如
)直接删除 - 处理
cid:图片:正文中可能出现这样的引用,指向 MIMEmultipart/related部分内嵌的图片,在邮件客户端外无法显示。需要图片的话从 .eml 中单独提取并替换 - 清理
造成的多余空白和空行 - 决定保留程度:发件人签名、法律声明、退订链接、引用回复链——留多少你自己定
批量转换 .eml — CLI 与 Python
有几十封 .eml 文件需要处理,或者想连发件人、收件人、日期等头部信息一起保留,就需要能正确解析 MIME 的工具。
CLI 方面,pip 安装的 eml-to-md在新标签页中打开 能输出带头部信息的 Markdown,适合"只给 AI 代理需要的邮件上下文,不给它全邮箱权限"的场景。还有 Rust 写的 eml2md在新标签页中打开。
pip install eml-to-md
eml2md message.eml # 转换单封
eml2md *.eml -o converted/ # 批量转换整个文件夹
如果要用 Python 搭自动化管道,标准库的 email 模块在新标签页中打开 负责提取 MIME 部分,markdownify在新标签页中打开 负责转换。关键细节:以二进制模式打开文件、传入 policy.default、用 get_body() 选择部分。get_content() 会自动处理 quoted-printable 和 base64 解码。
import email
from email import policy
from markdownify import markdownify
with open("message.eml", "rb") as f:
msg = email.message_from_binary_file(f, policy=policy.default)
body = msg.get_body(preferencelist=("html", "plain"))
print(markdownify(body.get_content()))
也可以把提取的 HTML 通过管道交给 pandoc在新标签页中打开 处理,或者手动提取后只把转换步骤交给 CLI(cat body.html | formatarc html-to-markdown)。这里的 formatarc 命令来自 npm 包 formatarc,无需安装也能用 npx 直接运行。
方法对比
| 转换方式 | 适用场景 | 所需环境 | 头部/线程信息 | 数据安全性 |
|---|---|---|---|---|
| HTML to Markdown(浏览器) | 手头几封邮件的正文 | 浏览器(免费、无需安装) | 仅正文 | 浏览器内处理,不外传 |
| eml2md 系列 CLI | 几十封 .eml 批量转换 | pip / cargo | 含头部输出 | 本地处理 |
| Python(email + markdownify) | 定期自动化管道 | Python 环境 | 完全可控 | 本地处理 |
| 上传型在线转换工具 | 没有其他选择时 | 浏览器 | 因工具而异 | 数据发送到外部服务器 |
常见问题
Gmail 里在哪里看邮件的 HTML 源码?
邮件右上角菜单选"显示原始邮件"可以看到完整 MIME 消息。但里面的 HTML 通常是 quoted-printable 编码的,直接复制会得到损坏的标签。如果只需要正文,在正文上右键"检查"然后复制正文容器的 outerHTML 更可靠。
邮件布局会被转成 Markdown 表格吗?
不会正常保留。布局用的嵌套 <table> 会被压平塌掉(实测 6,491 字节全文源码变成 12 列表格)。解决办法是只复制正文块。但邮件里真正的数据表(比如价格表、订单明细)会正确转为管道表格。HTML 表格怎么转成管道表格,见 HTML 转 Markdown 指南。
邮件里的图片怎么转?
src 指向普通网络 URL 的图片会转为  格式的 Markdown 图片。cid: 开头的引用是附件内部引用,在邮件外无法显示。1x1 的追踪像素建议转换后手动删除。
敏感邮件贴到在线转换工具安全吗?
上传型的工具在你粘贴的瞬间数据就离开了你的机器。FormatArc 的转换完全在浏览器内完成,不存在传输环节。涉及客户信息或内部文档的邮件用这种处理方式是安全的。
.eml 文件能直接粘贴转换吗?
不能。.eml 是带头部和编码部分的 MIME 容器,不是 HTML 文件。单封邮件用开发者工具复制正文块,多封用 eml2md 系列 CLI。
总结
HTML 邮件转 Markdown,关键在"从哪里复制"。粘贴全文源码,嵌套布局表格会塌成 12 列表格、CSS 也会泄漏到正文里;只复制正文块,就是 477 字干净的 Markdown(实测数据:scripts/benchmarks/html-email-to-markdown/)。手头几封用开发者工具提取正文块配合 HTML to Markdown,批量 .eml 用 CLI,定期管道用 Python。无论哪条路,把追踪链接和像素的清理纳入转换流程,别留到事后。