前言
在 .NET 中解析 HTML 文件,常見做法有 HtmlAgilityPack、AngleSharp 等第三方庫。若不想引入外部依賴,可使用 Windows 內建的 mshtml (Microsoft HTML Object Library),即 IE/MSHTML 解析引擎的 COM 包裝。本文示範如何透過 WebClient 下載網頁,再用 HTMLDocumentClass 解析標題、Body 文字等資訊。
準備工作
- 專案右鍵 → 加入參考 → COM → 搜尋 Microsoft HTML Object Library → 勾選 mshtml。
- 引用後會在
bin 產生 Microsoft.mshtml.dll (Interop 組件)。
- 若部署至無 IE 的 Server Core 環境,mshtml 可能無法運作;建議改用 HtmlAgilityPack。
核心程式碼
using System;
using System.Net;
using System.Text;
using mshtml; // 命名空間
class HtmlParserDemo
{
static void Main()
{
string url = "https://www.jb51.net/web/73969.html";
// 1. 下載 HTML 內容
using (var wc = new WebClient())
{
wc.Encoding = Encoding.UTF8; // 關鍵:指定編碼,避免亂碼
string html = wc.DownloadString(url);
// 2. 建立 MSHTML 文件物件
var doc = new HTMLDocumentClass();
// 3. 關鍵設定:designMode = "on" 防止腳本執行、圖片下載、跳轉等副作用
doc.designMode = "on";
// 4. 寫入 HTML 字串
// IHTMLDocument2_write 接受 object[],需用反射或 dynamic 呼叫
// 簡易寫法:dynamic doc2 = doc; doc2.write(html);
dynamic doc2 = doc;
doc2.write(html);
// 5. 關閉寫入流
doc2.close();
// 6. 擷取資訊
Console.WriteLine("Title: " + doc.title);
Console.WriteLine("Body Text (前500字): " + doc.body?.innerText?.Substring(0, Math.Min(500, doc.body.innerText.Length)));
// 7. 進階:查詢特定元素
// var links = doc.getElementsByTagName("a");
// foreach (IHTMLElement link in links) Console.WriteLine(link.getAttribute("href"));
}
Console.ReadKey();
}
}
關鍵 API 說明
| 成員 |
型別/介面 |
用途 |
HTMLDocumentClass |
Class |
MSHTML 文件實體,實作 IHTMLDocument2、IHTMLDocument3 等介面 |
designMode |
string |
"on" = 設計模式,禁用腳本執行、資源下載;"off" = 瀏覽模式 |
write(html) |
method |
將 HTML 字串解析載入文件 |
close() |
method |
結束寫入,觸發 DOM 完成事件 |
title |
string |
文件 <title> 內容 |
body |
IHTMLElement |
<body> 元素,含 innerText、innerHTML |
getElementsByTagName |
method |
依標籤名稱取得元素集合 |
getElementById |
method |
依 ID 取得單一元素 |
進階:使用 IHTMLDocument3 的 createDocumentFromUrl (非同步載入)
// 此方法會自動下載並解析,但較難控制編碼與逾時,不建議生產環境使用
var doc3 = (IHTMLDocument3)new HTMLDocumentClass();
doc3.createDocumentFromUrl(url, null);
// 需等待 readyState == "complete",可用事件或輪詢
編碼處理最佳實務
- WebClient.Encoding = Encoding.UTF8:強制以 UTF-8 解碼位元組。
- 若網頁宣告非 UTF-8(如 Big5),需先
DownloadData 取得 byte[],再用 Encoding.GetEncoding("big5").GetString(bytes)。
- MSHTML 內部以 Unicode 儲存,寫入時已轉換完成。
替代方案比較(強烈建議)
| 方案 |
優點 |
缺點 |
推薦度 |
| HtmlAgilityPack |
純 .NET、無 COM 依賴、支援 XPath/LINQ、容錯率高、NuGet 即裝即用 |
需額外套件 |
⭐⭐⭐⭐⭐ (首選) |
| AngleSharp |
現代標準、支援 CSS Selector、DOM 完整、非同步、.NET Standard |
效能略低於 HAP |
⭐⭐⭐⭐ |
| mshtml (本文) |
零第三方套件、Windows 內建、IE 相容性最好 |
需註冊 COM、部署麻煩、非執行緒安全、Server Core 不支援、效能差 |
⭐⭐ (特定舊專案維護) |
| System.Web.HttpUtility.HtmlDecode |
僅解碼 HTML 實體,非解析器 |
無 DOM 操作 |
⭐ |
HtmlAgilityPack 快速上手(推薦替代)
// NuGet: Install-Package HtmlAgilityPack
using HtmlAgilityPack;
var web = new HtmlWeb();
var doc = web.Load(url); // 自動處理編碼、重導向、Cookie
Console.WriteLine(doc.DocumentNode.SelectSingleNode("//title").InnerText);
Console.WriteLine(doc.DocumentNode.SelectSingleNode("//body").InnerText.Substring(0, 500));
// XPath 查詢所有連結
foreach (var link in doc.DocumentNode.SelectNodes("//a[@href]"))
Console.WriteLine(link.GetAttributeValue("href", ""));
常見問題排除
- 「找不到 mshtml」/ 型別載入失敗:確認專案參考 → COM → Microsoft HTML Object Library 已勾選,且
Embed Interop Types = False (預設)。
- 中文亂碼:檢查
WebClient.Encoding 是否正確設定;若網頁無 charset meta,嘗試自動偵測 (HtmlAgilityPack 內建)。
- 腳本錯誤彈窗/卡住:務必設定
doc.designMode = "on" 在 write 之前。
- 存取
body.innerText 為 null:HTML 可能無 <body> 標籤,需先檢查 doc.body != null。
- 多執行緒呼叫:MSHTML 為 STA COM 物件,不可跨執行緒共用
HTMLDocumentClass 實例,每個執行緒需自行建立。
總結
- mshtml 提供無第三方依賴的 HTML 解析能力,適合極度受限環境、舊專案維護。
- 核心流程:
WebClient.DownloadString → new HTMLDocumentClass() → designMode="on" → write(html) → close() → 讀取 title/body.innerText。
- 新專案請直接使用 HtmlAgilityPack 或 AngleSharp,開發效能、穩定性、部署便利性全面優於 mshtml。