ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HTML转Markdown实战指南:从正文提取到格式还原的完整方案

HTML转Markdown实战指南:从正文提取到格式还原的完整方案 大概两个月前我帮一位朋友迁移博客他丢过来几十个从网页保存的 HTML 文件说“帮我把正文转成 Markdown 吧我要统一丢进笔记系统”。我当时还以为这是个 20 分钟就能搞完的体力活结果真正动手才发现HTML 转 Markdown 这件事远没有想象中那么简单。直接复制粘贴全是乱掉的样式用在线转换器又经常把导航栏、评论区、广告一起带进来整理完的“正文”比原网页还长。后来我把整个流程拆开从正文提取、标签清洗到格式还原、批量处理一步一步调才终于攒出一套能稳定复用的操作方案。这篇实战指南就是把这些经验和坑全部记录下来适合经常搞网页内容采集、博客迁移、知识库整理或者想在编辑器里把网页片断转成干净 Markdown 的人。1. 思路拆解HTML转Markdown到底难在哪1.1 直接复制粘贴为什么总是翻车很多人最初接触这个问题时都想着“从网页复制直接在 Markdown 编辑器里粘贴不就行了”。试过一次你就会发现几乎每个网站都在跟你作对。浏览器复制出来的是富文本内容里面夹杂着大量内联样式、div和span的嵌套结构。你粘到 Typora、Obsidian 或者 VS Code 里表面上看起来还能显示只要点开源码模式铺天盖地的style、font、class...就能把你淹没。更麻烦的是现在的网页很少是纯静态结构。为了布局方便很多正文被拆成了几十个小块分别包在div里中间穿插着图片、卡片、引用块、代码高亮组件。这种结构不是靠“复制粘贴”能解决的哪怕你用浏览器自带的“开发者工具”去选“Copy outerHTML”得到的也还是一坨带着大量无关标签的代码。直接用 Markdown 正则去清洗的话很容易把正文里的代码示例、标签字符也一起误删越洗越乱。我还见过一种更隐蔽的翻车方式有一些网站本身是前后端分离的正文是接口返回的 JSON 数据HTML 只是壳子。你抓下来的是整个页面骨架正文根本不在里面。如果你一直盯着“HTML 转 Markdown”这个问题很容易忽略掉“先确认正文到底在不在 HTML 里”这一步。1.2 正文整理的核心目标所以“HTML 转 Markdown 正文整理”这件事真正要处理的不是“把标签换一下”而是两层问题第一层是正文提取。把页面里真正属于文章主体的内容挑出来去掉导航、页脚、侧边栏、评论区、推荐阅读、广告位、脚本样式。市面上很多在线转换器不管这层它们只是把整个 HTML 全部转成 Markdown结果转换完以后标题、列表、代码块、表格全都挤在一起正文反而找不到。真正的整理流程应该先做正文提取再做格式转换顺序不能反。第二层是结构还原。HTML 转成 Markdown 后要确保几个关键信息不丢标题层级、段落分隔、列表嵌套、代码块语言标识、表格结构、图片链接、引用块、数学公式。Markdown 本身是一种“约定优先”的标记语言它对空行的要求非常严格。比如 HTML 里ptext/p天然是段落但转成 Markdown 以后如果你不保留段落之间的空行渲染出来就会挤成一段再比如table转成 Markdown 表格时如果单元格里有|字符或者换行表格就会瞬间崩掉。这些细节才是正文整理的真正难点。很多人一上来就问“有没有一个命令直接把 HTML 变成 Markdown”我的回答是转换命令确实存在但如果你不先想清楚上面两层问题无论用什么工具产出的内容依然没法直接用。把思路理顺之后我们再来看工具选型会轻松很多。2. 工具选型不同的场景不同的方案2.1 轻量转换html2text如果你要处理的 HTML 是已经提取好的正文片段比如只有几个p、h2、img那最简单的工具就是 Python 社区的老牌库html2text。它做的事情非常纯粹把 HTML 标签翻译成 Markdown 标记比如h1变成#b变成**a变成[text](url)img变成![alt](src)。使用方式很简单先安装pip install html2text然后写一个小脚本import html2text converter html2text.HTML2Text() converter.ignore_links False converter.body_width 0 # 不自动折行 markdown_text converter.handle(html_content) print(markdown_text)它的优点是轻量、无外部依赖、用来处理干净的 HTML 片段非常高效。但也有一个硬伤它不做正文提取。如果你把整个网页丢给它它会把导航链接、脚本里的文字、页脚版权信息全部转成 Markdown你得到的还是一堆噪音。所以我通常只把html2text用在“已经确定了正文边界”的场景比如从爬虫里拿到了明确的 article 容器。2.2 命令行万金油Pandoc如果说html2text是小刀那 Pandoc 就是瑞士军刀。它能把 HTML 转成 Markdown也能把 Markdown 转成 Word、PDF、EPUB、LaTeX几乎什么文档格式之间的转换都能做。最常用的一条命令是这样pandoc input.html -o output.md默认情况下Pandoc 会保留文档的标题结构、列表、表格、代码块并且会把 HTML 里的粗体、斜体、链接、图片都转换成对应的 Markdown 语法。它还能处理行内公式比如 LateX 语法的$...$会保留下来。Pandoc 最大的优势是转换质量非常稳尤其是表格和代码块。HTML 里的table到了 Pandoc 手里大部分能转成标准的 Markdown 表格单元格里的|它也会自动做转义处理。代码块里的precode classpython也能变成带语言标识的 fenced code block。不过 Pandoc 同样默认不做正文提取。它更适合你已经有干净 HTML 文件的场景。比如你已经用其他工具把正文从页面里抠出来了再用 Pandoc 做格式转换会很舒服。2.3 智能提取Trafilatura 和 Readability如果你遇到的是“从一个完整网页里直接提取正文”那就得用专门做正文提取的库。最省心的是trafilatura它是专门为爬虫、文本挖掘设计的能自动识别网页主体内容并且支持直接输出 Markdown 格式。安装命令pip install trafilatura基本用法import trafilatura downloaded trafilatura.fetch_url(https://example.com/article) result trafilatura.extract( downloaded, output_formatmarkdown, include_imagesTrue, include_linksTrue, with_metadataTrue ) print(result)这一个库就能干掉很多工作它会自动跳过导航和页脚识别文章标题、发布时间、作者还能把正文提取成干净的 Markdown。和它类似思路的还有 Mozilla 的readability不过readability输出的是 HTML你还需要再接一层转换工具。如果不想装 Python 生态也可以用 Node 版的mozilla/readability提取正文后再用 Pandoc 转 Markdown。这里有一个经验抓取网页时最好把原始 HTML 完整保存下来然后离线再提取。这样就算网站改版、断网原始数据还在你还可以换不同工具重新提。2.4 我给这些工具的排序与组合建议如果你问我具体怎么选我一般按这个思路判断场景推荐工具理由已经有一小段干净的 HTML 片段html2text轻量快速少依赖几行代码就能用有一个完整 HTML 文件正文结构比较标准Pandoc格式转换最稳表格代码块都不容易崩有一个完整网页需要自动抠正文再去掉导航trafilatura自带正文提取输出 Markdown一步到位技术栈是 Node.js想在前端或爬虫里处理mozilla/readability 转换工具生态熟悉提取效果也不错只是偶尔转一个网页不想写代码Pandoc 在线版 / 浏览器插件快速解决但要注意检查和去噪组合方案是我平时最常用的trafilatura提取正文 → 保存 Markdown如果碰到提取结果不满意就退回 Pandoc 单独转 HTML 片段。这样既兼顾了自动化又保留了人工干预的口子。工具本身没有绝对的好坏关键看你能不能把每类工具用在它最擅长的位置。3. 实操过程从杂乱HTML到干净Markdown正文3.1 准备运行环境这节我会把完整流程走一遍。假定你用的是 Windows 或 macOS装了 Python 3.8 以上版本。需要安装的库不多我在本地一般只装这两样pip install trafilatura html2text如果你还想用 Pandoc 做格式转换需要单独到 Pandoc 官网下载安装包或者用包管理器安装macOS 上可以brew install pandocWindows 上可以用winget install pandoc。装完以后在终端里输入pandoc --version验证一下能看到版本号就说明环境没问题。这里多说一句很多教程会让你一次性装一堆依赖实际上没必要。我们把任务拆成“正文提取”和“格式转换”两步每步只用一个核心工具出问题也好排查。如果真的遇到某种格式 Pandoc 不认再补装其他库也不迟。3.2 第一步提取正文我先用一个实际的例子演示。假设我有这样一个网页文件article.html里面混着导航、侧边栏和正文。用 trafilatura 提取正文并转成 Markdownimport trafilatura with open(article.html, r, encodingutf-8) as f: html_content f.read() result trafilatura.extract( html_content, output_formatmarkdown, include_imagesTrue, include_linksTrue, with_metadataTrue ) with open(article_output.md, w, encodingutf-8) as f: f.write(result)如果result是None说明 trafilatura 没识别到正文。这时候别慌可能是网页结构比较特殊或者是登录墙页面也有可能是正文被包在一个它觉得“非主体”的容器里。可以换用trafilatura.extract(html_content, output_formathtml, no_fallbackFalse)多试几个参数或者退回到 readability 这类“通用网页正文提取算法”。提取完成后打开article_output.md的第一眼可能会很惊喜因为导航和页脚基本都不见了只剩标题、段落、图片、列表。但别急着下结论你还需要检查两件事一是标题和正文之间是否隔了多余的空行二是如果正文里有代码块代码缩进是否被破坏了。这两个问题在自动化提取时非常常见。3.3 第二步HTML片段转Markdown如果 trafilatura 提取出来的结果不理想或者你手里的素材本来就是一个干净的 HTML 片段那就直接用 Pandoc 转换。假设我把正文片段存成了body.html想转成 Markdown 文件命令是这样pandoc body.html -o body.md --wrapnone这里的--wrapnone很关键。默认情况下 Pandoc 会在 72 个字符左右自动折行这对普通段落影响不大但如果你的 Markdown 编辑器是 Obsidian、Typora 这类的“所见即所得”工具就会看到段落中间被硬换行非常讨厌。设置成--wrapnone可以避免这个问题让每一段保持一行。如果 HTML 片段里有表格Pandoc 会尽量转成 Markdown 表格但有个前提表格结构不能太随意。嵌套表格、跨行跨列rowspan/colspan在 Markdown 里本来就不支持Pandoc 遇到这种情况可能会变成普通的 HTML 代码保留在 Markdown 里这个不算 bug是 Markdown 语法本身的限制。转换完之后我通常会用文本编辑器打开文件快速检查一下div、span这样的标签是否还残留。如果发现还有就进入下一步的清洗。3.4 第三步清洗残留标签和结构还原即使经过 PandocMarkdown 里偶尔还会出现零星的原生 HTML 标签。常见的有这么几种包裹图片或文字的figure、figcaption脚注相关的sup、sub视频、iframe 对应的iframe表格单元格里残留的span、br我处理这些残留标签一般分两步走。第一步用 BeautifulSoup 做结构化清洗而不是一上来就写正则。因为 HTML 标签是嵌套结构正则很难处理好“保留标签内容只去掉标签本身”这件事。用 BeautifulSoup 可以这样from bs4 import BeautifulSoup markdown_with_html open(body_output.md, encodingutf-8).read() soup BeautifulSoup(markdown_with_html, html.parser) for tag in soup([span, div]): tag.unwrap() # 去掉标签保留内部内容 for br in soup.find_all(br): br.replace_with(\n) cleaned str(soup)第二步是处理一些常见的格式隐患。比如 Markdown 表格里如果有竖线|必须转义成\|否则表格就会多出几列再比如代码块里的 HTML 标签如果不放在 fenced code block 里就会被 Markdown 渲染器误当成真实标签解析掉。我的习惯是先清洗 HTML再转 Markdown如果转出来的 Markdown 里还有 HTML就再做一轮针对性的字符串替换。3.5 第四步处理换行、表格、图片路径、数学公式这一步是正文整理的精华也是容易踩坑最多的地方。我单独拆开讲。换行。Markdown 的换行规则和 Word 不太一样。你敲一个回车渲染出来不一定换行要么在行尾加两个空格要么在两个段落之间留一个空行。HTML 转完 Markdown 后经常会出现“所有段落挤在一行”的情况原因是原始 HTML 里的文本节点没有在合适的位置拆行。解决方法是转换后统一把连续多个空行压成一个空行然后检查每个段落是否以空行分隔。Pandoc 加--wrapnone之后段落之间会保留空行但如果是html2text建议设置body_width0后再人工处理空行。表格。HTML 表格转成 Markdown 表格最大的问题是单元格里的内容可能出现换行、图片、列表。Markdown 表格不支持单元格内换行所以遇到这种结构要么把内容简化成纯文本要么保留为 HTML 表格。如果目标是放到 GitHub、语雀这类渲染环境我会优先保留 HTML 表格如果目标是放到 Obsidian、TyporaMarkdown 表格通常就够了。另外如果你需要把 Markdown 表格导出到 Excel可以先复制到支持 Markdown 的表格工具里再粘贴到 Excel比在 Excel 里手工排版靠谱得多。图片路径。网页里的图片路径通常是相对路径比如/images/a.png。如果把这个 URL 原样写进 Markdown图片可能无法显示。我的做法是在提取正文后批量把相对路径改成完整 URL或者把图片下载到本地再重新生成相对路径。下载图片后还要注意文件名冲突最好按文章目录命名比如images/post1_01.png。import re text markdown_text base_url https://example.com # 把 ![alt](/images/a.png) 替换成完整地址 text re.sub(r!\[([^\]]*)\]\((/[^)]*)\), rf![\1]({base_url}\2), text)数学公式。如果你的正文里包含数学公式html2text和 Pandoc 默认都能处理 LaTeX 形式的公式但要注意转义问题。HTML 里的span classmath\( ... \)/span经过 Pandoc 转换后通常会变成\\( ... \\)有时会多出反斜杠。我一般会在转换后把\\(和\\)替换成$把\\[和\\]替换成$$这样各种 Markdown 渲染器都能正确识别。3.6 第五步放到编辑器里做最终检查转换完成并不等于整理完成。我最后一定会把 Markdown 文件放到编辑器里过一遍因为不同的 Markdown 方言对语法的解析可能不一样。比如 GitHub 的 GFMGitHub Flavored Markdown和 Obsidian、Typora 的解析就有细微区别。检查的重点有三个标题层级是否连贯。有些网页的标题是从h3开始的转成 Markdown 后可能没有h1作为总标题我一般会手动补一个二级或一级标题方便以后在笔记系统里生成目录。代码块是否有语言标识。很多网页代码高亮是用precode classlanguage-python表示的转出来的 Markdown 如果丢了语言信息后续阅读就少了高亮提示。缺了的可以补上。链接和图片是否有失效。有些站点有防盗链图片直接放链接会被拦。检查的时候如果光靠肉眼看不出来建议用一个简单的脚本批量检查 URL 状态码。这一步做完整个转换才算真正结束。4. 常见问题排查与避坑手册4.1 高频问题速查表我把自己在实际操作里经常遇到的问题整理成了一张表碰到类似情况可以直接按表排查。现象原因解决办法转换后正文里全是导航链接没有先做正文提取直接转换整个页面先用 trafilatura/readability 提取主体再做格式转换段落挤在一行没有换行Markdown 需要空行才能分段转换器没有帮你加上转换后统一处理空行或使用--wrapnone后再手洗表格里多出一列或错位单元格内容里有未转义的 或换行图片全都不显示相对路径没有拼接完整 URL或图片被防盗链用正则批量补全 base URL或下载图片到本地并改路径代码块里出现大段 HTML 标签code内部没有被正确封闭把代码放到带语言标识的 fenced code block 中数学公式变成了一堆反斜杠Pandoc 会把\(转成\\(转换后统一替换为$/$$大文件转换过程卡死或者内存暴涨页面太大正文提取算法逐节点分析太慢先截取正文容器再提取或者用流式处理HTML 文件浏览器打开没问题但 Pandoc 不认文件编码不是 UTF-8或带有 BOM用脚本统一转成 UTF-8 无 BOM 格式4.2 我在实践中攒下的独家避坑技巧这部分是我最想说的因为都是网上文档里看不到的细节。第一不要在“整页 HTML”上直接跑格式转换。很多人拿着一个带完整导航、页脚的 HTML 文件就丢给 PandocPandoc 确实能转但转换结果会非常“酸爽”。导航里的每个链接都变成一条列表侧边栏的推荐文章变成一堆无序列表正文反而被淹没。我见过最夸张的一次一篇 3000 字的文章转换出来有 8000 多字大部分都是噪音。正确顺序永远是“先正文后转换”。第二不要轻易用正则去配对 HTML 标签。HTML 是树形结构嵌套很深正则适合做“去掉某个标签名”这种浅层操作一旦涉及跨标签处理非常容易误伤。比如你想去掉div标签直接匹配div和/div如果 div 里还套了 div就会把结构切断。用 BeautifulSoup 的unwrap()方法会更安全它会把标签剥离但保留标签内部的所有内容。第三清洗时注意保留代码内容。正文里如果讲的是编程技术内容中很可能出现if a b这样的字符。如果你用正则像[^]*这样去匹配标签会把 b也当作标签开头删掉彻底搞坏代码。所以清洗前一定要先把代码块提取出来用占位符替代清洗完再把代码放回去。具体的做法是先匹配 fenced code block替换成临时标记如PLACEHOLDER_CODE_BLOCK_1等标签清理完再恢复。第四下载图片时要防止文件名冲突。不同文章里经常出现同名图片比如image.png。如果都下载到同一个目录会互相覆盖。我的习惯是每篇文章建一个独立图片目录比如images/20240601-post-slug/并且给文件名加一个前缀或序号。这样既避免冲突后期打包迁移也方便。第五正文提取和格式转换最好分开执行。用 trafilatura 提取正文时它已经输出 Markdown 了但如果你对提取结果不满意可能需要调整参数重新提取这时候再跑一遍 Pandoc 会浪费时间。更好的办法是先用 trafilatura 输出 HTML 中间格式确认提取范围正确后再用 Pandoc 转 Markdown。这样两步可以分别调试效率更高。4.3 再聊几个绕不开的衍生场景弄明白了 HTML 转 Markdown 以后你大概率还会碰到“Markdown 怎么转成 Word/PDF”这种反向需求。我简单提一下几个常见操作免得你去踩我踩过的坑。Pandoc 可以把 Markdown 转成 Word 文档pandoc input.md -o output.docx但要注意Word 对于 Markdown 里的表格支持不稳定尤其当表格包含合并单元格或复杂布局时转出来的 Word 表格容易崩。我之前试过在低代码平台上搭工作流把 Markdown 转成 Word看起来挺方便但一遇到带公式的大文档就各种格式错位。所以如果你只是偶尔转几个文档本地用 Pandoc 完全够如果你想做自动化流水线一定得对格式差异有预期。至于 Markdown 转 PDF常见做法有两种一是用 VS Code 装 Markdown PDF 插件二是用 Pandoc LaTeX。如果你用 VS Code 的 Markdown PDF 插件需要提前下载 PrinceXML 之类的渲染引擎不然插件会提示缺少组件。另一个问题是中文 PDF 的字体会缺建议在配置里指定系统中文字体否则导出后中文会变成方块。这个坑我第一次遇到时也懵了很久。还有人会从 PDF 里转 Markdown想着“反向操作”。我能直接劝一句除非 PDF 里的文本是纯文本、没有复杂表格和数学公式否则自动转换的准确率非常低。像扫描 PDF、数学公式多的 PDF转出来基本是乱码加错位。更好的方案是先用 OCR 工具识别文本再做人工校对最后转成 Markdown。这些衍生场景本质上都属于“文档格式迁移”这件事理解了 HTML 转 Markdown 的核心逻辑以后其他格式互相转也就不难了。核心永远是“先提取、后转换、最后人工检查”。我自己现在的固定流程是trafilatura 提取正文 → Pandoc 转换为 Markdown → 编辑器里检查标题、表格、图片路径三处要点。这套流程我已经用了半年多处理过几百个网页文件除了一些结构特别诡异的网站需要单独调试大部分情况都能一次搞定。以我的亲身体会来说只要你不急着偷懒把每一步的输入输出都看清楚HTML 转 Markdown 其实没有想象中那么折磨人。最后再分享一个小习惯写转换脚本时尽量保存一份原始 HTML 的备份别直接覆盖原文件。有了原始数据就算工具抽风、脚本写错你也能随时重来。
返回列表