最可靠的方法是使用 golang.org/x/net/html 包解析 html 并递归提取文本节点内容,正确处理实体解码、嵌套结构和边界情况,避免正则匹配的不可靠性。

用 golang.org/x/net/html 解析并递归提取文本最可靠
正则匹配 HTML 标签在 Go 里看似简单,但实际会漏掉注释、CDATA、自闭合标签嵌套、属性中含 等边界情况,根本不可靠。真正安全的做法是走标准 HTML 解析器路径。
推荐使用官方维护的 golang.org/x/net/html 包,它能正确处理实体解码、命名空间、嵌套结构和各种边缘 case。
- 先用
html.Parse将字符串转为文档树(*html.Node) - 写一个递归函数,只收集
html.TextNode类型节点的内容 - 对每个文本节点调用
strings.TrimSpace去首尾空白(可选,取决于是否需要保留换行缩进) - 跳过
html.CommentNode和html.DoctypeNode,避免把<!-- hello -->当成正文
strings.ReplaceAll + 正则只能用于“已知干净”的简单场景
如果你确定输入 HTML 是人工生成、无嵌套、无 JS/CSS、无实体编码(比如 ),且只是临时脚本快速提取,可以考虑正则辅助清理:
- 先用
strings.ReplaceAll(htmlStr, " ", " ")手动替换常见实体 - 再用
regexp.MustCompile(`]*>`).ReplaceAllString(htmlStr, "")删标签 - 最后
strings.Fields拆词并strings.Join拼空格——但这会彻底抹平所有格式,连段落分隔都消失
这种做法在真实网页抓取中大概率出错:遇到 <script>console.log("<div>")</script> 就直接崩。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
注意 html.Node.DataAtom 和 html.Node.Data 的区别
遍历节点时容易混淆这两个字段:
-
node.Type == html.TextNode时,有效文本在node.Data,不是node.DataAtom -
node.DataAtom只对元素节点(如html.DivAtom)有意义,是预计算的原子标识,文本节点的DataAtom恒为 0 - 别忘了对
node.Data做html.UnescapeString,否则©会原样输出
示例关键片段:
func extractText(n *html.Node) string {
if n.Type == html.TextNode {
return html.UnescapeString(strings.TrimSpace(n.Data))
}
var text strings.Builder
for c := n.FirstChild; c != nil; c = c.NextSibling {
text.WriteString(extractText(c))
if c.Type == html.TextNode && c.NextSibling != nil && c.NextSibling.Type == html.TextNode {
text.WriteString(" ") // 防止相邻文本节点粘连
}
}
return text.String()
}
性能敏感时避免重复解析或深递归
如果要高频处理大量 HTML 片段(比如日志清洗),注意两点:
- 不要每次调用都新建
strings.NewReader+html.Parse—— 解析本身开销不小;可考虑复用html.Tokenizer流式处理 - 递归过深可能触发 goroutine stack overflow(尤其畸形 HTML 有千层嵌套),改用显式栈模拟更稳妥
- 若只需纯文本且不关心结构,
golang.org/x/net/html/charset还能帮你自动检测并转换编码,避免乱码
真正难的不是“怎么拿文本”,而是“怎么拿得既准又稳还快”——大部分坑都在实体处理、编码识别和异常 HTML 容错上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










