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

C# 實作網頁爬蟲入門:抓取 HTML 原始碼與解析節點連結

     編輯:關於C語言

初次接觸網頁爬蟲開發時,往往會因為資訊龐雜而感到無從下手。網路上雖有大量教學資源,但真正可直接套用、解決實際問題的程式碼卻不易尋覽。本文將以實戰經驗為基礎,帶領讀者從零開始,完成「抓取網頁 HTML 原始碼」並「解析特定節點超連結」的核心功能,助你少走彎路、快速上手。

核心需求:擷取指定 UL 節點下的所有 HREF

假設目標網頁結構包含 <ul class="post_list">...</ul> 區塊,我們需要撈出該區塊內所有 <a> 標籤的 href 屬性值。這是許多列表型爬蟲的第一步關鍵動作。

必要命名空間

using System.IO;
using System.Net;
using System.Text; // StringBuilder 需要
using System.Text.RegularExpressions; // 若後續改用 Regex 解析

完整實作程式碼

/// <summary>
/// 根據傳入 URL 抓取網頁內容,並解析出 class="post_list" 區塊裡的所有超連結
/// </summary>
private void Search(string url)
{
    // 1. 建立請求並取得回應串流
    WebRequest request = WebRequest.Create(url.Trim());
    using (WebResponse response = request.GetResponse())
    using (Stream resStream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(resStream, Encoding.GetEncoding("utf-8"))) // 建議明確指定編碼
    {
        // 2. 將完整 HTML 讀入字串
        StringBuilder htmlBuilder = new StringBuilder();
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            htmlBuilder.AppendLine(line);
        }
        string fullHtml = htmlBuilder.ToString().ToLower(); // 統一轉小寫方便比對

        // 3. 截取目標節點區塊
        string targetBlock = Mid(fullHtml, "<ul class=\"post_list\">", "</ul>");
        if (string.IsNullOrEmpty(targetBlock)) return;

        // 4. 循環擷取所有 href
        int startIndex = 0;
        while (true)
        {
            string href = Mid(targetBlock, "href=\"", "\"", out startIndex);
            if (href == null) break;

            // 這裡可將 href 加入佇列、寫入資料庫或直接處理
            // 範例:lab[url] += href; // 原始碼寫法,建議改用 List<string> 或 ConcurrentQueue
            targetBlock = targetBlock.Substring(startIndex);
        }
    }
}

/// <summary>
/// 從來源字串中,擷取 startStr 與 endStr 之間的內容(不含界限字串)
/// </summary>
private string Mid(string source, string startStr, string endStr)
{
    int startPos = source.IndexOf(startStr);
    if (startPos == -1) return null;
    startPos += startStr.Length;

    int endPos = source.IndexOf(endStr, startPos);
    if (endPos == -1) return null;

    return source.Substring(startPos, endPos - startPos);
}

/// <summary>
/// 多載版本:擷取內容並回傳 endStr 結束位置,供後續 Substring 使用
/// </summary>
private string Mid(string source, string startStr, string endStr, out int endPos)
{
    endPos = 0;
    int startPos = source.IndexOf(startStr);
    if (startPos == -1) return null;
    startPos += startStr.Length;

    endPos = source.IndexOf(endStr, startPos);
    if (endPos == -1) return null;
    endPos += endStr.Length; // 指向 endStr 結束處

    return source.Substring(startPos, endPos - startPos - endStr.Length);
}

關鍵技術點解析

1. 編碼處理

原始碼使用 Encoding.Default 易導致中文亂碼。實務上建議:

  • 優先從 response.CharacterSet 取得編碼
  • 若為空則嘗試 Encoding.UTF-8 或 Encoding.GetEncoding("gb2312")
  • 或引入 HtmlAgilityPack 自動偵測編碼

2. 字串解析 vs HTML 解析器

本範例採用 IndexOf/Substring 純字串處理,優點是零依賴、效能極佳;缺點是對巢狀標籤、屬性順序變動、跨行等情況容錯率低。正式專案強烈建議改用 HtmlAgilityPack 或 AngleSharp:

// HtmlAgilityPack 範例
var doc = new HtmlDocument();
doc.LoadHtml(fullHtml);
var links = doc.DocumentNode.SelectNodes("//ul[@class='post_list']//a[@href]")
               ?.Select(n => n.GetAttributeValue("href", ""))
               .ToList();

3. 例外處理與資源釋放

程式碼已採用 using 區塊確保串流正確關閉,實務上尚需補充:

  • try-catch 捕捉 WebException、IOException
  • 設定 request.Timeout 避免長時間阻塞
  • 加入 User-Agent 模擬瀏覽器行為

後續擴充建議

  1. 非同步化:改用 HttpClient 搭配 await 提升併發效能
  2. 去重機制:引入 HashSet<string> 或 Bloom Filter 過濾重複 URL
  3. 禮貌性爬蟲:遵守 robots.txt、設定請求間隔、處理 301/302 重導向
  4. 動態網頁:遇到 JS 渲染內容需搭配 Selenium、Playwright 或 PuppeteerSharp

掌握「抓取→解析→存儲」這三步核心流程,即可奠定爬蟲開發的堅實基礎。建議讀者先跑通本範例,再逐步引入非同步、解析器、分散式佇列等進階架構,打造穩健可維護的爬蟲系統。

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