天天看點

Html解析神器 HtmlAgilityPack

 HtmlAgilityPack是.net下的一個HTML解析類庫。支援用XPath來解析HTML。這個意義不小,為什麼呢?因為對于頁面上的元素的xpath某些強大的浏覽器能夠直接擷取得到,并不需要手動寫。節約了大半寫正規表達式的時間,當然正規表達式有時候在進一步擷取的時候還需要寫,但是通過xpath解析之後,正規表達式已經要比對的範圍已經非常小了。而且,不用正規表達式在整個頁面源代碼上比對,速度也會有提升。總而言之,通過該類庫,先通過浏覽器擷取到xpath擷取到節點内容然後再通過正規表達式比對到所需要的内容,無論是開發速度,還是運作效率都有提升。

  HtmlAttribute  對應  Html元素的屬性

  HtmlAttributeCollection  一個元素屬性的集合,實作了IList<HtmlAttribute>, ICollection<HtmlAttribute>, IEnumerable<HtmlAttribute>, IEnumerable,都是集合的那一套東西,沒有新東西。

  HtmlNode    對應  HTML節點,包括注釋,文本,元素等

  HtmlNodeCollection  一個HtmlNode節點集合,實作了HtmlNodeCollection : IList<HtmlNode>, ICollection<HtmlNode>, IEnumerable<HtmlNode>, IEnumerable繼承了這些東西就沒什麼需要說的了,都是集合的東西,沒有新的東西。完全是集合那一套。

  HtmlNodeType  一個枚舉  表示節點的類型,文檔,注釋,元素,文本。

  HtmlTextNode  對應Html文本節點,很簡單的一個類,繼承自HtmlNode。

  HtmlEntity   對應實體   實用程式類以替換特殊字元的實體,反之亦然

  HtmlParseError   表示文檔在解析過程中發現的解析錯誤。

  還有一些其他的類,留到以後有用過的時候再補充。

HtmlAgilityPack 之 HtmlNode類

HtmlAgilityPack中的HtmlNode類與XmlNode類差不多,提供的功能也大同小異。下面來看看該類提供功能。

一、靜态屬性

public static Dictionary<string, HtmlElementFlag> //ElementsFlags;擷取集合的定義為特定的元素節點的特定行為的标志。表包含小寫标記名稱作為鍵和作為值的 HtmlElementFlags 組合 DictionaryEntry 清單。
public static readonly string HtmlNodeTypeNameComment;  //擷取一個注釋節點的名稱。實際上,它被定義為 '#comment
public static readonly string HtmlNodeTypeNameDocument;   //擷取文檔節點的名稱。實際上,它被定義為 '#document'
public static readonly string HtmlNodeTypeNameText;      //擷取一個文本節點的名稱。實際上,它被定義為 '#text'      

二、屬性

Attributes             擷取節點的屬性集合

ChildNodes            擷取子節點集合(包括文本節點)

Closed              該節點是否已關閉(</xxx>)

ClosingAttributes          在關閉标簽的屬性集合

FirstChild              擷取第一個子節點

HasAttributes            判斷該節點是否含有屬性

HasChildNodes          判斷該節點是否含有子節點

HasClosingAttributes        判斷該節點的關閉标簽是否含有屬性(</xxx class="xxx">)

Id                 擷取該節點的Id屬性

InnerHtml             擷取該節點的Html代碼

InnerText             擷取該節點的内容,與InnerHtml不同的地方在于它會過濾掉Html代碼,而InnerHtml是連Html代碼一起輸出

LastChild              擷取最後一個子節點

Line                擷取該節點的開始标簽或開始代碼位于整個HTML源代碼的第幾行(行号)

LinePosition            擷取該節點位于第幾列

Name                Html元素名

NextSibling            擷取下一個兄弟節點

NodeType              擷取該節點的節點類型

OriginalName           擷取原始的未經更改的元素名

OuterHtml             整個節點的代碼

OwnerDocument         節點所在的HtmlDocument文檔

ParentNode            擷取該節點的父節點

PreviousSibling          擷取前一個兄弟節點

StreamPosition          該節點位于整個Html文檔的字元位置

XPath                根據節點傳回該節點的XPath

代碼示例:

static void Main(string[] args)
        {
            //<ul class="user_match clear">
            //    <li>年齡:21~30之間</li>
            //    <li>婚史:未婚</li>
            //    <li>地區:不限</li>
            //    <li>身高:175~185厘米之間</li>
            //    <li>學曆:不限</li>
            //    <li>職業:不限</li>
            //    <li>月薪:不限</li>
            //    <li>住房:不限</li>
            //    <li>購車:不限</li>
            //</ul>


            WebClient wc = new WebClient();
            wc.BaseAddress = "http://www.juedui100.com/";
            wc.Encoding = Encoding.UTF8;
            HtmlDocument doc = new HtmlDocument();
            string html = wc.DownloadString("user/6971070.html");
            doc.LoadHtml(html);
            HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]");     //根據XPath查找節點,跟XmlNode差不多
            Console.WriteLine(node.InnerText);  //輸出節點内容      年齡:21~30之間 婚史:未婚 ......      與InnerHtml的差別在于,它不會輸出HTML代碼
            Console.WriteLine(node.InnerHtml);  //輸出節點Html <li>年齡:21~30之間</li> <li>婚史:未婚</li> ....
            Console.WriteLine(node.Name);       //輸出 ul    Html元素名 

            HtmlAttributeCollection attrs = node.Attributes;
            foreach(var item in attrs)
            {
                Console.WriteLine(item.Name + " : " + item.Value);    //輸出 class :user_match clear
            }

            HtmlNodeCollection CNodes = node.ChildNodes;    //所有的子節點
            foreach (HtmlNode item in CNodes)
            {
                Console.WriteLine(item.Name + "-" + item.InnerText);  //輸出 li-年齡:21~30之間#text-   li-婚史:未婚#text-     .......  别忘了文本節點也算
            }

            Console.WriteLine(node.Closed);     //輸出True    //目前的元素節點是否已封閉

            Console.WriteLine("================================");

            HtmlAttributeCollection attrs1 = node.ClosingAttributes;    //擷取在結束标記的 HTML 屬性的集合。  例如</ul class="">
            Console.WriteLine(attrs1.Count);    //輸出0

            HtmlNode node1 = node.FirstChild;   //悲劇了ul的第一個節點是一個 \n 換行文本節點 第二個節點才到第一個li
            Console.WriteLine(node1.NodeType);  //輸出Text 文本節點
            HtmlNode node3 = node.LastChild;    //同樣最後一個節點一樣是 \n 文本節點
            Console.WriteLine(node3.NodeType);  //輸出Text 文本節點

            HtmlNode node2 = node.SelectSingleNode("child::li[1]");     //擷取目前節點的第一個子li節點
            Console.WriteLine(node2.XPath);     //根據節點生成XPath表達式  /html/body/div[4]/div[1]/div[2]/ul[1]/li[1]   媽了個B,強大

            Console.WriteLine(node.HasAttributes);          //輸出 True   判斷節點是否含有屬性
            Console.WriteLine(node.HasChildNodes);          //輸出 True   判斷節點是否含有子節點
            Console.WriteLine(node.HasClosingAttributes);   //False     判斷節點結束标記是否含有屬性
            
            Console.WriteLine(node.Line);           //輸出 155  該節點開始标記位于頁面代碼的第幾行
            Console.WriteLine(node.LinePosition);   //輸出 1   該節點開始标記位于第幾列2
            Console.WriteLine(node.NodeType);       //輸出 Element   該節點類型 此處為元素節點            
            Console.WriteLine(node.OriginalName);   //輸出 ul
            HtmlNode node4 = node.SelectSingleNode("child::li[1]");
            Console.WriteLine(node4.InnerText);     //輸出 年齡:21~30之間
            HtmlNode node5 = node4.NextSibling.NextSibling;     //擷取下一個兄弟元素 因為有一個換行符的文本節點,是以要兩次,跳過換行那個文本節點
            Console.WriteLine(node5.InnerText);     //輸出 婚史:未婚
            HtmlNode node6 = node5.PreviousSibling.PreviousSibling;     //同樣兩次以跳過換行文本節點
            Console.WriteLine(node6.InnerText);     //輸出 年齡:21~30之間
            HtmlNode node7 = node6.ParentNode;      //擷取父節點
            Console.WriteLine(node7.Name);          //輸出 ul
            string str = node.OuterHtml;
            Console.WriteLine(str);     //輸出整個ul代碼class="user_match clear"><li>年齡:21~30之間</li>...</ul>
            Console.WriteLine(node.StreamPosition); //輸出7331    擷取此節點的流位置在文檔中,相對于整個文檔(Html頁面源代碼)的開始。
            HtmlDocument doc1 = node.OwnerDocument;
            doc1.Save(@"D:\123.html");

            HtmlNode node8 = doc.DocumentNode.SelectSingleNode("//*[@id=\"coll_add_aid59710701\"]");
            //<a id="coll_add_aid59710701" style="display:block" class="coll_fix needlogin" href="javascript:coll_add(5971070)">收藏</a>
            Console.WriteLine(node8.Id);    //輸出 coll_add_aid59710701   擷取Id屬性的内容

            Console.ReadKey();
        }      

三、方法

IEnumerable<HtmlNode> Ancestors();               傳回此元素的所有上級節點的集合。

IEnumerable<HtmlNode> Ancestors(string name);           傳回此元素參數名字比對的所有上級節點的集合。

IEnumerable<HtmlNode> AncestorsAndSelf();            傳回此元素的所有上級節點和自身的集合。

IEnumerable<HtmlNode> AncestorsAndSelf(string name);      傳回此元素的名字比對的所有上級節點和自身的集合。

HtmlNode AppendChild(HtmlNode newChild);              将參數元素追加到為調用元素的子元素(追加在最後)

void AppendChildren(HtmlNodeCollection newChildren);       将參數集合中的元素追加為調用元素的子元素(追加在最後)

HtmlNode PrependChild(HtmlNode newChild);              将參數中的元素作為子元素,放在調用元素的最前面

void PrependChildren(HtmlNodeCollection newChildren);       将參數集合中的所有元素作為子元素,放在調用元素前面

static bool CanOverlapElement(string name);             确定是否可以儲存重複的元素

IEnumerable<HtmlAttribute> ChildAttributes(string name);     擷取所有子元素的屬性(參數名要與元素名比對)

HtmlNode Clone();                          本節點克隆到一個新的節點

HtmlNode CloneNode(bool deep);                  節點克隆到一個新的幾點,參數确定是否連子元素一起克隆

HtmlNode CloneNode(string newName);               克隆的同時更改元素名

HtmlNode CloneNode(string newName, bool deep);          克隆的同時更改元素名。參數确定是否連子元素一起克隆

void CopyFrom(HtmlNode node);                   建立重複的節點和其下的子樹。

void CopyFrom(HtmlNode node, bool deep);             建立節點的副本。

XPathNavigator CreateNavigator();                  傳回的一個對于此文檔的XPathNavigator

static HtmlNode CreateNode(string html);               靜态方法,允許用字元串建立一個新節點

XPathNavigator CreateRootNavigator();               建立一個根路徑的XPathNavigator

IEnumerable<HtmlNode> DescendantNodes();            擷取所有子代節點

IEnumerable<HtmlNode> DescendantNodesAndSelf();        擷取所有的子代節點以及自身

IEnumerable<HtmlNode> Descendants();              擷取枚舉清單中的所有子代節點

IEnumerable<HtmlNode> Descendants(string name);        擷取枚舉清單中的所有子代節點,注意元素名要與參數比對

IEnumerable<HtmlNode> DescendantsAndSelf();          擷取枚舉清單中的所有子代節點以及自身

IEnumerable<HtmlNode> DescendantsAndSelf(string name);    擷取枚舉清單中的所有子代節點以及自身,注意元素名要與參數比對

HtmlNode Element(string name);                   根據參數名擷取一個元素

IEnumerable<HtmlNode> Elements(string name);          根據參數名擷取比對的元素集合

bool GetAttributeValue(string name, bool def);            幫助方法,用來擷取此節點的屬性的值(布爾類型)。如果未找到該屬性,則将傳回預設值。

int GetAttributeValue(string name, int def);              幫助方法,用來擷取此節點的屬性的值(整型)。如果未找到該屬性,則将傳回預設值。

string GetAttributeValue(string name, string def);          幫助方法,用來擷取此節點的屬性的值(字元串類型)。如果未找到該屬性,則将傳回預設值。

HtmlNode InsertAfter(HtmlNode newChild, HtmlNode refChild);     将一個節點插入到第二個參數節點的後面,與第二個參數是兄弟關系

HtmlNode InsertBefore(HtmlNode newChild, HtmlNode refChild);   講一個節點插入到第二個參數節點的後面,與第二個參數是兄弟關系

static bool IsCDataElement(string name);               确定是否一個元素節點是一個 CDATA 元素節點。

static bool IsClosedElement(string name);              确定是否封閉的元素節點

static bool IsEmptyElement(string name);                确定是否一個空的元素節點。

static bool IsOverlappedClosingElement(string text);          确定是否文本對應于一個節點可以保留重疊的結束标記。

void Remove();                            從父集合中移除調用節點

void RemoveAll();                           移除調用節點的所有子節點以及屬性

void RemoveAllChildren();                       移除調用節點的所有子節點

HtmlNode RemoveChild(HtmlNode oldChild);              移除調用節點的指定名字的子節點

HtmlNode RemoveChild(HtmlNode oldChild, bool keepGrandChildren);移除調用節點調用名字的子節點,第二個參數确定是否連孫子節點一起移除

HtmlNode ReplaceChild(HtmlNode newChild, HtmlNode oldChild);   将調用節點原有的一個子節點替換為一個新的節點,第二個參數是舊節點

HtmlNodeCollection SelectNodes(string xpath);           根據XPath擷取一個節點集合

HtmlNode SelectSingleNode(string xpath);              根據XPath擷取唯一的一個節點

HtmlAttribute SetAttributeValue(string name, string value);      設定調用節點的屬性

string WriteContentTo();                        将該節點的所有子級都儲存到一個字元串中。

void WriteContentTo(TextWriter outText);              将該節點的所有子級都儲存到指定的 TextWriter。

string WriteTo();                           将目前節點儲存到一個字元串中。

void WriteTo(TextWriter outText);                  将目前節點儲存到指定的 TextWriter。

void WriteTo(XmlWriter writer);                     将目前節點儲存到指定的則 XmlWriter。

  示例代碼:

static void Main(string[] args)
        {
            //<ul class="user_match clear">
            //    <li>年齡:21~30之間</li>
            //    <li>婚史:未婚</li>
            //    <li>地區:不限</li>
            //    <li>身高:175~185厘米之間</li>
            //    <li>學曆:不限</li>
            //    <li>職業:不限</li>
            //    <li>月薪:不限</li>
            //    <li>住房:不限</li>
            //    <li>購車:不限</li>
            //</ul>


            WebClient wc = new WebClient();
            wc.BaseAddress = "http://www.juedui100.com/";
            wc.Encoding = Encoding.UTF8;
            HtmlDocument doc = new HtmlDocument();
            string html = wc.DownloadString("user/6971070.html");
            doc.LoadHtml(html);
            HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]");     //根據XPath查找節點,跟XmlNode差不多

            IEnumerable<HtmlNode> nodeList = node.Ancestors();  //擷取該元素所有的父節點的集合
            foreach (HtmlNode item in nodeList)
            {
                Console.Write(item.Name + " ");   //輸出 div div body html #document
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList1 = node.Ancestors("body");  //擷取名字比對的該元素的父集合,其實參數就是一個篩選的功能
            foreach (HtmlNode item in nodeList1)
            {
                Console.Write(item.Name + " ");   //輸出 body
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList2 = node.AncestorsAndSelf();  //擷取所有的父節點和自身
            foreach (HtmlNode item in nodeList2)
            {
                Console.Write(item.Name + " "); //輸出 ul div div div body html #document
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList3 = node.AncestorsAndSelf("div");     //擷取父節點和自身,參數用于篩選
            foreach (HtmlNode item in nodeList3)
            {
                Console.Write(item.Name + " "); //輸出 div div div
            }
            Console.WriteLine();

            HtmlNode node1 = doc.CreateElement("li");
            node1.InnerHtml = "我是附加的li元素";
            node.AppendChild(node1);    //...<li>購車:不限</li> 後面加了一個<li>我是附加的li元素</li>
            Console.WriteLine(node.InnerHtml);


            HtmlNode node2 = doc.CreateElement("li");
            node2.InnerHtml = "新li一";
            HtmlNode node3 = doc.CreateElement("li");
            node3.InnerHtml = "新li二";
            HtmlNodeCollection nc = new HtmlNodeCollection(node2);
            nc.Add(node2);
            nc.Add(node3);
            node.AppendChildren(nc);    //一次過追加多個元素
            Console.WriteLine(node.InnerHtml);      //...<li>我是附加的li元素</li><li>新li一</li><li>新li二</li>

            Console.WriteLine(HtmlNode.CanOverlapElement("node2"));     //輸出False   确定是否可以儲存一個重複的元素

            IEnumerable<HtmlAttribute> attrs = node.ChildAttributes("class");   //擷取子節點與自身的所有名為class的屬性集合
            foreach (HtmlAttribute attr in attrs)
            {
                Console.Write(attr.Value);      //輸出 user_match clear 
            }

            HtmlNode node4 = node.Clone();
            Console.WriteLine(node4.InnerHtml);     //輸出node的代碼,node已被複制到了node

            HtmlNode node5 = node.CloneNode(false); //參數決定是否複制子節點,與XmlNode一樣
            Console.WriteLine(node5.OuterHtml);     //<ul class="user_match clear"></ul>    因為參數設為了false子節點沒有被複制

            HtmlNode node6 = node.CloneNode("div");    //複制節點的同時,更改名字
            Console.WriteLine(node6.OuterHtml);        //輸出 <div class="user_match clear"><li>年齡:21~30之間</li>...</div>  ul已被改為了div

            HtmlNode node7 = node.CloneNode("table",false);
            Console.WriteLine(node7.OuterHtml);        //輸出<table class="user_match clear"></table>     參數為false是以沒有複制子節點

            HtmlNode node8 = node.SelectSingleNode("child::li[1]");
            node.CopyFrom(node);
            Console.WriteLine(node.OuterHtml);
            Console.WriteLine("========================");
            //public void CopyFrom(HtmlNode node);
            //public void CopyFrom(HtmlNode node, bool deep);
            //public XPathNavigator CreateNavigator();
            //public XPathNavigator CreateRootNavigator();

            HtmlNode node9 = HtmlNode.CreateNode("<li>新節點</li>");   //直接用字元串建立節點,還是挺好用的
            Console.WriteLine(node9.OuterHtml);     //輸出 <li>新節點</li>

            IEnumerable<HtmlNode> nodeList4 = node.DescendantNodes();   //擷取所有的子節點集合
            foreach (HtmlNode item in nodeList4)
            {
                Console.Write(item.OuterHtml);      //輸出 node的每個子li節點
            }
            Console.WriteLine("===================");

            IEnumerable<HtmlNode> nodeList5 = node.DescendantNodesAndSelf();
            foreach (HtmlNode item in nodeList5)
            {
                Console.Write(item.OuterHtml);      //輸出自身<ul>..包括子節點<li>...</li></ul> 再輸出所有的子li節點
            }
            Console.WriteLine();

            IEnumerable<HtmlNode> nodeList6 = node.DescendantNodes();   //擷取枚舉清單中的所有子代節
            foreach (HtmlNode item in nodeList6)
            {
                Console.Write(item.InnerText);  //輸出所有的li節點的内容
            }
            Console.WriteLine("---------------");

            IEnumerable<HtmlNode> nodeList7 = node.Descendants("li");   //擷取所有的子後代元素    //文本節點不在此範圍内
            foreach(HtmlNode item in nodeList7)
            {
                Console.Write(item.InnerText);   
            }

            IEnumerable<HtmlNode> nodeList8 = node.DescendantsAndSelf("ul");   //擷取所有的子後代元素    //文本節點不在此範圍内
            foreach (HtmlNode item in nodeList8)
            {
                Console.Write(item.Name);       //輸出 ul 參數實際上隻相當于過濾的作用
            }

            HtmlNode node10 = node.Element("li");   //擷取第一個子節點名稱比對的元素
            Console.WriteLine(node10.InnerText);        //輸出 年齡:年齡:21~30之間
            Console.WriteLine("----------------------------------------");

            IEnumerable<HtmlNode> nodeList9 = node.Elements("li");
            foreach (HtmlNode item in nodeList9)
            {
                Console.Write(item.InnerText);      //輸出 所有的li節點内容
            }
            Console.WriteLine();

            //換一個新的,好像有點亂了
            HtmlNode newnode = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[3]");
            //<div class="col say">
            //    <h3>愛情獨白</h3>
            //    <p>願得一心人,白首不相離。我一直相信我的另一半就在茫茫人海中,有一天一定會與我相遇。</p>
            //</div>

            //bool b = newnode.GetAttributeValue("class", false);   //擷取一個布爾值的屬性,沒有找到則傳回第二個參數的預設值
            //Console.WriteLine(b);
            //int i = newnode.GetAttributeValue("class", 0);        //擷取一個整形的屬性,沒有找到則傳回第二個參數的預設值
            //Console.WriteLine(i);

            string str = newnode.GetAttributeValue("class", "");    //擷取一個字元串屬性
            Console.WriteLine(str); //輸出 col say

            HtmlNode node11 = HtmlNode.CreateNode("<b>我是加粗節點</b>");
            HtmlNode node12 = newnode.SelectSingleNode("h3");
            newnode.InsertAfter(node11, node12);    //意思是在node12代表的h3節點後面插入node11節點
            Console.WriteLine(newnode.InnerHtml);   //h3>愛情獨白</h3><b>我是加粗節點</b><p>願得一心人...      留意到b節點已經被插入到h3後面

            newnode.InsertBefore(node11, node12);   //再插入多一次,方法不同罷了,這次是在node12帶包的h3前面插入
            Console.WriteLine(newnode.InnerHtml);   //<b>我是加粗節點</b><h3>愛情獨白</h3><b>我是加粗節點</b><p>願得一心人

            Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            newnode.RemoveChild(node11);    //移除了第一個<b>我是加粗節點</b>   此方法的重載,第二個參數決定是否移除孫子節點
            Console.WriteLine(newnode.InnerHtml);   //<h3>愛情獨白</h3><b>我是加粗節點</b><p>願得一心人....

            newnode.RemoveAllChildren();        //移除所有子節點
            Console.WriteLine(newnode.OuterHtml);   //<div class="col say"></div>   所有子節點都被移除了

            newnode.RemoveAll();                    //移除所有的屬性和子節點,由于子節點已經被上個方法移除了,是以這次連屬性也移除了
            Console.WriteLine(newnode.OuterHtml);   //輸出 <div></div>    注意到屬性也被移除了。

            //都移除光了,再來一個,還是剛才那個
            HtmlNode newnode1 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[3]");
            Console.WriteLine("===================");
            Console.WriteLine(newnode1.OuterHtml);  //輸出 <div></div>    注意 移除是從HtmlDocument中移除的,再次擷取擷取不到了

            HtmlNode newnode2 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/div[2]/p");
            Console.WriteLine(newnode2.OuterHtml);
            //<p class="no_tip">她還沒有設定不能忍受清單 
            //    <a href="javascript:invite(5971070,8,'邀請設定不能忍受');" class="link_b needlogin">邀請她設定</a>
            //</p>
            newnode2.Remove();    //從文檔樹中移除newnode2節點
            HtmlNode newnode3 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/div[2]/p");   //再次擷取該節點
            //Console.WriteLine(newnode3.OuterHtml);  //報未将對象引用到對象的執行個體異常,明顯是找不到了,

            HtmlNode newnode4 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[1]/div/div[1]/p[2]/b[1]");
            Console.WriteLine(newnode4.OuterHtml);
            //<b>相冊:
            //    <a href="/photo/6971070.html" class="red">4</a>張
            //</b>
            HtmlNode node17 = HtmlNode.CreateNode("<div>再次建立一個節點</div>");
            newnode4.PrependChild(node17);      //跟AppengChild類似,隻是插入位置不同PrependChildren接受一個節點集合,一次過插入多個節點而已
            Console.WriteLine(newnode4.OuterHtml);  
            //輸出
            //<b>相冊:
            //    <div>再次建立一個節點</div>
            //    <a href="/photo/6971070.html" class="red">4</a>張
            //</b>
            HtmlNode node16 = newnode4.SelectSingleNode("child::a[1]");
            HtmlNode node18 = HtmlNode.CreateNode("<p>建立一行</p>");
            newnode4.ReplaceChild(node18, node16);
            Console.WriteLine(newnode4.OuterHtml);
            //輸出
            //<b>相冊:
            //    <div>再次建立一個節點</div>
            //    <p>建立一行</p>張      //留意到node16代表得節點已經被替換掉了
            //</b>

            HtmlNode node19 = newnode4.SelectSingleNode("child::p[1]");
            node19.SetAttributeValue("class","class1");
            Console.WriteLine(node19.OuterHtml);    //輸出 <p class="class1">建立一行</p>

            Console.WriteLine(HtmlNode.IsOverlappedClosingElement("<a>我愛你</a>"));   //輸出 False
            Console.WriteLine(HtmlNode.IsCDataElement("<a>我愛你</a>"));   //輸出 False
            Console.WriteLine(HtmlNode.IsClosedElement("<a>我愛你</a>"));   //輸出 False
            Console.WriteLine(HtmlNode.IsEmptyElement("<a>我愛你</a>"));   //輸出 False
            Console.WriteLine(newnode4.OuterHtml);


            HtmlNode node20 = HtmlNode.CreateNode("<p>新的第二行</p>");
            newnode4.AppendChild(node20);
            HtmlNodeCollection hnc = newnode4.SelectNodes("//p");   //根據XPath一次過擷取多個Node
            Console.WriteLine(hnc.Count);   //輸出29

            string str1 = node20.WriteContentTo();
            Console.WriteLine(str1);    //輸出 新的第二行  将節點内容寫入字元串

            //public void WriteContentTo(TextWriter outText);
            //public string WriteTo();
            //public void WriteTo(TextWriter outText);
            //public void WriteTo(XmlWriter writer);
            Console.ReadKey();
        }      

HtmlNodeType枚舉

HtmlNodeType是一個枚舉,用于說明一個節點的類型。

源代碼如下所示:

public enum HtmlNodeType
    {
        Document = 0,
        Element = 1,
        Comment = 2,
        Text = 3,
    }      

1、Document  是文檔

2、Element     是元素節點

3、Conment   是注釋節點

4、Text      是文本節點

 HtmlAttribute HTML屬性處理類

  HtmlAttribute 在HtmlAgilityPack扮演的是一個HTML代碼屬性的容器,同時提供了用于處理HTML屬性的一些功能。

一、屬性

int Line { get; }           擷取文檔中的此屬性的行數。

int LinePosition { get; }       擷取文檔中此屬性所在列數

string Name { get; set; }       目前屬性的名稱

string OriginalName { get; }     目前屬性未經更改的屬性

HtmlDocument OwnerDocument { get; }    傳回目前屬性所在的文檔引用

HtmlNode OwnerNode { get; }    目前屬性所在節點的引用

AttributeValueQuote QuoteType { get; set; } 傳回一個枚舉值,訓示屬性包裝在單引号裡還是雙引号裡

int StreamPosition { get; }      此屬性開始位置位于整個文檔的字元位置

string Value { get; set; }      此屬性的值

string XPath { get; }         傳回屬性的通路XPath表達式

二、方法

HtmlAttribute Clone();        克隆到另外一個HttpAttribute

int CompareTo(object obj);    将目前執行個體與另一個屬性進行比較。比較基于屬性的名稱。

void Remove();           從文檔中移除該屬性

static void Main(string[] args)
        {
            //<ul class="user_match clear">
            //    <li>年齡:21~30之間</li>
            //    <li>婚史:未婚</li>
            //    <li>地區:不限</li>
            //    <li>身高:175~185厘米之間</li>
            //    <li>學曆:不限</li>
            //    <li>職業:不限</li>
            //    <li>月薪:不限</li>
            //    <li>住房:不限</li>
            //    <li>購車:不限</li>
            //</ul>

            WebClient wc = new WebClient();
            wc.BaseAddress = "http://www.juedui100.com/";
            wc.Encoding = Encoding.UTF8;
            HtmlDocument doc = new HtmlDocument();
            string html = wc.DownloadString("user/6971070.html");
            doc.LoadHtml(html);
            HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]");     //根據XPath查找節點,跟XmlNode差不多
            HtmlAttribute attr = node.Attributes["class"];
            Console.WriteLine(attr.Line);   //輸出 155    此屬性所在文檔的行數
            Console.WriteLine(attr.LinePosition);   //輸出 6  此屬性位于文檔的列數
            Console.WriteLine(attr.Name);   //輸出 class  屬性名
            Console.WriteLine(attr.OriginalName);   //輸出 class  未經過更改的原始屬性名
            Console.WriteLine(attr.OwnerDocument);  //擷取所在文檔
            HtmlNode node1 = attr.OwnerNode;
            Console.WriteLine(node1.Name);      //輸出 ul
            AttributeValueQuote ty = attr.QuoteType;    //指定的資料包裝在雙引号裡還是單引号裡
            Console.WriteLine(ty.ToString());   //輸出 DoubleQuote        AttributeValueQuote是一個枚舉,隻有兩個值SingleQuote與DoubleQuote
            Console.WriteLine(attr.StreamPosition);     //輸出7355    屬性所在文檔的字元位置
            Console.WriteLine(attr.Value);      //輸出 user_match clear  屬性的值
            Console.WriteLine(attr.XPath);      //輸出 /html[1]/body[1]/div[4]/div[1]/div[2]/ul[1]/@class[1]    目前屬性的通路XPath表達式

            HtmlAttribute attr1 = attr.Clone();
            Console.WriteLine(attr1.Name + " : " + attr1.Value);    //輸出 class : user_match clear
            //Compareto(Object obj)
            attr.Remove();
            Console.WriteLine(node.OuterHtml);     //輸出 <ul><li>.....</ul>  該屬性已被移除

            Console.ReadKey();
        }      

 HtmlTextNode & HtmlCommentNode

 在HtmlAgilityPack裡,HtmlTextNode對應的是文本節點。這是一個非常簡單的一個類,方法和字段都比較少。

override string InnerHtml { get; set; }    文本内的HTML代碼(不包括自身)

override string OuterHtml { get; }       整個文本節點的Html代碼

string Text { get; set; }           文本字元串

  internal HtmlTextNode(HtmlDocument ownerdocument, int index);

static void Main(string[] args)
        {
            //<ul class="user_match clear">
            //    <li>年齡:21~30之間</li>
            //    <li>婚史:未婚</li>
            //    <li>地區:不限</li>
            //    <li>身高:175~185厘米之間</li>
            //    <li>學曆:不限</li>
            //    <li>職業:不限</li>
            //    <li>月薪:不限</li>
            //    <li>住房:不限</li>
            //    <li>購車:不限</li>
            //</ul>

            WebClient wc = new WebClient();
            wc.BaseAddress = "http://www.juedui100.com/";
            wc.Encoding = Encoding.UTF8;
            HtmlDocument doc = new HtmlDocument();
            string html = wc.DownloadString("user/6971070.html");
            doc.LoadHtml(html);
            HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]/li[1]");     //根據XPath查找節點,跟XmlNode差不多
            //在此處node是第一個li節點
            HtmlTextNode tNode = node.FirstChild as HtmlTextNode;
            Console.WriteLine(tNode.Text);  //輸出 年齡:21~30之間
            Console.WriteLine(tNode.InnerHtml);     //輸出 年齡:21~30之間
            Console.WriteLine(tNode.OuterHtml);     //輸出 年齡:21~30之間     奇怪沒什麼變化

            Console.ReadKey();
        }      

HtmlDocument

  HtmlDocument類對應着一個HTML文檔代碼。它提供了建立文檔,裝載文檔,修改文檔等等一系列功能,來看看它提供的功能。

int CheckSum { get; }           如果 OptionComputeChecksum 設定為 true 之前解析,0 否則擷取文檔 CRC32 校驗和。

Encoding DeclaredEncoding { get; }    擷取文檔的聲明的編碼。聲明确定編碼使用 meta http-equiv ="内容類型"内容 ="文本/html ; charset = XXXXX"html 節點。

HtmlNode DocumentNode { get; }     擷取文檔的根節點。

Encoding Encoding { get; }        擷取文檔的輸出編碼。

IEnumerable<HtmlParseError> ParseErrors { get; }   擷取文檔在解析過程中,發現的解析錯誤集合

string Remainder { get; }         擷取剩餘的文本。如果 OptionStopperNodeName 為空,此屬性将始終為空。

int RemainderOffset { get; }        擷取原始 Html 文本中其餘部分的偏移量。如果 OptionStopperNodeName 為 null,這将傳回原始 Html 文本的長度。

Encoding StreamEncoding { get; }     擷取文檔的流的編碼。

HtmlAttribute CreateAttribute(string name);           建立一個屬性,指定名稱

HtmlAttribute CreateAttribute(string name, string value);     建立一個屬性,指定名稱和值

HtmlCommentNode CreateComment();               建立一個空的注釋節點

HtmlCommentNode CreateComment(string comment);       使用指定的名稱建立一個注釋節點

HtmlNode CreateElement(string name);              使用指定的名稱建立一個 HTML 元素節點。

XPathNavigator CreateNavigator();                 建立一個XPathNavigator 對象

HtmlTextNode CreateTextNode();                 建立一個文本節點

HtmlTextNode CreateTextNode(string text);           建立一個文本節點,并用參數的值指派

Encoding DetectEncoding(Stream stream);             檢測到的 HTML 流的編碼。

Encoding DetectEncoding(string path);               檢測編碼的 HTML 文本。

Encoding DetectEncoding(TextReader reader);           檢測到的關于 TextReader 提供 HTML 文本的編碼。

void DetectEncodingAndLoad(string path);             檢測到第一,從一個檔案的 HTML 文檔的編碼,然後加載該檔案。

void DetectEncodingAndLoad(string path, bool detectEncoding); 檢測到第一,從一個檔案的 HTML 文檔的編碼,然後加載該檔案。

Encoding DetectEncodingHtml(string html);            檢測編碼的 HTML 文本。

HtmlNode GetElementbyId(string id);               根據Id查找一個節點

static string GetXmlName(string name);             擷取一個有效的 XML 名稱。

static string HtmlEncode(string html);              靜态方法,對一個字元串進行HTML編碼

static bool IsWhiteSpace(int c);                  确定指定的字元是否是一個空白字元。

void Load(Stream stream);                    從流中加載一個文檔

void Load(string path);                      從路徑中加載一個文檔

void Load(TextReader reader);

void Load(Stream stream, bool detectEncodingFromByteOrderMarks);

void Load(Stream stream, Encoding encoding);

void Load(string path, bool detectEncodingFromByteOrderMarks);

void Load(string path, Encoding encoding);

void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks);

void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks);

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

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

void LoadHtml(string html);                    從字元串中加載一個文檔

void Save(Stream outStream);                   将目前HTML文檔儲存入流

void Save(StreamWriter writer);

void Save(string filename);                     将HTML文檔儲存到指定的路徑

void Save(TextWriter writer);

void Save(XmlWriter writer);

void Save(Stream outStream, Encoding encoding);

void Save(string filename, Encoding encoding);

屬性代碼示例:

static void Main(string[] args)
        {
            WebClient wc = new WebClient();
            wc.BaseAddress = "http://www.juedui100.com/";
            wc.Encoding = Encoding.UTF8;
            HtmlDocument doc = new HtmlDocument();
            string html = wc.DownloadString("user/6971070.html");
            doc.LoadHtml(html);

            int i = doc.CheckSum;   //如果 OptionComputeChecksum 設定為 true 之前解析,0 否則擷取文檔 CRC32 校驗和。
            Console.WriteLine(i);   //輸出 0  

            Encoding enc = doc.DeclaredEncoding;    //擷取文檔的聲明的編碼。
            Console.WriteLine(enc.BodyName);        //輸出 utf-8

            HtmlNode node = doc.DocumentNode;   //擷取文檔的根節點
            Console.WriteLine(node.Name);       //輸出 #document

            Encoding enc1 = doc.Encoding;       //擷取文檔的輸出編碼
            Console.WriteLine(enc1.BodyName);   //輸出utf-8

            IEnumerable<HtmlParseError> eList = doc.ParseErrors;    //文檔在解析過程中發現的解析錯誤集合

            string str = doc.Remainder;         //擷取剩餘的文本。
            Console.WriteLine(str);             //什麼都沒輸出

            int offset = doc.RemainderOffset;   //擷取原始 Html 文本中其餘部分的偏移量。
            Console.WriteLine(offset);          //輸出 25762

            Encoding enc2 = doc.StreamEncoding;
            Console.WriteLine(enc2.BodyName);

            Console.ReadKey();
        }      

 方法代碼示例:

用于測試的HTML代碼:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>

</body>
</html>      

主程式代碼:

static void Main(string[] args)
        {
            HtmlDocument doc = new HtmlDocument();
            doc.Load(@"D:\1234.html");     //此方法有11個重載,支援各種加載Html文檔
            //Console.WriteLine(doc.DocumentNode.InnerHtml);    已經加載成功,輸出D:123.html的頁面代碼
            HtmlNode node1 = doc.CreateElement("div");      //本文檔建立一個節點
            node1.InnerHtml = "我是一個div";
            doc.DocumentNode.SelectSingleNode("//body").AppendChild(node1); //将節點追加到body裡
            
            HtmlAttribute attr = doc.CreateAttribute("class", "class1");
            doc.DocumentNode.SelectSingleNode("/html/body/div[1]").Attributes.Add(attr);    //此方法也可以用兩個參數添加。
            //以上代碼執行之後 body裡的内容變為 <body><div class="class1">我是一個div</div></body>    看到屬性又被添加進去了

            HtmlCommentNode cNode = doc.CreateComment();
            cNode.Comment = "<!--這是一段注釋-->";            //應該不是這樣寫的吧?可能是我寫錯了,先跳過這一段
            doc.DocumentNode.SelectSingleNode("/html/body/div[1]").AppendChild(cNode);      //雖然達到了目的,但是應該不是這樣寫的吧
            //執行之後
            //<body>
            //    <div class="class1">我是一個div<!--這是一段注釋--></div>    留意到注釋節點已添加進去了
            //</body>

            HtmlTextNode tNode = doc.CreateTextNode("我是一個文本節點");
            doc.DocumentNode.SelectSingleNode("/html/body/div[1]").AppendChild(tNode);
            //執行之後
            //<body>
            //    <div class="class1">我是一個div<!--這是一段注釋-->我是一個文本節點</div>    //留意到文本節點已添加進去了
            //</body> 

            Encoding enc = doc.DetectEncoding(@"D:\1234.html");    //3個重載,應該是從流中,TextWriter中和 路徑中檢測編碼
            //Console.WriteLine(enc.BodyName);              //擷取不到對象,不知道哪裡錯了

            HtmlNode node = doc.CreateElement("p");
            node.InnerHtml = "我是一個p";
            HtmlAttribute attr2 = doc.CreateAttribute("id","id1");
            node.Attributes.Add(attr2);
            doc.DocumentNode.AppendChild(node);

            HtmlNode node2 = doc.GetElementbyId("id1"); //根據Id查找節點
            Console.WriteLine(node2.InnerText);         //輸出 我是一個p

            string strHtml = "<b>我是一個加粗節點</b>";
            string s = HtmlDocument.HtmlEncode(strHtml);
            Console.WriteLine(s);                   //輸出 &lt;b&gt;我是一個加粗節點&lt;/b&gt;    這是經過HTML編碼的字元串

            string str = HtmlDocument.GetXmlName("<sss");   //根據字元串擷取一個有效的XML名稱  
            Console.WriteLine(str);     //輸出 _3c_sss    

            Console.WriteLine(HtmlDocument.IsWhiteSpace(10));   //True
            Console.WriteLine(HtmlDocument.IsWhiteSpace(101));   //False

            doc.Save(@"D:\123.html");       //Save方法有多個重載,可以通過流,路徑,并且還能指定編碼等等。

            HtmlDocument doc1 = new HtmlDocument();
            string html = File.ReadAllText(@"D:\123.html");
            doc1.LoadHtml(html);        //此方法表示從一個字元串中載入HtmlDocument

            Console.ReadKey();
        }      

