1. 程式人生 > >Html Agility Pack基礎類介紹及運用

Html Agility Pack基礎類介紹及運用

Html Agility Pack 原始碼中的類大概有28個左右,其實不算一個很複雜的類庫,但它的功能確不弱,為解析DOM已經提供了足夠強大的功能支援,可以跟jQuery操作DOM媲美:)

基礎類和基礎方法介紹

Html Agility Pack最常用的基礎類其實不多,對解析DOM來說,就只有HtmlDocument和HtmlNode這兩個常用的類,還有一個 HtmlNodeCollection集合類。

HtmlDocument類

當然在解析DOM前需要載入html原始檔案或者html的字串,HtmlDocument類封裝了支援此功能的方法,下面是載入html的方法介紹。

HtmlDocument類定義了多個過載的Load方法來實現以不同方式載入html,其實主要分為兩種,一種是從Stream中載入html,另外一種是從物理路徑載入html,分別見下面:

方法:public void Load(TextReader reader)
說明:從指定的 TextReader物件中載入Html
示例:

HtmlDcument doc =newHtmlDocument();StreamReader sr =File.OpenText("file path");
doc.Load(sr);

基於上面方法,衍生出了幾個不同過載方法。

以指定的Stream物件為主的有:

(1)public void Load(Stream stream) ///從指定的Stream物件中載入html;

(2)public void Load(Stream stream, bool detectEncodingFromByteOrderMarks) ///指定是否從順序位元組流中解析編碼格式

(3)public void Load(Stream stream, Encoding encoding) ///指定編碼格式

(4)public void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)

(5)public void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize)

以指定的物理路徑為主的有:

(1)public void Load(string path)

(2)public void Load(string path, bool detectEncodingFromByteOrderMarks) ///指定是否從順序位元組流中解析編碼格式

(3)public void Load(string path, Encoding encoding) ///指定編碼格式

(4)public void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks)

(5)public void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize)

HtmlDocument類中還定義了直接從html字串中載入Html,如下:

方法:public void LoadHtml(string html)
說明:從指定的html字串中載入html
示例:

HtmlDocument doc =newHtmlDocument();string html ="<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span></div>";
doc.LoadHtml(html);

HtmlDocument類還有其他寫DOM方法的定義,這裡不作詳細介紹,留作以後專門介紹Html Agility Pack寫DOM章節介紹吧,這裡著重介紹Html Agility pack解析DOM的細節。

HtmlNode類和HtmlNodeCollection類

通過HtmlDocument把html載入進來後,接著是要做什麼呢?當然是對html解析了,解析DOM就需要提到HtmlNode類了。 HtmlDocument類由屬性DocumentNode屬性返回當前Html解析後的一個全域性的HtmlNode物件;如果想獲取某一個元素的 HtmlNode,可以通過HtmlDocument類的GetElementbyId(string Id)方法來獲取,返回指定某一個html元素的HtmlNode物件。如何通過HtmlNode物件來訪問DOM呢?介紹之前先對它的功能瞭解下。

HtmlNode類實現了IXPathNavigable介面,這說明了它可以通過xpath來查詢DOM了,如果對System.Xml名稱空間下的 XmlDocument類瞭解的,特別是使用過了SelectNodes()和SelectSingleNode()方法的朋友對使用HtmlNode類將會很熟悉。其實Html Agility Pack內部是把html解析成xml文件格式了的,所以支援xml中的一些常用查詢方式。下面對HtmlNode的一些主要的常用成員作簡要的說明。

HtmlNode類的主要屬性

1)Attributes屬性

獲取當前Html元素的屬性的集合,返回的是一個HtmlAttributeCollection物件。如一個div元素,它可能會定義一些屬性,如:<div id="title" name="title" class="class-name" title="title div">***</div>,那Attributes返回的HtmlAttributeCollection就包含了 “id,name,class,title”的資訊。HtmlAttributeCollection類是實現了介面 IList<HtmlAttribute>的一個集合類,故此可以通過下面程式碼方式訪問每一個成員。

HtmlNode node = doc.GetElementbyId("title");string titleValue = node.Attributes["title"].Value;

或者

foreach(HtmlAttribute attr in node.Attributes){Console.WriteLine("{0}={1}",attr.Name,attr.Value);}

在獲取屬性值時,如果某一個屬性名稱不存在的話,Attributes["name"]返回的是null值。

2)FirstChild,LastChild,ChildNodes,ParentNode屬性

FirstChild屬性:返回所有子節點的第一個節點,如下面程式碼:

string html ="<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span><div id="innerDiv">inner div</div></div>";

FirstChild則返回的是“<span style="color:red;"><h1>Hello World!</h1></span>” 的節點。

LastChild屬性:返回所有子節點的最後一個節點,以上面的html為例,則返回“<div id="innerDiv">inner div</div>”節點。

ChildNodes屬性:返回當前節點所有直接一代的子節點的集合,不包括跨代子節點,以上面的html為例,則返回“<span style="color:red;"><h1>Hello World!</h1></span>” 和“<div id="innerDiv">inner div</div>”兩個節點。

