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); //輸出 <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();
}
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;