首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 實作 RSS 新聞閱讀器:XmlDocument 解析、TreeView 顯示與超連結導航

     編輯:關於C語言

專案目標

建立一個 Windows Forms 應用,輸入 RSS 連結,解析 XML 結構,以樹狀檢視顯示頻道標題與新聞項目,點擊新聞標題以預設瀏覽器開啟原文連結。

RSS 結構速覽

<rss version="2.0">
  <channel>
    <title>頻道名稱</title>
    <link>頻道網址</link>
    <description>頻道描述</description>
    <item>
      <title>新聞標題</title>
      <link>新聞網址</link>
      <description>摘要內容</description>
      <pubDate>發布時間</pubDate>
    </item>
    ... 多個 item ...
  </channel>
</rss>

核心節點:`rss` → `channel` → 多個 `item`(各含 `title`, `link`, `description` 等)。

資料模型定義

public class RssFeed
{
    public Channel Channel { get; set; } = new();

    public class Channel
    {
        public string Title { get; set; } = "";
        public string Link { get; set; } = "";
        public string Description { get; set; } = "";
        public List Items { get; set; } = new();
    }

    public class Item
    {
        public string Title { get; set; } = "";
        public string Link { get; set; } = "";
        public string Description { get; set; } = "";
        public DateTime PubDate { get; set; }
    }
}

核心解析邏輯(XmlDocument + XPath)

using System.Xml;

public static RssFeed ParseRss(string url)
{
    var feed = new RssFeed();
    var doc = new XmlDocument();
    
    // 設定逾時與安全性
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Timeout = 10000;
    request.UserAgent = "RssReader/1.0";

    using (var response = (HttpWebResponse)request.GetResponse())
    using (var stream = response.GetResponseStream())
    {
        doc.Load(stream); // 直接從串流載入,不落地檔案
    }

    // 找 channel 節點
    XmlNode channelNode = doc.SelectSingleNode("/rss/channel");
    if (channelNode == null) throw new XmlException("無效的 RSS 格式:找不到 channel");

    // 讀取 channel 基本資訊
    feed.Channel.Title = channelNode.SelectSingleNode("title")?.InnerText ?? "";
    feed.Channel.Link = channelNode.SelectSingleNode("link")?.InnerText ?? "";
    feed.Channel.Description = channelNode.SelectSingleNode("description")?.InnerText ?? "";

    // 遍歷所有 item
    XmlNodeList itemNodes = channelNode.SelectNodes("item");
    foreach (XmlNode itemNode in itemNodes)
    {
        var item = new RssFeed.Item
        {
            Title = itemNode.SelectSingleNode("title")?.InnerText ?? "(無標題)",
            Link = itemNode.SelectSingleNode("link")?.InnerText ?? "",
            Description = itemNode.SelectSingleNode("description")?.InnerText ?? "",
        };

        // 解析發布日期(RFC 822 / ISO 8601 格式兼容)
        string dateStr = itemNode.SelectSingleNode("pubDate")?.InnerText;
        if (DateTime.TryParse(dateStr, out DateTime dt))
            item.PubDate = dt;

        feed.Channel.Items.Add(item);
    }

    return feed;
}

WinForms UI 綁定與互動

介面配置

  • `TextBox txtUrl` — 輸入 RSS 連結
  • `Button btnLoad` — 觸發載入
  • `TreeView tvFeed` — 顯示頻道與新聞標題
  • `WebBrowser` / `TextBox` (可選) — 顯示摘要內容

載入按鈕事件

private async void btnLoad_Click(object sender, EventArgs e)
{
    btnLoad.Enabled = false;
    tvFeed.Nodes.Clear();
    try
    {
        // 非同步載入避免 UI 凍結
        var feed = await Task.Run(() => ParseRss(txtUrl.Text.Trim()));
        
        // 建立樹狀節點
        var channelNode = new TreeNode(feed.Channel.Title) { Tag = feed.Channel.Link };
        tvFeed.Nodes.Add(channelNode);

        foreach (var item in feed.Channel.Items)
        {
            var itemNode = new TreeNode(item.Title) { Tag = item.Link };
            // 可選:ToolTip 顯示摘要/日期
            itemNode.ToolTipText = $"{item.PubDate:yyyy-MM-dd HH:mm}\n{item.Description}";
            channelNode.Nodes.Add(itemNode);
        }
        channelNode.ExpandAll();
    }
    catch (Exception ex)
    {
        MessageBox.Show($"載入失敗:{ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    finally { btnLoad.Enabled = true; }
}

節點點擊事件 — 開啟連結

private void tvFeed_AfterSelect(object sender, TreeViewEventArgs e)
{
    if (e.Node.Tag is string url && !string.IsNullOrWhiteSpace(url))
    {
        try
        {
            // 使用 Process.Start 開啟預設瀏覽器 (.NET Core 3.0+ 需 UseShellExecute=true)
            var psi = new ProcessStartInfo(url) { UseShellExecute = true };
            Process.Start(psi);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"無法開啟連結:{ex.Message}");
        }
    }
}

進階強化方向

功能實作建議
非同步增量載入大量項目時分批 `BeginUpdate/EndUpdate` 或虛擬模式
內容預覽選中項目時在 `WebView2` 或 `RichTextBox` 顯示 `Description` (HTML 清理)
已讀/未讀標記本地 SQLite / JSON 儲存已讀項目 GUID/Link,節點圖示區分
定時自動重新整理`System.Threading.Timer` 背景輪詢,更新節點文字顏色提示新文章
支援 Atom 格式擴充解析器偵測 `feed` 根節點,對應 `entry`、`link[@rel='alternate']`
OPML 匯入/匯出管理多組 RSS 訂閱清單

常見問題與解決

  • ❌ **XmlDocument.Load 直接丟 URL 會失敗** → 必須用 `HttpWebRequest`/`HttpClient` 取得串流再 `Load(stream)`,才能控制逾時、User-Agent、Cookie、重導向。
  • ❌ **編碼亂碼** → RSS 通常宣告 ``,`XmlDocument` 會自動處理;若無宣告且實際為 Big5/GB2312,需手動指定 `XmlTextReader` 編碼。
  • ❌ **HTTPS 憑證驗證失敗** → 開發環境可暫時 `ServerCertificateCustomValidationCallback = (_,_,_,_) => true;`,生產環境務必修正憑信任鏈。
  • ❌ **UI 凍結** → 必須用 `Task.Run` 或 `async/await` 將網路 IO 與解析移出 UI 執行緒。

總結

利用 `XmlDocument` + XPath 解析 RSS 結構直觀且穩健,搭配 `TreeView` 呈現階層資料、`Process.Start` 導航外部連結,即可快速構建實用的新聞閱讀器原型。後續可擴充為支援分類、標籤、離線閱讀、全文搜尋的完整資訊聚合工具。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved