后端【免费下载链接】defuddleGet the main content of any page as Markdown.项目地址https://gitcode.com/gh_mirrors/de/defuddle点击查看免费下载本文以仓库中的 general--back-nav-link.md 测试夹具为入口剖析 Defuddle 如何在提取网页正文时自动识别并移除页面顶部的返回上一级导航链接如← back、↑ index。读完本文你将理解该清理规则的判定条件、底层实现content-patterns.ts以及 fixtures 测试机制如何保证这类输出稳定可回归。从一个 fixture 案例看提取结果general--back-nav-link.md 是 Defuddle 对 general--back-nav-link.html 处理后的期望输出。其输入页面结构非常典型在h1之前有一个指向父目录的返回链接然后是文章正文!-- {url: https://example.com/articles/sorting} -- a href../index.html← back/a h1An Article About Sorting/h1 p classmetaJanuary 1, 2025/p pSorting algorithms are fundamental to computer science. .../p对应的期望输出即关联文档由两部分组成JSON 元数据前言以 json 代码块形式输出title、author、site、published四个字段便于人工对比与审查Markdown 正文只保留标题与段落正文页面顶部的a href../index.html← back/a已被完全清除也没有留下空行残骸。这个案例直观展示了 Defuddle 的两项核心能力结构化元数据提取与正文去噪。其中返回导航链接被移除正是由下文介绍的removeByContentPattern中专门的分支逻辑完成的。返回导航链接的识别规则清理逻辑位于 content-patterns.ts 的Remove section breadcrumbs and back-navigation links段落。其目标元素是短文本的div, span, p及独立的裸a元素匹配前提是链接指向当前 URL 的父路径共有两种模式直接路径前缀链接路径是当前 URL 的路径前缀例如当前为/blog/2024/post链接为/blog/或/blog父目录索引页链接指向父目录下的index.html/index.php例如当前为/articles/hensels链接为../index.html解析后等价于/index.html。在 fixture 案例中页面 URL 为https://example.com/articles/sorting链接../index.html解析后路径为/index.html满足isParentIndex判定/^index\.(html?|php)$/i且urlPath.startsWith(linkDir)因此被移除。代码通过以下条件约束避免误伤正文内的正常链接字数上限元素文本countWords后不得超过 10 个词words 10直接跳过叶子元素元素内部不得再包含p, div, section, article等块级子元素裸a特判若父元素文本与链接文本完全一致说明该链接是独立元素否则视为流动正文中的内联链接而跳过除非满足两个例外——链接不在p内或位于首个标题h1/h2/h3之前。最后一个例外正是为 fixture 这种页眉返回链接场景设计的注释明确写道出现在首个标题之前的嵌入a元素属于页面头部的返回导航链接而非正文内联链接content-patterns.ts。文档顺序判断isNodeBefore位于首个标题之前的判定依赖 src/utils/dom.ts 中的isNodeBefore工具函数。它不依赖 linkedom 跨父节点的compareDocumentPosition该实现可能不一致而是通过祖先链比对 兄弟节点遍历自行推导文档顺序分别收集两个节点到根的祖先路径若根不一致处于分离子树则返回false从根向下弹出公共祖先直到分叉点从分叉点的第一个节点开始沿nextSibling向后查找若能遇到第二个节点则说明前者在前。这样无论底层 DOM 实现是 linkedom 还是 jsdom参见 helpers.ts 的parseDocument切换removeByContentPattern都能获得稳定的顺序判断结果。与相邻清理模式的配合返回链接清理并不是孤立逻辑它处在removeByContentPattern的前置内容清理链条中同一函数还包含面包屑列表移除内容顶部首个ul/ol若被isBreadcrumbList判定为导航列表连同单子包装容器一并删除content-patterns.ts促销横幅链接首个h1之前、含块级子元素div、文本较短的a会被当作公告横幅移除L379-L394hero 头部块包裹日期、作者、标签与 hero 图的容器在元数据剥离后被整体清理。这些规则共享一个内容起点锚点findContentStartcontent-boundary.ts它先定位与页面标题匹配的h1/h2再沿文档序找到第一个散文长度的块级元素作为正文从哪里开始的权威参考替代了早期按字节偏移如contentText.indexOf(text) 300的脆弱做法。fixture 测试如何保证输出稳定general--back-nav-link.html 属于tests/fixtures/下的自动化夹具体系由 fixtures.test.ts 驱动扫描tests/fixtures/下所有.html文件从文件头部!-- {url: ...} --注释读取测试 URL调用Defuddle(doc, url, { separateMarkdown: true, fetch: offlineFetch })提取内容——offlineFetch强制禁用网络保证结果确定性将title/author/site/published序列化为 JSON 前言与 Markdown 正文合并成期望文件createComparableResult与tests/expected/中已保存的期望结果比对不一致即测试失败。若期望文件不存在测试会自动创建基线saveExpectedResult更新期望值的方法是删除对应期望文件后重新运行npm test。因此当有人误改了返回链接清理规则导致输出变化时fixture 对比会立刻报警防止回归。在项目外复现该行为你可以在任何 Node.js 项目中按如下方式复现仓库源码位于 src/node.ts 的Defuddle入口import { Defuddle } from defuddle; import { parseLinkedomHTML } from defuddle/utils/linkedom-compat; // 或使用 jsdom const html !DOCTYPE htmlhtmlheadtitleAn Article About Sorting/title/head bodya href../index.html← back/a h1An Article About Sorting/h1 pSorting algorithms are fundamental.../p/body/html; const doc parseLinkedomHTML(html, https://example.com/articles/sorting); const result await Defuddle(doc, https://example.com/articles/sorting, { separateMarkdown: true }); console.log(result.contentMarkdown); // 不含 ← back console.log(result.published); // 由 p classmeta 等日期文本推断注意两点限制一是URL 必须真实传入——urlPath为空时整段返回链接清理不会执行这正是 fixture 头部注释携带url的原因二是输出中的published字段来自 metadata.ts 的getPublished优先取 Schema.org 的datePublished、publishDate、article:published_time等 meta 标签再扫描h1前后三个兄弟元素中的p/time日期文本本案例的2025-01-01即来自 byline 文本January 1, 2025。小结从 general--back-nav-link.md 这一个期望文件出发可以看到 Defuddle 处理返回导航链接的完整链路removeByContentPattern依据 URL 路径前缀/父目录索引页两种模式识别链接用字数、叶子结构、标题位置与isNodeBefore文档序判定约束边界最后通过 fixtures 自动化测试将行为固化为可回归的期望输出。若你的站点在正文头部有返回列表类链接这一规则会默认生效而若某些内联链接被误删调整 content-patterns.ts 中的匹配条件后更新对应 fixture 期望即可安全迭代。赞分享后端【免费下载链接】defuddleGet the main content of any page as Markdown.项目地址https://gitcode.com/gh_mirrors/de/defuddle点击查看免费下载相关推荐defuddle 如何处理无效的 codepre 嵌套fixture 用例与两阶段源码实现解析defuddle 如何处理无效的 code pre 嵌套fixture 用例与两阶段源码实现解析 本篇技术指南以 codeblocks code pre后端BuildAdmin内置WEB终端深度探索无需手动npm install的智能开发体验BuildAdmin内置WEB终端深度探索无需手动npm install的智能开发体验 BuildAdmin是一款集颜值、实用、敏捷于一身的管理系统技术栈为后端前端低代码认证鉴权企业应用void 仓库 Markdown 相对链接解析实战链接格式、锚点导航与源码级实现void 仓库 Markdown 相对链接解析实战链接格式、锚点导航与源码级实现 导读 本文以 void 仓库内 markdown language feat代码编辑器开发工具AI Agent人工智能上一篇为什么选择esp32-snippets深度解析这个ESP32开发者的秘密武器下一篇CANN/asc-devkit矢量乘法API创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考