HTML解析引擎只负责解析标签结构,不识别语义;提取导航菜单需结合aria-label、class等定义规则,并过滤不可见/禁用节点,动态内容需渲染后处理。

jsdom、Python 的 BeautifulSoup 或 lxml)只负责把 HTML 字符串转成 DOM 树或类似结构,它**不理解“导航菜单”是什么**——它只认得 nav、ul、li、a 这些标签,不会自动标记哪块是“主导航”、哪块是“页脚链接”。
你要的是「从 HTML 中识别并提取导航菜单结构」,关键在**你怎么定义“导航菜单”**,而不是解析引擎本身。
怎么用 jsdom 提取主流导航结构
如果你用 Node.js 做服务端预处理或自动化测试,jsdom 是最贴近浏览器行为的选择。但别直接查 document.querySelector('nav') 就完事:
-
nav元素可能有多个(主导航、页脚导航、侧边工具栏),需结合aria-label或 class 判断,比如document.querySelector('nav[aria-label="主导航"]') - 有些老项目根本没写
nav,只用div class="menu",这时得 fallback 到 CSS 选择器,例如document.querySelector('header .nav, .header-nav, #main-menu') -
ul下的li可能嵌套下拉菜单,要递归遍历li > ul,但注意:子ul必须是父li的直接子元素,否则语义断裂 - 跳过
aria-hidden="true"或display: none的节点——它们对用户不可见,也不该被当作有效导航项提取
为什么 BeautifulSoup 容易漏掉动态菜单
Python 侧常用 BeautifulSoup,但它默认不执行 JS。如果导航是 React/Vue 渲染的,或靠 fetch 加载后插入 DOM,原始 HTML 里根本没 nav 或 ul。
- 直接 parse HTML 源码,大概率拿到空容器或占位
div id="root" - 想完整提取,得配合无头浏览器(如
playwright或selenium)先渲染再抓取,开销大、启动慢、CI 环境难配 - 更轻量的做法:约定前端输出一个全局变量,比如
window.NAV_STRUCTURE = [{...}],后端直接eval或 JSON.parse 那段 script 文本(前提是可信源)
querySelector 查不到下拉菜单?检查 ARIA 属性是否匹配
很多“可访问下拉菜单”的 DOM 结构其实很规范,但提取逻辑写错就全白搭:
- 触发按钮必须有
aria-expanded和aria-controls,例如:<a href="#" aria-expanded="false" aria-controls="submenu-1">产品</a> - 对应子菜单
ul必须有匹配的id="submenu-1",且不能放在body底层——否则 JS 能定位,但解析器查aria-controls值时找不到目标节点 - 别依赖
role="menu":这个 role 在实际项目中极少被正确实现,浏览器也不强制校验;优先信nav > ul > li > a这个路径 - 禁用项要过滤掉:
a[aria-disabled="true"], a[tabindex="-1"]不该算进可点击菜单列表
nav 里,但不属于导航项。要不要剔除,取决于你的用途:做 SEO 分析?保留所有 a;做键盘导航模拟?只取可聚焦、有 href 或 role="menuitem" 的节点。这个边界,没人替你划。



