ParentNode屬性:返回當前節點的直接父節點。

3)獲取Html原始碼和文字

HtmlNode類設計了OuterHtml屬性和InnerHtml屬性用於獲取當前節點的Html原始碼。兩者不同之處是,OuterHtml屬性返回的是包含當前節點的Html程式碼在內的所有Html程式碼,而InnerHtml屬性返回的是當前節點裡面子節點的所有Html程式碼。如下面:

程式碼

HtmlDocument doc =newHtmlDocument();string html ="<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span></div>";
doc.LoadHtml(html);HtmlNode node = doc.HtmlDocument;Console.WriteLine(node.OuterHtml);/// return "<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span></div>";Console.WriteLine(node.InnerHtml);/// return "<span style="color:red;"><h1>Hello World!</h1></span>";

如要獲取節點的文字值,通過InnerText屬性來獲取,InnerText屬性過濾掉了所有的Html標記程式碼,只返回文字值,如下面:

Console.WriteLine(node.InnerText);/// return "Hello World!";

HtmlNode類的主要方法

HtmlNode類提供了足夠豐富的方法供查詢當前節點下的子節點(元素),當然也包括查詢當前節點的父節點(元素)的方法,下面列出主要的方法和使用說明。

獲取父節點的系列方法:

1)public IEnumerable<HtmlNode> Ancestors()

獲取當前節點的父節點列表(不包含自身)。

2)public IEnumerable<HtmlNode> Ancestors(string name)

以指定一個名稱來獲取父節點的列表(不包含自身)。

3)public IEnumerable<HtmlNode> AncestorsAndSelf()

獲取當前節點的父節點列表(包含自身)。

4)public IEnumerable<HtmlNode> AncestorsAndSelf(string name)

以指定一個名稱來獲取父節點的列表(包含自身)。

獲取子節點的系列方法:

1)public IEnumerable<HtmlNode> DescendantNodes()

獲取當前節點下的所有子節點的列表,包括子節點的子節點(不包含自身)。

2)public IEnumerable<HtmlNode> DescendantNodesAndSelf()

獲取當前節點下的所有子節點的列表,包括子節點的子節點(包含自身)。

3)public IEnumerable<HtmlNode> Descendants()

獲取當前節點下的直接子節點的列表(不包含自身)。

4)public IEnumerable<HtmlNode> DescendantsAndSelf()

獲取當前節點下的直接子節點的列表(包含自身)。

5)public IEnumerable<HtmlNode> Descendants(string name)

獲取當前節點下的以指定名稱的子節點列表。

6)public IEnumerable<HtmlNode> DescendantsAndSelf(string name)

獲取當前節點下的以指定名稱的子節點的列表(包含自身)。

7)public HtmlNode Element(string name)

獲取第一個符合指定名稱的直接子節點的節點元素。

8)public IEnumerable<HtmlNode> Elements(string name)

獲取符合指定名稱的所有直接子節點的節點列表。

9)public HtmlNodeCollection SelectNodes(string xpath)

獲取符合指定的xpath的子節點列表。

10)public HtmlNode SelectSingleNode(string xpath)

獲取符合指定的xpath的單個位元組點元素。

查詢節點的方法主要是上面10個方法,該類還有其他寫節點的系列方法,這裡不詳細介紹寫操作的方法,留作以後詳細介紹。

結合Xpath進行查詢節點是功能比較強大,這像操作xml那樣方便。

簡單例子的程式碼

下面例子是把部落格園的精華區部落格列表查詢出來。

usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.IO;usingHtmlAgilityPack;namespaceDemoCnBlogs{classProgram{staticvoidMain(string[] args){HtmlWeb web =newHtmlWeb();HtmlDocument doc = web.Load("http://www.cnblogs.com/pick/");HtmlNode node = doc.GetElementbyId("post_list");StreamWriter sw =File.CreateText("log.txt");foreach(HtmlNode child in node.ChildNodes){if(child.Attributes["class"]==null|| child.Attributes["class"].Value!="post_item")continue;HtmlNode hn =HtmlNode.CreateNode(child.OuterHtml);///如果用child.SelectSingleNode("//*[@class=\"titlelnk\"]").InnerText這樣的方式查詢,是永遠以整個document為基準來查詢,///這點就不好,理應以當前child節點的html為基準才對。Write(sw,String.Format("推薦:{0}", hn.SelectSingleNode("//*[@class=\"diggnum\"]").InnerText));Write(sw,String.Format("標題:{0}", hn.SelectSingleNode("//*[@class=\"titlelnk\"]").InnerText));Write(sw,String.Format("介紹:{0}", hn.SelectSingleNode("//*[@class=\"post_item_summary\"]").InnerText));Write(sw,String.Format("資訊:{0}", hn.SelectSingleNode("//*[@class=\"post_item_foot\"]").InnerText));Write(sw,"----------------------------------------");}

      sw.Close();Console.ReadLine();}staticvoidWrite(StreamWriter writer,string str){Console.WriteLine(str);
      writer.WriteLine(str);}}}