HTML页面大纲提取必须严格遵循W3C Outline Algorithm:跳级(如h2后接h4)、多h1、首屏无h1均导致大纲为空或扁平化;验证须用Chrome Accessibility Tree或htmlq等工具检查heading-level连续性,而非正则匹配标签数量。

直接提取 HTML 页面大纲,不能靠正则匹配 h1 到 h6 标签数量或顺序,必须按 W3C Outline Algorithm 规则遍历——跳级(如 h2 后接 h4)、多 h1、首屏无 h1 都会导致大纲为空或扁平化。服务端渲染缺失 h1 或动态组件中 h1 被条件隐藏,是生产环境最常被忽略的根源。
用 htmlq 命令行快速验证标题层级合法性
htmlq 是轻量、可靠、无需运行时依赖的 CLI 工具,适合 CI/CD 中做前置校验。它不解析语义,只按 DOM 顺序提取标签并输出结构,能暴露跳级、断裂等硬伤。
- 检查是否跳级:运行
curl -s https://yoursite.com | htmlq 'h1, h2, h3, h4, h5, h6' --text,观察输出是否严格递增或回退一级(h2 → h3 → h2合法,h2 → h4非法) - 确认首屏有
h1:加-r 'h1:first-of-type'过滤,若无输出说明 SSR 阶段未生成或被 CSS 隐藏 - 避免误判隐藏标题:不要加
--ignore-whitespace,否则可能漏掉display: none的h1—— 大纲提取器仍会处理它,但语义已断裂
用 jsoup 在 Java 后端提取结构化大纲数据
语雀、Confluence 等平台导出 HTML 后需服务端解析生成导航目录,jsoup 是稳妥选择。关键不是“找到所有 h*”,而是模拟浏览器 Outline Algorithm 构建嵌套树。
- 必须按 DOM 顺序遍历:用
document.body.children()深度优先遍历,而非getElementsByTag("h1")等分批取,否则顺序错乱 - 层级计算不能靠 tag 名字:
h3不一定等于 level 3,要根据前一个标题动态推导——当前为h2、上一个是h1,则 level = 2;当前为h4、上一个是h2,则非法,应降级为h3 - 跳级时别 crash:遇到
h1 → h4,建议 fallback 为h2并打 warning 日志,而不是抛异常中断流程
用 Cheerio + Node.js 提取并修复不合规标题
前端构建阶段(如 Vite 插件或 Next.js build hook)可用 cheerio 扫描并自动修正常见问题,比 runtime 修复更可控。
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
立即学习“前端免费学习笔记(深入)”;
- 修复缺失
h1:检测$('body').children().first().is('h1')为 false 时,在<body>开头注入<h1 aria-hidden="true">Page Title</h1>(仅用于大纲,不干扰视觉) - 限制
h5/h6使用:全局搜索h5和h6,若非文档页脚注或特殊场景,统一替换为h3—— 95% 的业务页面真不需要它们 - 防
props.level注入漏洞:模板中写<h${level}>时,必须在 JS 层做断言:const level = Math.min(6, Math.max(1, parseInt(levelStr) || 2)),否则可能输出<hNaN>
Chrome Accessibility Tree 是唯一可信验证方式
所有自动化脚本的输出,最终都要回归到机器怎么“读”它。DevTools 的 Accessibility 面板显示的 heading-level 属性,才是 axe、Lighthouse、屏幕阅读器实际依赖的依据。
- 别信
document.querySelectorAll('h1,h2,h3').length:它只数标签,不管嵌套与顺序 - 右键任意标题 → “Reveal in Accessibility Tree”,看右侧
heading-level是否连续、有无0或重复1 - VoiceOver(macOS)快捷键
Ctrl + Option + Cmd + H列出的标题菜单,就是最终用户拿到的大纲——如果这里跳层或空,说明前面所有提取都白做了
真正难的不是提取,而是让提取结果和浏览器实际构建的大纲一致。很多团队花时间写 parser,却没在 SSR 阶段确保首帧 HTML 包含有效 h1,也没约束 CMS 输出字段的数值范围。这些点漏掉,自动化脚本再准也产出废数据。









