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();
        }
複製代碼
複製代碼

 


出處:https://www.cnblogs.com/kissdodog/archive/2013/02/28/2936950.html 

 


 

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();
        }
複製代碼
複製代碼

 

出處:https://www.cnblogs.com/kissdodog/archive/2013/02/28/2937795.html 

 


 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();
        }
複製代碼
複製代碼

 HtmlCommentNode類與HtmlTextNode幾乎一樣,因此不再敘述。

 

出處:https://www.cnblogs.com/kissdodog/archive/2013/02/28/2937838.html


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();
        }
複製代碼
複製代碼

 

出處: https://www.cnblogs.com/kissdodog/archive/2013/02/28/2937900.html

 


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();
        }
複製代碼 
複製代碼

 

出處:https://www.cnblogs.com/kissdodog/archive/2013/03/01/2938698.html 

 


HtmlAgilityPack下載開啓壓縮的頁面亂碼

當一個被採集的網頁是開啓壓縮了的話,如果使用HtmlAgilityPack 的HtmlWeb默認配置去下載,下載回來的HTML代碼是亂碼,應該進行如下操作

複製代碼
複製代碼
    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;

 出處:https://www.cnblogs.com/kissdodog/p/5420332.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章