HtmlWeb類

  HtmlWeb類是一個從網絡上擷取一個HTML文檔的類,其提供的功能大多是基于完成此需求出發。現在來來HtmlWeb類有哪些方法以及屬性。

  一、屬性

bool AutoDetectEncoding { get; set; }     擷取或設定一個值,該值訓示是否必須将自動檢測文檔編碼。

bool CacheOnly { get; set; }          擷取或設定一個值,該值訓示是否隻從緩存中擷取的文檔。如果此設定為 true 并且文檔未找到在緩存中,并不會加載。

string CachePath { get; set; }          擷取或設定緩存路徑。如果為 null,則将使用無緩存的機制。

bool FromCache { get; }            擷取一個值,該值訓示是否從緩存中檢索的最後一次加載的文檔。

Encoding OverrideEncoding { get; set; }    擷取或設定用于重寫從任何 web 請求的響應流的編碼。

int RequestDuration { get; }           擷取上次請求持續時間,以毫秒為機關。

Uri ResponseUri { get; }            擷取的網際網路資源的實際響應請求的 URI。

HttpStatusCode StatusCode { get; }      擷取上次請求狀态。

int StreamBufferSize { get; set; }       擷取或設定用于記憶體操作的緩沖區的大小。

bool UseCookies { get; set; }          擷取或設定一個值,該值訓示是否将存儲的 cookie。

string UserAgent { get; set; }         擷取或設定任何 webrequest 上發送的使用者代理 HTTP 1.1 标頭

bool UsingCache { get; set; }          擷取或設定一個值,訓示是否使用的緩存機制。

二、方法(删減了不少重載)

object CreateInstance(string url, Type type);   從指定的網際網路資源建立給定類型的執行個體。

void Get(string url, string path);          從網際網路資源擷取 HTML 文檔并将其儲存到指定的檔案。

string GetCachePath(Uri uri);            擷取指定的 url 緩存檔案路徑。

static string GetContentTypeForExtension(string extension, string def);      擷取給定的路徑擴充的 MIME 内容類型。

static string GetExtensionForContentType(string contentType, string def);      擷取給定的 MIME 内容類型的路徑擴充。

HtmlDocument Load(string url);          從一個網址加載代碼并傳回一個HtmlDocument

void LoadHtmlAsXml(string htmlUrl, string xsltUrl, XsltArgumentList xsltArgs, XmlTextWriter writer);   從網際網路資源加載 HTML 文檔,并将其儲存到指定的 XmlTextWriter。

代碼示例:由于對HTTP方面的很多知識尚不熟悉,是以先跳過,以後再補充那些不懂的。

static void Main(string[] args)
        {
            HtmlWeb web = new HtmlWeb();
            Console.WriteLine(web.AutoDetectEncoding);  //輸出 True
            Console.WriteLine(web.CacheOnly);   //輸出 False   
            Console.WriteLine(web.CachePath);   //輸出 空白(啥都不輸出)
            Console.WriteLine(web.OverrideEncoding);    //輸出  空白
            Console.WriteLine(web.RequestDuration);     //輸出 0 上次持續請求時間為0?
            Console.WriteLine(web.ResponseUri);         //輸出 空白
            Console.WriteLine(web.StatusCode);          //輸出 Ok 就是 200了,枚舉來的
            Console.WriteLine(web.StreamBufferSize);    //輸出 1024
            Console.WriteLine(web.UseCookies);          //輸出 False
            Console.WriteLine(web.UserAgent);           //輸出 FireFox...................................
            Console.WriteLine(web.UsingCache);          //輸出 False
            HtmlDocument doc = web.Load("http://www.juedui100.com");
            Console.WriteLine(doc.DocumentNode.SelectSingleNode("//title").InnerText);  //輸出 交友_征婚_找對象,上絕對100婚戀交友網

            Uri uri = new Uri("http://www.juedui100.com");

            web.CachePath = @"D:\juedui100\";
            web.UsingCache = true;      //要先開啟使用緩存,下面的方法才能夠使用
            Console.WriteLine(web.GetCachePath(uri));

            web.Get("http://www.juedui100.com",@"D:\juedui100.html"); 
            //二、方法(删減了不少重載)

            //object CreateInstance(string url, Type type);   從指定的網際網路資源建立給定類型的執行個體。
            //static string GetContentTypeForExtension(string extension, string def);            擷取給定的路徑擴充的 MIME 内容類型。 
            //static string GetExtensionForContentType(string contentType, string def);            擷取給定的 MIME 内容類型的路徑擴充。
            //void LoadHtmlAsXml(string htmlUrl, string xsltUrl, XsltArgumentList xsltArgs, XmlTextWriter writer);      從網際網路資源加載 HTML 文檔,并将其儲存到指定的 XmlTextWriter。

            Console.ReadKey();
        }      

HtmlAgilityPack下載下傳開啟壓縮的頁面亂碼

HtmlWeb web = 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;
    };
    web.PreRequest += handler;
    web.OverrideEncoding = Encoding.Default;      
web.OverrideEncoding = Encoding.Default;      

繼續閱讀