应使用 encoding/xml 而非正则解析 xml,因其能正确处理嵌套、命名空间、cdata、注释等复杂场景;对 sitemap.xml 中 标签,需定义含 xml:",chardata" 的导出结构体并用 xml.unmarshal 或 xml.decoder 流式解析。

别用正则提取 XML 标签 —— 它在嵌套、属性、CDATA、注释等场景下必然出错,且无法保证语义正确性。 如果你只是想快速从纯文本(非标准 XML)里捞几个 <tag></tag> 或 <loc></loc>,正则可以凑合;但只要文件来自 .xlsx 解压内容、RSS、sitemap 或任何带命名空间/嵌套结构的 XML,encoding/xml 是唯一靠谱路径。
为什么 regexp.FindAllString 提取 <loc></loc> 会漏掉或错配
常见错误现象:用 regexp.MustCompile(`<loc>([^`)</loc> 匹配,结果遇到以下任一情况就失效:
-
<loc></loc>和跨行(正则默认不匹配换行符) -
<loc xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></loc>带命名空间,闭合标签可能缩写为,但正则无法识别语义等价 - 内容含
或注释<!-- ... -->,正则会把它们当普通文本误吞 -
<loc>http://a<b>c</b>d.com</loc>这种内部有标签的合法 XML,正则直接崩溃
encoding/xml 解析 <loc></loc> 的最小可行结构体
必须满足三个条件:字段导出、xml: 标签显式声明、文本内容用 ,chardata 捕获。例如解析 sitemap.xml 中所有 <loc></loc>:
type Sitemap struct {
XMLName xml.Name `xml:"urlset"`
Locs []Loc `xml:"url>loc"`
}
<p>type Loc struct {
Text string <code>xml:",chardata"</code>
}
</p>
关键点:
-
Locs []Loc `xml:"url>loc"`表示只取<url></url>下的<loc></loc>,避免顶层同名干扰 -
Text string `xml:",chardata"`直接抓文本,不额外包一层<loc></loc>结构 - 字段名
Locs和Text必须首字母大写,否则xml.Unmarshal完全忽略
遇到命名空间时怎么绕过 xmlns 导致解析为空
错误现象:<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset> 一加命名空间,xml.Unmarshal 就返回空切片,不报错也不提示。
最轻量解法是预处理 XML 字符串,删掉命名空间声明(仅限你完全控制输入格式时):
data = bytes.ReplaceAll(data, []byte(`xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"`), []byte(""))
data = bytes.ReplaceAll(data, []byte(`xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`), []byte(""))
更健壮的做法是改用 xml.Decoder 流式解析,手动跳过命名空间检查:
- 调用
decoder.Token()遍历每个 token - 遇到
xml.StartElement时,检查Name.Space是否为空,非空则用decoder.Skip()跳过整段 - 只在
Name.Local == "loc"且父元素是"url"时提取CharData
大文件用 xml.Unmarshal 会 OOM?该换 xml.Decoder 了
如果你的 XML 文件超过几 MB,xml.Unmarshal 会把整个文档加载进内存再解析,极易触发 GC 压力甚至 panic: out of memory。
流式解析的核心逻辑:
- 用
xml.NewDecoder(file)替代xml.Unmarshal - 循环调用
decoder.Token(),只保留当前需要的字段值 - 遇到不需要的深层嵌套,立刻
decoder.Skip(),不递归展开 - 每提取一个
<loc></loc>就立即处理或写入 channel,不累积切片
真正容易被忽略的是:流式解析时,decoder.Token() 返回的 xml.CharData 可能包含空白符(换行、缩进),需用 strings.TrimSpace 清洗,否则拿到的 Text 前后带 \n 或 \t。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











