尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从 HTML 乱码到精美报表,Dify 工作流里的数据清洗术

从 HTML 乱码到精美报表,Dify 工作流里的数据清洗术 为什么不能直接把 HTML 塞给大模型TL;DR 速览HTML 不能直接喂给大模型噪声多、Token 贵、结构不稳定必须先清洗。Code 节点做确定性清洗用正则提取关键字段转成 Markdown 表格再交给 LLM。前端用 marked DOMPurify安全解析 Markdown防 XSS 注入渲染精美报表。核心范式是职责分离采集、清洗、推理、展现各司其职数据干净 AI 输出才可靠。很多刚开始尝试 AI 工程化的后端工程师最容易踩的一个坑就是认为“只要把数据丢给模型它就能自己看懂”。在构建 GitHub 热榜解读器这类应用时这个误区尤为致命——模型不是搜索引擎它不会替你在乱码里“找数据”。当我们通过 HTTP 请求获取 GitHub Trending 页面时拿到的是标准的 HTML 文档。这份文档里包含了大量对模型毫无价值甚至有害的信息导航栏、脚本标签、CSS 类名、广告位、页脚链接以及无数嵌套的div容器。直接把这几万字符的原始 HTML 丢给 LLM后果是双重的一方面迅速烧掉昂贵的 Token 配额另一方面噪声过多会让模型“注意力分散”生成的报告往往充斥着幻觉或者干脆忽略关键数据。更严重的是HTML 的结构是不稳定的。GitHub 前端团队随时可能调整 class 名称或 DOM 层级依赖模型去“猜测”哪个div里藏着 Star 数是极不可靠的。真正的工程化方案必须在数据进入模型之前完成一次确定性的清洗与结构化——把非结构化的网页源码转化为模型最擅长处理的结构化文本如 Markdown 表格或 JSON让模型只负责“推理”和“生成观点”而不是负责“找数据”。这就是我们在 Dify 工作流中引入Code 节点的核心原因它充当了数据管道中的过滤器确保流入 LLM 的每一滴数据都是纯净且高价值的。Dify 工作流中的 Code 节点清洗实战在 Dify 的 Chatflow 编排中我们设计了一个专门的数据清洗环节当 HTTP 请求节点从 GitHub 抓取到 Trending 页面的 HTML 内容后数据会立即流入一个 Python 代码节点。这个节点的任务非常明确——解析 HTML提取关键字段并组装成紧凑的 Markdown 格式。正则提取的核心逻辑面对复杂的 HTML 文档使用重型解析库如 BeautifulSoup在某些 Serverless 或受限运行环境中可能显得笨重而精心设计的正则表达式往往能更高效地定位目标。我们的清洗逻辑主要聚焦于提取每个项目卡片article标签中的核心元数据。首先我们需要定位到包含项目信息的卡片容器。GitHub Trending 列表中每个项目都被包裹在一个article标签内。我们可以先通过正则匹配出所有的文章块importreimporthtmldefclean_github_trending(html_content:str)-str:# 匹配所有 article 标签内容re.DOTALL 确保跨行匹配articlesre.findall(rarticle\b[\s\S]*?/article,html_content,re.IGNORECASE)ifnotarticles:return未检测到有效的项目列表可能页面结构已变更。projects[]forarticleinarticles[:10]:# 仅处理前 10 个热门项目控制上下文长度# 1. 提取仓库所有者和名称# 匹配 href/owner/repo 模式repo_matchre.search(rhref/([^/])/([^/]),article,re.IGNORECASE)ifnotrepo_match:continueownerhtml.unescape(repo_match.group(1)).strip()repo_namehtml.unescape(repo_match.group(2)).strip()full_namef{owner}/{repo_name}repo_urlfhttps://github.com/{full_name}# 2. 提取项目描述# 描述通常位于 class 包含 col-9 的 p 标签中desc_matchre.search(rp[^]*class[^]*col-9[^]*[^]*([\s\S]*?)/p,article)descriptionifdesc_match:# 去除标签内的多余空白和换行raw_descdesc_match.group(1)# 简单清理内部标签保留纯文本clean_textre.sub(r[^],,raw_desc)description .join(clean_text.split()).strip()# 3. 提取编程语言# 查找 itempropprogrammingLanguage 的 span 标签lang_matchre.search(rspan[^]*itempropprogrammingLanguage[^]*([\s\S]*?)/span,article)languageUnknowniflang_match:languagere.sub(r[^],,lang_match.group(1)).strip()# 4. 提取今日新增 Star 数# 匹配类似 123 stars today 或 56 stars this week 的模式star_matchre.search(r([0-9,.]\s*)\s*stars?\s*(today|this week|this month)?,article,re.IGNORECASE)stars_todayN/Aifstar_match:countstar_match.group(1).replace(,,).strip()periodstar_match.group(2)ifstar_match.group(2)elsetodaystars_todayf{count}({period})projects.append({rank:len(projects)1,name:full_name,url:repo_url,desc:description,lang:language,stars:stars_today})returnbuild_markdown_table(projects)defbuild_markdown_table(projects:list)-str:将提取的数据组装为 Markdown 表格ifnotprojects:return无有效数据。md_lines[### GitHub Trending 实时热榜,,| 排名 | 项目名称 | 语言 | 今日热度 | 简介 |,| :--- | :--- | :--- | :--- | :--- |]forpinprojects:# 对描述进行截断防止表格过宽short_desc(p[desc][:40]...)iflen(p[desc])40elsep[desc]linef|{p[rank]}| [{p[name]}]({p[url]}) |{p[lang]}|{p[stars]}|{short_desc}|md_lines.append(line)md_lines.append()md_lines.append( 注数据源自 GitHub 官方 Trending 页面经自动化清洗生成。)return\n.join(md_lines)这段代码逻辑虽然看似基础但在工程实践中至关重要。它做了三件模型做不好的事确定性提取无论模型如何“思考”正则匹配的结果是确定的不会因为 Prompt 的微小变化而丢失数据。噪声过滤彻底丢弃了 HTML 标签、脚本、样式等无关信息只保留语义内容。格式标准化将杂乱的文本统一转换为 Markdown 表格。对于 LLM 来说阅读结构化的表格比阅读散乱的文本要容易得多这能显著提升后续生成报告的质量。经过这个 Code 节点处理后传递给下游 LLM 节点的上下文不再是几万字符的 HTML 乱码而是一段几百字符、清晰明了的 Markdown 表格。模型可以将全部算力集中在分析项目趋势、技术栈价值和潜在风险上而不是浪费在解析网页结构上。前端渲染挑战从 Markdown 到精美报表数据在后端清洗完毕并由模型生成报告后挑战并没有结束。Dify 返回的内容通常是标准的 Markdown 格式包含标题、表格、引用块、代码片段甚至列表。如果前端只是简单地将这些内容作为纯文本显示用户体验将大打折扣——用户看到的将是满屏的##、|和符号而不是一份精美的技术简报。在 OpenRadar 项目的实践中我们放弃了早期尝试的“手写正则替换 HTML方案。那种方式不仅难以覆盖所有 Markdown 语法尤其是嵌套列表和复杂表格而且极易引发 XSS 安全风险。最终的解决方案采用了业界标准的组合拳marked 解析器 DOMPurify 安全清洗。解析与安全的双重保障marked是一个高性能的 Markdown 解析库它能将 Markdown 文本精准地转换为 HTML DOM 结构。然而直接渲染模型生成的 HTML 存在风险因为大模型偶尔可能会“幻觉”出一些script标签或不安全的onerror属性。因此我们必须引入DOMPurify作为中间层。在前端以 React/Next.js 为例的处理流程如下importMarkedfrommarked;importDOMPurifyfromdompurify;// 配置 marked 选项启用 GFM (GitHub Flavored Markdown)Marked.setOptions({gfm:true,breaks:true,// 支持回车换行headerIds:false,mangle:false});constrenderReport(markdownText:string){// 1. 将 Markdown 解析为 HTMLconstrawHtmlMarked.parse(markdownText);// 2. 使用 DOMPurify 清洗 HTML移除潜在的恶意脚本constcleanHtmlDOMPurify.sanitize(rawHtml,{ALLOWED_TAGS:[h1,h2,h3,p,ul,ol,li,table,thead,tbody,tr,th,td,code,pre,blockquote,strong,em,br,a],ALLOWED_ATTR:[href,target,rel,class]});// 3. 渲染到页面returndiv dangerouslySetInnerHTML{{__html:cleanHtml}}/;};这套组合确保了即使用户输入的 Prompt 诱导模型输出了恶意代码最终展现在页面上的也永远是安全的静态内容。样式还原与细节打磨仅仅解析出 HTML 还不够要让报表看起来“精美”还需要配套的 CSS 样式。GitHub 风格的 Markdown 渲染是目前开发者最熟悉的视觉语言。我们需要重点优化以下几个元素的显示效果表格Table这是数据展示的核心。必须添加边框、斑马纹背景nth-child(even)、表头加粗以及适当的内边距。否则模型生成的对比表格会变成一团难以阅读的文字。代码块Pre/Code技术报告中常包含启动命令或配置片段。需要为其添加深色背景、等宽字体以及横向滚动条防止长代码撑破布局。引用Blockquote模型常用引用块来输出“风险提示”或“总结”。需要添加左侧竖线标识和浅色背景使其在视觉上与普通段落区分开。链接Anchor报告中的项目链接应带有下划线或悬停变色效果明确其可点击性。下面给出这三类核心元素的完整 CSS 实现可直接复制到你的样式表中使用/* 1. 表格Table样式 *//* 适用场景模型生成的对比表格、热榜数据表、指标汇总表 */.report-table{width:100%;/* 撑满容器宽度避免窄表居中留白 */border-collapse:collapse;/* 合并相邻边框避免双线 */margin:1rem 0;/* 上下留白与正文段落拉开距离 */font-size:0.9rem;/* 略小于正文让表格更紧凑 */line-height:1.5;/* 行高适中保证多行文本可读 */}/* 表格边框统一使用浅灰色细线视觉上更轻盈 */.report-table th, .report-table td{border:1px solid #d0d7de;/* GitHub 风格的浅灰边框 */padding:8px 12px;/* 内边距保证单元格内容不贴边 */text-align:left;/* 默认左对齐符合阅读习惯 */}/* 表头加粗 浅灰背景与数据行形成层级区分 */.report-table th{background-color:#f6f8fa;/* 浅灰底突出表头 */font-weight:600;/* 半粗体避免过重 */white-space:nowrap;/* 表头不换行保持整洁 */}/* 斑马纹偶数行浅灰底提升长表格的扫读效率 */.report-table tbody tr:nth-child(even){background-color:#f6f8fa;}/* 行悬停高亮鼠标划过时反馈便于定位目标行 */.report-table tbody tr:hover{background-color:#eaeef2;}/* 2. 代码块Pre/Code样式 *//* 适用场景启动命令、配置文件片段、API 调用示例 */.report-code{background-color:#0d1117;/* GitHub 深色代码背景护眼且专业 */color:#e6edf3;/* 浅色文字与深底形成高对比 */border-radius:6px;/* 圆角贴合现代 UI 风格 */padding:16px;/* 内边距避免代码贴边 */overflow-x:auto;/* 横向滚动防止长代码撑破布局 */font-family:SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:0.85rem;/* 等宽字体略小容纳更多内容 */line-height:1.6;/* 行距适中多行代码易读 */margin:1rem 0;/* 上下留白与正文区分 */}/* 行内代码用于正文中提及的变量名、函数名、标签名 */.report-code-inline{background-color:rgba(175,184,193,0.2);/* 浅灰半透明底 */color:#cf222e;/* GitHub 风格的代码红 */border-radius:4px;padding:2px 6px;font-family:SFMono-Regular,Consolas,monospace;font-size:0.85em;}/* 3. 引用块Blockquote样式 *//* 适用场景模型输出的“风险提示”“总结”“注意事项” */.report-quote{border-left:4px solid #0969da;/* 左侧蓝色竖线GitHub 经典引用标识 */background-color:#f6f8fa;/* 浅灰底与正文段落区分 */padding:12px 16px;/* 内边距让引用内容呼吸感更强 */margin:1rem 0;/* 上下留白 */border-radius:0 6px 6px 0;/* 右侧圆角左侧保持直角贴合竖线 */color:#57606a;/* 引用文字用中灰色弱化于正文 */font-size:0.9rem;/* 略小于正文体现“附注”性质 */}/* 引用块内的段落去掉默认外边距避免上下空隙过大 */.report-quote p{margin:0.25rem 0;}/* 引用块内的强调文字保持醒目用于“风险提示”等关键词 */.report-quote strong{color:#cf222e;/* 红色强调突出警示语义 */}通过这一系列的前端处理原本枯燥的 Markdown 源文被转化为了图文并茂、结构清晰的交互式报表。用户不仅可以流畅阅读还可以一键复制原始的 Markdown 内容方便粘贴到笔记软件或团队文档中实现了从“数据清洗”到“知识交付”的完整闭环。构建可复用的数据处理范式通过这个 GitHub 热榜解读器的实战我们可以总结出一套适用于各类 AI 应用的数据处理范式。这套范式的核心思想是职责分离采集层HTTP 节点负责原样获取数据不关心内容格式。清洗层Code 节点负责将非结构化数据HTML/XML/JSON转换为模型友好的结构化文本Markdown/JSON。这是保证输出质量的关键防线。推理层LLM 节点专注于逻辑分析、观点生成和内容润色不再承担数据提取的脏活累活。展现层前端负责安全解析与美化渲染提供最佳阅读体验。这种架构不仅解决了 HTML 乱码问题更极大地提升了系统的稳定性和可维护性。当 GitHub 页面结构发生微调时我们只需调整 Code 节点中的正则逻辑而无需重新训练模型或修改复杂的 Prompt。对于正在探索 AI 工程化的后端工程师而言掌握这种“预处理 结构化”的思维比单纯调试 Prompt 更有长远价值。毕竟在真实的业务场景中干净的数据永远是高质量 AI 输出的基石。
返回列表