.Net解析html文件類庫HtmlAgilityPack完整使用說明

weixin_34219944發表於2014-06-13

在前幾篇文章中([搜房網房產資料採集程式demo--GeckoWebBrowser例項] )都有提到一個解析html的C#類庫HtmlAgilityPack,

今天終於有時間整理一下,並把Demo分享一下。

HtmlAgilityPack是一個基於.Net的、第三方免費開源的微型類庫,主要用於在伺服器端解析html文件(在B/S結構的程式中客戶端可以用Javascript、jquery解析html)。截止到本文發表時,HtmlAgilityPack的最新版本為 1.4.6。下載地址:http://htmlagilitypack.codeplex.com/。最新版本支援Linq to Objects ( LINQ to Xml ).

準備:

如果你有安裝Nuget的話,可以直接查詢安裝即可。

下載後解壓縮後有3個檔案,這裡只需要將其中的HtmlAgilityPack.dll(程式集)、HtmlAgilityPack.xml(文件,用於Visual Studio 2008中程式碼智慧提示和幫助說明之用)引入解決方案中即可使用,無需安裝任何東西,非常好用。

在C#類檔案開頭引入using HtmlAgilityPack;就可以使用該名稱空間下的型別了。實際使用中,幾乎都是以HtmlDocument類為主線的,這一點非常類似於微軟.net framework中的XmlDocument類。XmlDocument類是操作的是xml文件,而HtmlDocument類操作的是html文件(其實也可以操作xml文件),它們的操作方式都是基於Dom,所不同的是後者取消了諸如GetElementsByTagName這樣的方法,強化了GetElementById方法(在HtmlDocument中可以直接使用,而XmlDocument則不可以)。

HtmlAgilityPack中定位節點基本上都用Xpath表示式,Xpath表示式的參考文件可見:http://www.w3school.com.cn/xpath/xpath_syntax.asp。自行學習。

不過可以先用幾個簡單就可以。比如,我們用到最多可能就是針對某個元素(div)、或者某個class屬性的div、或者某個id的div,或者以什麼開頭的div,

類似這樣的Xpath還是比較簡單的。

Xpath舉幾個例子,下面的程式碼中我們就會用到:

"//comment()"在XPath中表示“所有註釋節點”

1、獲取網頁title:doc.DocumentNode.SelectSingleNode("//title").InnerText;  解釋:XPath中“//title”表示所有title節點。SelectSingleNode用於獲取滿足條件的唯一的節點。

2、獲取所有的超連結:doc.DocumentNode.Descendants("a")

3、獲取name為kw的input,也就是相當於getElementsByName():             var kwBox = doc.DocumentNode.SelectSingleNode("//input[@name='kw']");

解釋:"//input[@name='kw']"也是XPath的語法,表示:name屬性等於kw的input標籤。

//li/h3/a[@href]:所有li下面的h3包含a超級連結有href屬性才符合。有的a可能是支援的js事件

//div[starts-with(@class,'content_single')]:所有符合條件的div,並且它的class是由字串content_single 開頭的。

//標示獲取documet下的所有符合條件。/div標示從根目錄開始的符合條件的。

 

以上是準備工作。下面說一下HtmlAgilityPack讀取web頁面,並解析的方法步驟。

1.讀取url:

 

                HtmlAgilityPack.HtmlWeb hw = new HtmlAgilityPack.HtmlWeb();

                HtmlAgilityPack.HtmlDocument doccc = hw.Load(url);//是你需要解析的url

                ArrayList ImagePtahs = GetHrefs(doccc);

這裡可能會遇到2個問題。

一個是編碼問題,一個是gzip不支援的問題。

首先編碼問題解決辦法:就是不用HtmlAgilityPack去獲取Url的data資料,自己獲取了。大家可能就問了:我自己獲取了他不給我解析那?

沒事,他不會那麼笨的。誰的肉不是吃啊?

方法如下:

 

              WebProxy proxyObject = new WebProxy(IP, port);//這裡我是用的代理。

                //向指定地址傳送請求

               HttpWebRequest HttpWReq = (HttpWebRequest)WebRequest.Create(url);

                HttpWReq.Proxy = proxyObject;

                HttpWReq.Timeout = 10000;

                HttpWebResponse HttpWResp = (HttpWebResponse)HttpWReq.GetResponse();               

                StreamReader sr = new StreamReader(HttpWResp.GetResponseStream(), System.Text.Encoding.GetEncoding("UTF-8"));              

                //注意上面的編碼了嗎??

                HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();

                doc.Load(sr);

                int res = CheckIsGoodProxy(doc); //這是我解析的函式,還沒到那一步。不解釋了。

                sr.Close();

                HttpWResp.Close();

                HttpWReq.Abort();

另一個問題就是很奇怪了。gzip的問題開啟了gzip壓縮的網頁請求時會報錯。報錯資訊為gzip”不是受支援的編碼名。

 在谷歌上搜尋了半天,終於找到解決方案,而且不必更換HttpRequest或WebClient進行請求。同時還可以用此方法設定cookie,render偽裝等等。。。
解決後程式碼如下:在你發起請求的是修改一下。

 

HtmlWeb webClient = new HtmlWeb();

HtmlAgilityPack.HtmlWeb.PreRequestHandler handler = delegate(HttpWebRequest request)

{

       request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";

       request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

       request.CookieContainer = new System.Net.CookieContainer();

       return true;

};

webClient.PreRequest += handler;

HtmlDocument doc = webClient.Load(this.getUrl());

可能最新版本的HtmlAgilityPack會修復這個問題吧。期待中。

2.用Xpath解析。

這一步就比較簡單了。就用Xpath選出你想要的資料,遍歷他們,取出他們的value即可。

例項程式碼:

 

    private ArrayList GetHrefs(HtmlAgilityPack.HtmlDocument _doc)

        {

            try

            {

                Images = new ArrayList();

                HtmlNodeCollection hrefs = _doc.DocumentNode.SelectNodes("//li/h3/a[@href]");

                   HtmlNodeCollection hrefs2 = _doc.DocumentNode.SelectNodes("//div[starts-with(@class,'content_single')]");

                if (hrefs == null)

                    return new ArrayList();

                foreach (HtmlNode href in hrefs)

                {

                    //  Images.Add(href.Attributes["src"].Value);

                    string hreff = href.Attributes["href"].Value;// 排除  博海拾貝第二百零二期】吃完薯條寂寞了 

                    string title = href.Attributes["title"].Value;

                  if (title.IndexOf("邪惡") >= 0)

                    {

                        continue;

                    }

                    if (title.IndexOf("惡搞") >= 0)

                    {

                        continue;

                    }

                    if (title.IndexOf("雷人") >= 0)

                    {

                        continue;

                    }

                    ///執行資料儲存的邏輯

                }

}

 catch (Exception ex)

            {

                ShowLogMsg("出錯了:"+ex.Message+ex.StackTrace);

                return new ArrayList();

            }

        }

 

每一個Htmlnode,你要獲取他的資料用這個方法:  img.Attributes["src"].Value

相關文章