如何在Golang中开发RSS聚合阅读器CLI Go语言XML解析实战

1次阅读

根本原因是RSS命名空间未显式声明导致xml.Unmarshal忽略子元素,需在结构体字段中用完整URL映射如xml:”http://purl.org/dc/elements/1.1/ creator”,并手动处理重定向、gzip解压、并发安全及缓存格式选择。

如何在Golang中开发RSS聚合阅读器CLI Go语言XML解析实战

encoding/xml 解析 RSS 2.0 时字段经常为空?

根本原因不是 XML 结构复杂,而是 RSS 常见命名空间(如 xmlns:dc="http://purl.org/dc/elements/1.1/")和混用前缀导致 xml.Unmarshal 默认忽略子元素。go 的标准库不自动处理命名空间,也不会“猜”你想要哪个 title —— 是 channel 的还是 item 的?

  • 显式声明结构体字段的 XML 标签,尤其注意嵌套层级:XMLName xml.Name `xml:"rss"`Title String `xml:"channel>title"`
  • 对带命名空间的字段(如 dc:creator),直接按带冒号的完整名映射:Creator string `xml:"http://purl.org/dc/elements/1.1/ creator"`(注意 URL 必须写全,不能省略协议)
  • 避免用 anyInterface{} 接收未知结构 —— 它会让解析静默失败,字段全为空字符串

net/http 抓 RSS 源时遇到重定向或 gzip 崩溃?

RSS 源常返回 301/302 重定向(比如从 HTTP 跳 https),而 Go 默认的 http.DefaultClient 会自动跟随,但某些 RSS 生成器在重定向后没设好 Content-Type,导致后续解析误判编码;另外不少服务默认启用 gzip,但 xml.Unmarshal 不会自动解压。

  • 手动控制重定向:设置 Client.CheckRedirect 返回 http.ErrUseLastResponse,自己读取 resp.Header.Get("location") 再发请求,确保最终响应头含 Content-Type: application/rss+xml
  • 强制解 gzip:检查 resp.Header.Get("Content-Encoding") == "gzip",用 gzip.NewReader(resp.Body) 包一层再传给 xml.NewDecoder
  • 别依赖 resp.Body 直接传给 xml.Decode —— 万一 Body 已被读过(比如打印了 raw body 调试),就会得到空数据

并发拉取多个 RSS 源时 panic: “invalid memory address”?

典型表现是跑几轮后在 xml.Unmarshalhttp.Do 处 panic,实际是共享了未加锁的结构体字段(比如共用一个 *http.Client 或全局切片),或在 goroutine 里直接修改了 map / slice 而没同步。

  • 每个 goroutine 应该有自己的局部变量接收解析结果,不要往同一个 []Itemappend —— 改用 sync.WaitGroup + 闭包捕获,或收集到 channel 后统一合并
  • http.Client 本身是并发安全的,但它的 Transport 如果被手动替换(比如加了自定义 DialContext),就得确认底层连接池没被多 goroutine 错误复用
  • XML 解析过程不涉及共享状态,但如果你在 Unmarshal 后立刻修改结构体指针字段(比如补全 item.URL = feedURL),要确保该结构体没被其他 goroutine 同时读取

本地缓存 RSS 数据该用什么格式?别碰 json

JSON 看似方便,但 RSS 的 pubDate 格式不统一(RFC 822、RFC 850、ISO 8601 都有),Go 的 time.Time 反序列化 JSON 时容易出错;更麻烦的是,JSON 无法保留原始 XML 的命名空间信息和属性(比如 <item dc:creator="A">),下次重新聚合时元数据就丢了。

立即学习go语言免费学习笔记(深入)”;

  • 缓存首选二进制格式:用 gob 编码原生 Go 结构体,天然支持 time.Time 和嵌套指针,且体积小、速度快
  • 如果必须人眼可读,选 XML 本身 —— 把解析后的结构体用 xml.Marshal 写回文件,下次直接 xml.Unmarshal,零信息损失
  • 绝对避免把 map[string]interface{} 存 JSON:字段类型在反序列化时丢失,pubDate 变成 string,排序、过滤全得手动 parse

真正卡住人的从来不是怎么解析 XML,而是 RSS 源本身质量参差——有的连 </item> 都漏写,有的在 description 里塞未转义 HTML。建议在 Unmarshal 后加一层校验:检查必要字段非空、日期能 parse 成 time.Time、link 字段是合法 URL。这些细节不提前挡掉,后面聚合逻辑越写越像修仙。

text=ZqhQzanResources