当前位置:首页 > 文章列表 > Golang > Go教程 > Go goquery 怎么移除副本中的节点而保留原文档

Go goquery 怎么移除副本中的节点而保留原文档

来源:17golang原创 2026-10-13 15:35:43 0浏览 收藏

要在 goquery 副本中删节点而保留原文档,先对需要编辑的选择集调用 Clone(),再从克隆结果中 Find(...).Remove()。直接赋值选择集变量,或重新从原文档查一次相同节点,都不会产生独立的 HTML 节点树。如果要修改整份文档,则使用 goquery.CloneDocument(doc)。

官方地址:https://github.com/PuerkitoBio/goquery

选择集赋值为什么不能隔离修改

假设一份页面需要生成两种内容:归档版本保留作者备注,阅读版本去掉工具栏和备注。共同输入应保持不变,每个导出任务只编辑自己的工作副本。问题不在于变量名称是否不同,而在于选择集是否仍然引用同一批底层节点。

例如 work := source 只是另一个引用。即使复制 Selection 结构体,节点指针仍可能共享。原文档上一次删除操作会改变后续导出看到的内容,这类依赖调用顺序的问题不能靠“换一个变量名”解决。Selection 的节点组成可参阅 官方类型实现。

本例只导出一个 article,所以把克隆范围限定在文章子树,避免每个小任务都复制页面导航、页脚等无关内容。这里是结构上的取舍,不是对实际吞吐量的测试结论。

克隆文章子树,再移除副本中的后代节点

下面的完整程序使用内存 HTML,不发起网页请求。source 来自原文档,work 来自 Clone。辅助节点只在 work 范围内查找和移除。示例代码为原创,未在本文环境执行;输出语句和计数条件供读者自行运行比较。

package main

import (
    "fmt"
    "strings"

    "github.com/PuerkitoBio/goquery"
)

func main() {
    input := `

周末工坊记录

保留这段正文。

分享入口
` doc, err := goquery.NewDocumentFromReader(strings.NewReader(input)) if err != nil { fmt.Println("解析失败:", err) return } source := doc.Find("article#entry").First() if source.Length() == 0 { fmt.Println("文章节点不存在") return } // 深复制文章及其后代;后续修改只发生在工作子树。 work := source.Clone() removed := work.Find(".toolbar, .editor-note").Remove() // OuterHtml保留article本身,Html只返回内部内容。 fragment, err := goquery.OuterHtml(work) if err != nil { fmt.Println("序列化失败:", err) return } fmt.Println("移除数量:", removed.Length()) fmt.Println("阅读片段:", fragment) // 用各自的查询范围比较,避免又查回原文档。 fmt.Println("原文辅助节点:", source.Find(".toolbar, .editor-note").Length()) fmt.Println("副本辅助节点:", work.Find(".toolbar, .editor-note").Length()) }

对这个输入,按代码逻辑应移除两个副本后代,原文辅助节点计数仍为两个,副本为零。不要把这些预期值当作本文实测日志。Remove() 返回被移除节点的选择集,而不是清理后的 article。因此结果必须从 work 序列化,不能把 removed 当成最终文章。

官方 Clone 与 Remove 实现明确区分了深复制和从父节点脱离的操作。克隆节点不自动插入原文档;在克隆子树内部移除后代,不需要把克隆根重新挂回页面。

原文章和克隆文章拥有不同的辅助节点
静态结构说明图:原始子树与工作子树的节点分属不同边界;对副本后代的删除不修改原树。图示不是运行截图或实测证据。

子树 Clone 和 CloneDocument 有什么区别

Selection.Clone 适合处理选中的片段;完整文档还包含文档根以及查询上下文。如果工作需要同时编辑 head、body 和多个区域,可使用完整文档克隆,并从克隆文档重新取得后续选择集:

// 此片段接在doc解析成功后;不要复用原doc上的Selection。
workingDoc := goquery.CloneDocument(doc)
workingDoc.Find(".toolbar, .editor-note").Remove()
page, err := goquery.OuterHtml(workingDoc.Selection)
if err != nil {
    fmt.Println("页面序列化失败:", err)
    return
}
fmt.Println(page)

完整克隆的节点根实现参阅 CloneDocument 源码。不要把 Clone() 理解为“所有关联信息都变成独立文档”:克隆选择集仍有关联的文档上下文。在片段模式下,保持查询相对于 work,避免使用会访问文档根的操作重新接触原始节点。

处理范围选择注意点
一个文章片段source.Clone()从work查找并输出片段
整页多个区域CloneDocument(doc)从workingDoc取得选择集
多个选中根节点选择集CloneOuterHtml只输出第一项,需逐项处理
工作选择集与内外HTML序列化范围
静态关系说明图:片段根、后代选择集、移除结果与序列化函数是不同对象;重点区分内部HTML和包含根标签的HTML,不表示执行时序。

批量导出时控制复制范围与生命周期

复制隔离的代价是额外节点和内存占用。只需要文章时复制文章,确实需要整页时再复制文档;尽量选择互不重叠的根,避免同时复制父节点和它的后代形成重复工作。具体节省多少,应由真实输入规模和项目测量判断,不在这里编造指标。

保留原树只读,每个编辑任务使用独立副本,是一种清楚的职责划分。但不能让多个 goroutine 同时编辑同一棵工作树,也不能在原树正被修改时把克隆当成一致性快照。克隆不是并发锁。任务完成后不要长期保存 removed 等节点选择集;它们仍引用节点,移除不等于立即释放内存。

如果下游接受 HTML 片段,OuterHtml 可以保留文章根;需要完整页面时输出完整文档。序列化方法和第一项范围可参阅 官方序列化工具实现。HTML 解析和重新输出也不承诺字节级保持原文本,原始字节归档应另存输入。

Find 会把克隆根本身移除吗?

本例 Find 用于寻找后代。如果筛选条件针对选择集自身,应使用 Filter 等筛选方式;克隆根已经脱离原文档,没有父节点时 Remove 不能把它从某个父树里再删除。

为什么原文还是被删了?

检查删除操作是否实际使用 doc.Find 或 source.Find,或者复用了克隆之前取得的辅助节点选择集。删除目标必须重新从 work 或 workingDoc 中取得。

删掉工具栏就是 HTML 安全过滤了吗?

不是。本例仅按业务选择器移除指定节点,不提供通用安全净化能力。不要把版面裁剪用于替代面向不可信HTML的专门处理。

保留原文档的关键是节点归属:明确哪棵树只读、哪棵树可编辑,再限定选择器和序列化范围。变量分开只是表面,节点深复制才是隔离这次删除操作的核心。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go goquery 怎么区分属性缺失和属性值为空Go goquery 怎么区分属性缺失和属性值为空
上一篇
Go goquery 怎么区分属性缺失和属性值为空
Go goquery Text 为什么包含子节点中的全部文字
下一篇
Go goquery Text 为什么包含子节点中的全部文字
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    460次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    521次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    539次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    487次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    307次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码