初次接觸網頁爬蟲開發時,往往會因為資訊龐雜而感到無從下手。網路上雖有大量教學資源,但真正可直接套用、解決實際問題的程式碼卻不易尋覽。本文將以實戰經驗為基礎,帶領讀者從零開始,完成「抓取網頁 HTML 原始碼」並「解析特定節點超連結」的核心功能,助你少走彎路、快速上手。
假設目標網頁結構包含 <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);
}
原始碼使用 Encoding.Default 易導致中文亂碼。實務上建議:
response.CharacterSet 取得編碼Encoding.UTF-8 或 Encoding.GetEncoding("gb2312")HtmlAgilityPack 自動偵測編碼本範例採用 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();
程式碼已採用 using 區塊確保串流正確關閉,實務上尚需補充:
try-catch 捕捉 WebException、IOExceptionrequest.Timeout 避免長時間阻塞User-Agent 模擬瀏覽器行為HttpClient 搭配 await 提升併發效能HashSet<string> 或 Bloom Filter 過濾重複 URLrobots.txt、設定請求間隔、處理 301/302 重導向掌握「抓取→解析→存儲」這三步核心流程,即可奠定爬蟲開發的堅實基礎。建議讀者先跑通本範例,再逐步引入非同步、解析器、分散式佇列等進階架構,打造穩健可維護的爬蟲系統。