如今愈來愈多的場景須要咱們使用網絡爬蟲,抓取相關數據便於咱們使用,今天咱們要講的主角Html Agility Pack是在爬取的過程中,可以高效的解析咱們抓取到的html數據。html
在.NET技術下,解析html工具也不少,好比不少人可能會使用htmlparser,或者微軟的MSHTML,htmlparser雖然比較易上手,可是相對應的解析速度較慢,而Html Agility Pack解析速度至關快,而且開源,易用,它能夠幫助咱們解析html文檔就像用XmlDocument類來解析xml同樣輕鬆、方便。node
傳送門:官網地址,Github開源代碼地址git
其實Html Agility Pack的類不是不少,咱們解析Html的時候,用到的也就HtmlDocument和HtmlNode(還有HtmlNodeCollection集合類)這幾個類。官網也有相對應的API文檔說明,其實真的是簡單易懂,傳送門:API文檔github
Question 一、如何加載Html?web
HtmlDocument類定義; 多個重載Load方法來實現不一樣方式的Html加載,主要常見的有三種方式;從文件加載、從字符串加載、從網頁連接加載。api
示例:網絡
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) ///緩衝區大小
以指定的物理路徑爲主的重載方法:post
(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 二、如何精準定位到咱們須要的數據?ui
這個時候咱們須要用到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 三、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