如何使用 Golang 解码 Reddit 的 RSS?

Posted

技术标签:

【中文标题】如何使用 Golang 解码 Reddit 的 RSS?【英文标题】:How to decode Reddit's RSS using Golang? 【发布时间】:2013-10-05 13:32:31 【问题描述】:

我一直在玩 Go 的 XML 包,看不出下面的代码有什么问题。

package main

import (
    "encoding/xml"
    "fmt"
    "net/http"
) 

type Channel struct 
    Items Item


type Item struct 
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`


func main() 

    var items = new(Channel)
    res, err := http.Get("http://www.reddit.com/r/google.xml")

    if err != nil 
        fmt.Printf("Error: %v\n", err)
     else 
        decoded := xml.NewDecoder(res.Body)

        err = decoded.Decode(items)

        if err != nil 
            fmt.Printf("Error: %v\n", err)
        

        fmt.Printf("Title: %s\n", items.Items.Title)
    

上面的代码运行没有任何错误并打印到终端:

Title:

该结构似乎是空的,但我不明白为什么它没有填充 XML 数据。

【问题讨论】:

【参考方案1】:

您的程序接近了,但只需要指定更多的上下文来匹配 XML 文档。

您需要修改字段标签以帮助引导 XML 绑定通过您的 Channel 结构到您的Item 结构:

type Channel struct 
    Items []Item `xml:"channel>item"`


type Item struct 
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`

根据the documentation for encoding/xml.Unmarshal(),这里适用第七条:

如果 XML 元素包含名称匹配的子元素 格式为“a”或“a>b>c”的标记的前缀,解组 将下降到 XML 结构中查找具有 给定名称,并将最里面的元素映射到该结构 场地。以“>”开头的标签相当于一个开头 字段名称后跟“>”。

在您的情况下,您希望通过*** <rss> 元素的 <channel> 元素向下查找每个 <item> 元素。但请注意,我们不需要(实际上也不能)指定 Channel 结构应该通过将 Items 字段的标记写入*** <rss> 元素为

`xml:"rss>channel>item"`

上下文是隐含的;提供给 Unmarshall() 的结构已经映射到*** XML 元素。

还要注意,您的 Channel 结构的 Items 字段应该是 slice-of-Item 类型,而不仅仅是单个 Item


您提到您无法让提案生效。这是一个完整的清单,我发现它可以按预期工作:

package main

import (
    "encoding/xml"
    "fmt"
    "net/http"
    "os"
) 

type Channel struct 
    Items []Item `xml:"channel>item"`


type Item struct 
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`


func main() 
    if res, err := http.Get("http://www.reddit.com/r/google.xml"); err != nil 
        fmt.Println("Error retrieving resource:", err)
        os.Exit(1)
     else 
        channel := Channel
        if err := xml.NewDecoder(res.Body).Decode(&channel); err != nil 
            fmt.Println("Error:", err)
            os.Exit(1)
         else if len(channel.Items) != 0 
            item := channel.Items[0]
            fmt.Println("First title:", item.Title)
            fmt.Println("First link:", item.Link)
            fmt.Println("First description:", item.Description)
        
    

【讨论】:

【参考方案2】:

我会像这样完全明确 - 命名所有 XML 部分

See the playground for a full working example

type Rss struct 
    Channel Channel `xml:"channel"`


type Channel struct 
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`
    Items       []Item `xml:"item"`


type Item struct 
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`

【讨论】:

【参考方案3】:

如今,Reddit RSS 提要似乎已更改为 atom 类型。这意味着常规解析将不再起作用。 go-rss 的原子功能可以解析此类提要:

//Feed struct for RSS
type Feed struct 
  Entry []Entry `xml:"entry"`


//Entry struct for each Entry in the Feed
type Entry struct 
  ID      string `xml:"id"`
  Title   string `xml:"title"`
  Updated string `xml:"updated"`


//Atom parses atom feeds
func Atom(resp *http.Response) (*Feed, error) 
  defer resp.Body.Close()
  xmlDecoder := xml.NewDecoder(resp.Body)
  xmlDecoder.CharsetReader = charset.NewReader
  feed := Feed
  if err := xmlDecoder.Decode(&feed); err != nil 
      return nil, err
  
  return &feed, nil

【讨论】:

以上是关于如何使用 Golang 解码 Reddit 的 RSS?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有for循环的情况下抓取网址列表?

如何使用Selenium Python从reddit.com搜索页面上的问题中提取title和href属性

PHP编码gzdeflate与Golang解码DEFLATE

在 golang 中使用 XMLNS 声明编码/解码 XML 根令牌

Golang 解码 MusicXML

golang json 编码解码