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

C# 網頁爬蟲實戰三部曲:緩存策略、編碼偵測與 HtmlAgilityPack 解析

     編輯:ASP.NET 基礎

前言

開發網頁爬蟲時,三大核心挑戰:遵守緩存減輕伺服器壓力、正確處理多種編碼與壓縮、高效解析 HTML 結構。本文以實戰代碼逐一攻克。

一、緩存策略:條件請求 If-Modified-Since

HTTP 規範支援 Last-Modified / ETag 條件請求,未變更回傳 304 Not Modified,無需傳輸內容。

實作:模擬瀏覽器條件請求


static void CrawlWithCache()
{
    DateTime lastModified = DateTime.MinValue;
    for (int i = 0; i < 10; i++)
    {
        try
        {
            var req = (HttpWebRequest)WebRequest.Create("https://www.cnblogs.com");
            req.Method = "HEAD"; // 僅取標頭
            if (i > 0) req.IfModifiedSince = lastModified;
            req.Timeout = 3000;

            using var resp = (HttpWebResponse)req.GetResponse();
            Console.WriteLine($"第 {i+1} 次狀態碼: {resp.StatusCode}");

            if (resp.StatusCode == HttpStatusCode.OK)
            {
                lastModified = DateTime.Parse(resp.Headers[HttpResponseHeader.Date]);
                Console.WriteLine("  → 內容已更新,記錄新 Last-Modified");
            }
        }
        catch (WebException ex) when (ex.Response is HttpWebResponse hr)
        {
            Console.WriteLine($"第 {i+1} 次狀態碼: {hr.StatusCode}");
        }
        Thread.Sleep(1000);
    }
}

輸出示例:首次 200 OK,後續多次 304 Not Modified,完美符合緩存機制。

二、編碼與壓縮地雷:GZIP + Charset 偵測

常見坑點

  1. GZIP 壓縮:伺服器回應 `Content-Encoding: gzip`,直接讀取 Stream 會得到亂碼。
  2. Charset 缺失/錯誤:`Content-Type: text/html; charset=gb2312` 可能缺失,或標示 `ISO-8859-1` 實為中文編碼。
  3. Meta 標籤覆蓋:HTTP 標頭與 `` 不一致,以 HTTP 標頭優先,但無標頭時需解析 Meta。

完整解碼流程


static string FetchHtml(string url)
{
    var req = (HttpWebRequest)WebRequest.Create(url);
    req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; // 自動解壓
    using var resp = (HttpWebResponse)req.GetResponse();

    // 1. 判斷編碼
    string encodingName = resp.CharacterSet;
    if (encodingName == "ISO-8859-1") encodingName = "gb2312"; // 常見誤標修正

    // 2. 取得解壓後 Stream
    Stream stream = resp.GetResponseStream(); // AutomaticDecompression 已處理

    // 3. 讀取並解碼
    using var reader = new StreamReader(stream, Encoding.GetEncoding(encodingName));
    return reader.ReadToEnd();
}

HttpWebRequest.AutomaticDecompression 是 .NET 4.5+ 隱藏神技,自動處理 GZIP/Deflate,無需手動包裝 GZipStream。

三、HTML 解析:HtmlAgilityPack + XPath

安裝

Install-Package HtmlAgilityPack

核心用法


var html = FetchHtml("https://www.sohu.com/");
var doc = new HtmlDocument();
doc.LoadHtml(html);

// 1. 取 Title
string title = doc.DocumentNode.SelectSingleNode("//title")?.InnerText?.Trim();

// 2. 取 Keywords
string keywords = doc.DocumentNode
    .SelectSingleNode("//meta[@name='keywords' or @name='Keywords']")
    ?.GetAttributeValue("content", "");

// 3. 取所有連結
var links = doc.DocumentNode.SelectNodes("//a[@href]")
    ?.Select(a => a.GetAttributeValue("href", ""))
    .Where(h => h.StartsWith("http"))
    .Distinct()
    .ToList();

// 4. 取特定結構資料(如新聞列表)
var newsItems = doc.DocumentNode.SelectNodes("//div[@class='news-list']/ul/li")
    ?.Select(li => new
    {
        Title = li.SelectSingleNode(".//h3")?.InnerText?.Trim(),
        Url = li.SelectSingleNode(".//a")?.GetAttributeValue("href", ""),
        Time = li.SelectSingleNode(".//span[@class='time']")?.InnerText?.Trim()
    })
    .ToList();

XPath 快速入門

語法含義
//div文件中所有 div 元素
//div[@class='news']class='news' 的 div
//ul/liul 直系子元素 li
//div//adiv 所有後代 a 元素
//a/@href所有 a 元素的 href 屬性值
(//table)[2]第二個 table 元素

完整爬蟲骨架


public class SimpleCrawler
{
    private readonly HashSet _visited = new();
    private readonly Queue _queue = new();

    public async Task CrawlAsync(string startUrl, int maxPages = 100)
    {
        _queue.Enqueue(startUrl);
        while (_queue.Count > 0 && _visited.Count < maxPages)
        {
            string url = _queue.Dequeue();
            if (!_visited.Add(url)) continue;

            try
            {
                string html = await FetchAsync(url);
                var doc = new HtmlDocument();
                doc.LoadHtml(html);

                // 業務解析邏輯
                ProcessPage(doc, url);

                // 發現新連結
                var links = ExtractLinks(doc, url);
                foreach (var link in links) _queue.Enqueue(link);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] {url}: {ex.Message}");
            }
            await Task.Delay(500); // 禮貌延遲
        }
    }
    // ... FetchAsync, ProcessPage, ExtractLinks 實作
}

避坑指南

  1. 尊重 robots.txt:檢查 `User-agent: *` `Disallow:` 路徑。
  2. 設定 User-Agent:模擬真實瀏覽器,避免被封鎖。
  3. 處理相對/絕對 URL:`Uri` 類別自動解析。
  4. 異常重試:`Polly` 庫實現指數退避重試。
  5. 法律合規:僅抓取公開資料,遵守網站服務條款。

爬蟲開發的核心不是「如何拿到資料」,而是「如何負責任、高效率、穩健地持續拿到資料」。掌握條件請求、編碼自動偵測、XPath 精準定位,是邁向專業爬蟲工程師的必修課。

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