簡介
現在越來越多的場景需要我們使用網絡爬蟲,抓取相關數據便於我們使用,今天我們要講的主角Html Agility Pack是在爬取的過程當中,能夠高效的解析我們抓取到的html數據。
優勢
在.NET技術下,解析html工具也很多,比如很多人可能會使用htmlparser,或者微軟的MSHTML,htmlparser雖然比較易上手,但是相對應的解析速度較慢,而Html Agility Pack解析速度相當快,並且開源,易用,它可以幫助我們解析html文檔就像用XmlDocument類來解析xml一樣輕松、方便。
傳送門:官網地址,Github開源代碼地址
方法介紹
其實Html Agility Pack的類不是很多,我們解析Html的時候,用到的也就HtmlDocument和HtmlNode(還有HtmlNodeCollection集合類)這幾個類。官網也有相對應的API文檔說明,其實真的是簡單易懂,傳送門:API文檔
Question 1、如何加載Html?
HtmlDocument類定義; 多個重載Load方法來實現不同方式的Html加載,主要常見的有三種方式;從文件加載、從字符串加載、從網頁鏈接加載。
示例:
1 // 從物理路徑的文件加載 2 var doc = new HtmlDocument(); 3 doc.Load(filePath);//文件路徑 4 5 // 從Stream當中加載 6 var doc = new HtmlDocument(); 7 doc.LoadHtml(html); 8 9 // 從網頁的Url鏈接加載 10 var url = "http://www.cnblogs.com/xuliangxing/"; 11 var web = new HtmlWeb(); 12 var doc = web.Load(url);
以Stream對象為主的重載方法:
(1)public void Load(Stream stream) ///從指定的Stream對象中加載html; (2)public void Load(Stream stream, bool detectEncodingFromByteOrderMarks) ///指定是否從順序字節流中解析編碼格式 (3)public void Load(Stream stream, Encoding encoding) ///指定編碼格式 (4)public void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks) (5)public void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize) ///緩沖區大小
以指定的物理路徑為主的重載方法:
(1)public void Load(string path) (2)public void Load(string path, bool detectEncodingFromByteOrderMarks) ///指定是否從順序字節流中解析編碼格式 (3)public void Load(string path, Encoding encoding) ///指定編碼格式 (4)public void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks) (5)public void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize)
Question 2、如何精准定位到我們需要的數據?
這個時候我們需要用到HtmlNodeCollection和HtmlNode這兩個類,我們把Html每個標簽看作一個Node,所有我們想到定位到某個標簽的內容,就需要知道這個標簽的相關屬性。順便說一下,HtmlNode類實現了IXPathNavigable接口,這說明了它可以通過xpath來定位數據了,如果你對解析XML格式數據的XmlDocument類了解的話,特別是使用過了SelectNodes()和SelectSingleNode()方法的人來說,對使用HtmlNode類將會很熟悉。其實Html Agility Pack內部是把html解析成xml文檔格式了的,所以支持xml中的一些常用查詢方式。下面通過簡單示例對HtmlNode的一些主要的常用成員作簡要的說明。
就以我博客園主頁為例,希望大家在此基礎上能夠舉一反三,這里只做拋磚引玉,界面圖如下:
后台Html代碼,我抓取了部分代碼拿來做示例
1 <!DOCTYPE html> 2 <html lang="zh-cn"> 3 <head> 4 <meta charset="utf-8"/> 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 <title>法號阿興 - 博客園</title> 7 </head> 8 <body> 9 <div id="home"> 10 <div id="header"> 11 <div id="blogTitle"> 12 <a id="lnkBlogLogo" href="http://www.cnblogs.com/xuliangxing/"><img id="blogLogo" src="/Skins/custom/images/logo.gif" alt="返回主頁" /></a> 13 <h1><a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/xuliangxing/">法號阿興</a></h1> 14 <h2>你的能力還駕馭不了你的目標時,就應該沉下心來歷練</h2> 15 </div> <!--end: blogTitle 博客的標題和副標題 --> 16 <div id="navigator"> 17 <ul id="navList"> 18 <li><a id="blog_nav_sitehome" class="menu" href="http://www.cnblogs.com/">博客園</a></li> 19 <li><a id="blog_nav_myhome" class="menu" href="http://www.cnblogs.com/xuliangxing/">首頁</a></li> 20 <li><a href="http://news.cnblogs.com/">新聞</a></li> 21 <li><a id="blog_nav_newpost" class="menu" rel="nofollow" href="https://i.cnblogs.com/EditPosts.aspx?opt=1">新隨筆</a></li> 22 <li><a id="blog_nav_contact" accesskey="9" class="menu" rel="nofollow" href="https://msg.cnblogs.com/send/%E6%B3%95%E5%8F%B7%E9%98%BF%E5%85%B4">聯系</a></li> 23 <li><a id="blog_nav_admin" class="menu" rel="nofollow" href="https://i.cnblogs.com/">管理</a></li> 24 <li><a id="blog_nav_rss" class="menu" href="http://www.cnblogs.com/xuliangxing/rss">訂閱</a> 25 <a id="blog_nav_rss_image" class="aHeaderXML" href="http://www.cnblogs.com/xuliangxing/rss"><img src="//www.cnblogs.com/images/xml.gif" alt="訂閱" /></a></li> 26 </ul> 27 </div><!--end: header 頭部 --> 28 <div id="main"></div><!--end: 正文 --> 29 <div id="footer"></div><!--end: 底部 --> 30 </div> 31 </body> 32 </html>
一般Html最常見的是div標簽元素,它可能會定義一些屬性,比例本文當中的<div id="blogTitle">,有些不是id屬性,是class屬性,這個根據實際情況而定,要靈活變通
(1)通過ID屬性(或者其他屬性)來選擇對應的節點
通用格式:@id=‘xxxx’(id可以是其他屬性等等),比如我們要定位到本文博客主頁的標題和副標題內容。
1 HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); 2 doc.LoadHtml(url)//博客主頁URL 3 //下面的意思是:通過屬性id的值,來定位header下的blogTitle節點信息 4 HtmlNode titleNode = doc.DocumentNode.SelectSingleNode("//div[@id='header']/div[@id='blogTitle']");
我們還可以不通過屬性id去定位,還有通過索引去定位,如下所示,這個效果和上面是等同的:
1 //下面的意思是:通過索引定位,div[2]是表示根節點的第二個 2 HtmlNode titleNode = doc.DocumentNode.SelectSingleNode("//div[2]/div[1]");
備注:注意路徑里"//"表示從根節點開始查找,兩個斜杠‘//’表示查找所有childnodes;一個斜杠'/'表示只查找第一層的childnodes(即不查找grandchild);點斜杠"./"表示從當前結點而不是根結點開始查找。我們接着上面titleNode節點,查找我博客園的ID。
1 //下面的意思是:通過當前titleNode節點,獲取便簽h1的節點 2 HtmlNode IDNode = titleNode.SelectSingleNode("./h1");
講解了上面這些,大家應該已經能夠明白了Html Agility Pack的基本使用方法,那么如何一次性獲取博主的ID呢?
1 HtmlNode IDNode = doc.DocumentNode.SelectSingleNode("//div[@id='header']/div[@id='blogTitle']/h1");
(2)如何獲取節點文本內容
接着上面(1)所說的,通過代碼“HtmlNode IDNode = doc.DocumentNode.SelectSingleNode("//div[@id='header']/div[@id='blogTitle']/h1") ”獲取到了IDNode的節點,那么接下來,我們需要這么獲取具體的文本值呢(即博主ID)?有三種方式獲取OuterHtml,InnerHtml和InnerText。
HtmlNode類設計了OuterHtml屬性和InnerHtml屬性用於獲取當前節點的Html源碼。兩者不同之處是,OuterHtml屬性返回的是包含當前節點的Html代碼在內的所有Html代碼,而InnerHtml屬性返回的是當前節點里面子節點的所有Html代碼,InnerText屬性過濾掉了所有的Html標記代碼,只返回文本值。具體用哪種方式,要根據我們實際情況而定,一般InnerHtml和InnerText使用的頻率比較多。如下所示:
1 //我們獲取博客ID 2 IDNode.OuterHtml ///返回結果是:<h1><a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/xuliangxing/">法號阿興</a></h1> 3 IDNode.InnerHtml ///返回結果是:<a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/xuliangxing/">法號阿興</a> 4 IDNode.InnerText ///返回結果是:法號阿興
(3)如何獲取節點屬性值
假如我們上面Html數據當中,博主博客地址,在標簽<div id="header">里的<a>標簽里,這個時候就需要使用HtmlNode下的Attribute屬性了。
1 string url = doc.DocumentNode.SelectSingleNode("//div[@id='header']/div[@id='blogTitle']/a").Attributes["href"].Value;
(4)如何獲取某個標簽的所有節點
我們如果獲取前面Html數據的li所有分類,這個時候需要使用方法SelectNodes了
HtmlNodeCollection uiListNodes = doc.DocumentNode.SelectNodes("//ui[@id='navList']/li");
Question 3、Html Agility Pack進行刪除操作
Html Agility Pack是可以對Html做刪除操作的,具體的可以參考官網的API,這里我們講下最常見
(1)如何去掉外層的html tag只留下文本內容
回到我們剛剛上面講到的地方,用remove方法。假設要刪除上文結點<a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/xuliangxing/">法號阿興</a>,你想留下博客名稱而不要<a></a>的話,那你需要先得到這個Html結點,通過remove方法刪除掉多余的HTML Tag假設該節點叫Node:
Node.ParentNode.RemoveChild(Node,true);
參數true表示留下grandchild,在這里即博主博客ID內容; false表示將此結點連同其grandchilds一起刪除。
更多的方法大家可以到官網的API文檔進行了解,這里就不做更多的說明。
PS:如有疑問,請留言,未經允許,不得私自轉載,轉載請注明出處:http://www.cnblogs.com/xuliangxing/p/8004403.html