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); //輸出 <b>我是一個加粗節點</b> 這是經過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;