HTML解析利器HtmlAgilityPack

 在之前的項目中周公曾有解析HTML的狀況,當時是採用正則表達式一步步將無關的HTML註釋及JS代碼部分刪除掉,而後再用正則表達式找出須要提取的部分,能夠說使用正則表達式來作是一個比較繁瑣的過程,特別是對於正則表達式不是很熟悉或者要處理的HTML很複雜的狀況下。前一陣子周公仍是經過這個辦法將http://wz.csdn.net/zhoufoxcn上保存的網址導入到http://cang.baidu.com,原本還想將周公博客上的文章好好整理一下,可是考慮到使用正則真的是很繁瑣也很麻煩,因此就一直沒有動手。
 直到前兩天在網上發現了一個.NET下的HTML解析類庫HtmlAgilityPack。HtmlAgilityPack是一個支持用XPath來解析HTML的類庫,在花了一點時間學習瞭解HtmlAgilityPack的API和XPath以後,周公就作了一個簡單的工具完成了這個功能,目前在CSDN上週公博文的收集地址爲:http://blog.csdn.net/zhoufoxcn/archive/2011/06/23/6564578.aspx,在51CTO上週公博文的收集地址爲http://zhoufoxcn.blog.51cto.com/792419/595327。HtmlAgilityPack是一個開源的.NET類庫,它的主頁是http://htmlagilitypack.codeplex.com/,在這裏能夠下載到最新版的類庫及API手冊,此外還能夠下載到一個用於調試的輔助工具。
 
 XPath簡明介紹
 XPath 使用路徑表達式來選取 XML 文檔中的節點或節點集。節點是經過沿着路徑 (path) 或者步 (steps) 來選取的。
 下面列出了最有用的路徑表達式:
 nodename:選取此節點的全部子節點。
 /:從根節點選取。
 //:從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置。
 .:選取當前節點。
 ..:選取當前節點的父節點。
 例若有下面一段XML:
 html

  
  
           
  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2.  <Articles> 
  3.  <Article> 
  4.    <Title>在ASP.NET中使用Highcharts js圖表</title> 
  5.    <Url>http://zhoufoxcn.blog.51cto.com/792419/537324</Url> 
  6.    <CreateAt type="en">2011-04-07</price> 
  7.  </Article> 
  8.  <Article> 
  9.    <Title lang="eng">Log4Net使用詳解(續)</title> 
  10.    <Url>http://blog.csdn.net/zhoufoxcn/archive/2010/11/23/6029021.aspx</Url> 
  11.    <CreateAt type="zh-cn">2010年11月23日</price> 
  12.  </Article> 
  13.  <Article> 
  14.    <Title>J2ME開發的通常步驟</title> 
  15.    <Url>http://blog.csdn.net/zhoufoxcn/archive/2011/06/12/6540223.aspx</Url> 
  16.    <CreateAt type="zh-cn">2011年06月12日</price> 
  17.  </Article> 
  18.  <Article> 
  19.    <Title lang="eng">PowerDesign高級應用</title> 
  20.    <Url>http://zhoufoxcn.blog.51cto.com/792419/166415</Url> 
  21.    <CreateAt type="zh-cn">2007-09-08</price> 
  22.  </Article> 
  23.  </Articles> 


  針對上面的XML文件,咱們列出了帶有謂語的一些路徑表達式,以及表達式的結果:
 /Articles/Article[1]:選取屬於Articles子元素的第一個Article元素。
 /Articles/Article[last()]:選取屬於Articles子元素的最後一個Article元素。
 /Articles/Article[last()-1]:選取屬於Articles子元素的倒數第二個Article元素。
 /Articles/Article[position()<3]:選取最前面的兩個屬於 bookstore 元素的子元素的Article元素。
 //title[@lang]:選取全部擁有名爲lang的屬性的title元素。
 //CreateAt[@type='zh-cn']:選取全部CreateAt元素,且這些元素擁有值爲zh-cn的type屬性。
 /Articles/Article[Order>2]:選取Articles元素的全部Article元素,且其中的Order元素的值須大於2。
 /Articles/Article[Order<3]/Title:選取Articles元素中的Article元素的全部Title元素,且其中的Order元素的值須小於3。
 
 HtmlAgilityPack API簡明介紹
 在HtmlAgilityPack中經常使用到的類有HtmlDocument、HtmlNodeCollection、
HtmlNode和HtmlWeb等。
 其流程通常是先獲取HTML,這個能夠經過HtmlDocument的Load()或LoadHtml()來加載靜態內容,或者也能夠HtmlWeb的Get()或Load()方法來加載網絡上的URL對應的HTML。
 獲得了HtmlDocument的實例以後,就能夠用HtmlDocument的DocumentNode屬性,這是整個HTML文檔的根節點,它自己也是一個HtmlNode,而後就能夠利用HtmlNode的SelectNodes()方法返回多個HtmlNode的集合對象HtmlNodeCollection,也能夠利用HtmlNode的SelectSingleNode()方法返回單個HtmlNode。
 
 HtmlAgilityPack實戰
 下面是一個解析CSDN博客的代碼實例:
 node

  
  
           
  
  
  1. using System;  
  2.  using System.Collections.Generic;  
  3.  using System.Text;  
  4.  using HtmlAgilityPack;  
  5.  using System.Text.RegularExpressions;  
  6.    
  7.  namespace CrawlPageApplication  
  8.  {  
  9.      /**  
  10.       * 做者:周公  
  11.       * 日期:2011-06-23  
  12.       * Blog: http://blog.csdn.net/zhoufoxcn or http://zhoufoxcn.blog.51cto.com  
  13.       * Weibo: http://weibo.com/zhoufoxcn  
  14.       */ 
  15.      public class CSDN_Parser  
  16.      {  
  17.          private const string CategoryListXPath = "//html[1]/body[1]/div[1]/div[1]/div[2]/div[1]/div[1]/dl[1]/dd[3]/div[1]/ul[1]/li";  
  18.          private const string CategoryNameXPath = "//li[1]/a[2]";  
  19.    
  20.          /// <summary>  
  21.          /// 分析博客首頁  
  22.          /// </summary>  
  23.          /// <param name="url"></param>  
  24.          /// <returns></returns>  
  25.          public static List<Category> ParseIndexPage(string url)  
  26.          {  
  27.              Uri uriCategory=null;  
  28.              List<Category> list = new List<Category>(40);  
  29.                
  30.              HtmlDocument document = new HtmlDocument();  
  31.     //注意,這裏省略掉了使用本人其它類庫中加載URL的類,而是直接加載本地的HTML文件  
  32.              //string html = HttpWebUtility.ReadFromUrl(url, Encoding.UTF8);  
  33.              //document.LoadHtml(html);  
  34.              document.Load("CSDN_index.html", Encoding.UTF8);  
  35.              HtmlNode rootNode = document.DocumentNode;  
  36.              HtmlNodeCollection categoryNodeList = rootNode.SelectNodes(CategoryListXPath);  
  37.              HtmlNode temp = null;  
  38.              Category category = null;  
  39.              foreach (HtmlNode categoryNode in categoryNodeList)  
  40.              {  
  41.                  temp = HtmlNode.CreateNode(categoryNode.OuterHtml);  
  42.                  category = new Category();  
  43.                  category.Subject = temp.SelectSingleNode(CategoryNameXPath).InnerText;  
  44.                  Uri.TryCreate(UriBase, temp.SelectSingleNode(CategoryNameXPath).Attributes["href"].Value, out uriCategory);  
  45.                  category.IndexUrl = uriCategory.ToString();  
  46.                  category.PageUrlFormat=category.IndexUrl+"?PageNumber={0}";  
  47.                  list.Add(category);  
  48.                  Category.CategoryDetails.Add(category.IndexUrl, category);  
  49.              }  
  50.              return list;  
  51.          }  
  52.    
  53.      }  
  54.  } 


  固然實現相似的解析51CTO的博客文章數據的代碼以下:
 正則表達式

  
  
           
  
  
  1. using System;  
  2.  using System.Collections.Generic;  
  3.  using System.Text;  
  4.  using HtmlAgilityPack;  
  5.  using System.Text.RegularExpressions;  
  6.    
  7.  namespace CrawlPageApplication  
  8.  {  
  9.      /**  
  10.       * 做者:周公  
  11.       * 日期:2011-06-23  
  12.       * Blog: http://blog.csdn.net/zhoufoxcn or http://zhoufoxcn.blog.51cto.com  
  13.       * Weibo: http://weibo.com/zhoufoxcn  
  14.       */ 
  15.      public class CTO_Parser  
  16.      {  
  17.          private static Encoding PageEncoding = Encoding.GetEncoding("gb2312");  
  18.          private static readonly Uri UriBase = new Uri("http://zhoufoxcn.blog.51cto.com");  
  19.          private static string CategoryListXPath = "/html[1]/body[1]/div[5]/div[1]/div[1]/div[2]/ul[1]/li";  
  20.          private static string CategoryNameXPath = "/li[1]/a[1]";  
  21.    
  22.          /// <summary>  
  23.          /// 分析博客首頁  
  24.          /// </summary>  
  25.          /// <param name="url"></param>  
  26.          /// <returns></returns>  
  27.          public static List<Category> ParseIndexPage(string url)  
  28.          {  
  29.              Uri uriCategory = null;  
  30.              List<Category> list = new List<Category>(40);  
  31.    
  32.              HtmlDocument document = new HtmlDocument();  
  33.              //string html = HttpWebUtility.ReadFromUrl(url, PageEncoding);  
  34.              //document.LoadHtml(html);  
  35.              document.Load("51cto_index.html", PageEncoding);  
  36.              HtmlNode rootNode = document.DocumentNode;  
  37.              HtmlNodeCollection categoryNodeList = rootNode.SelectNodes(CategoryListXPath);  
  38.              HtmlNode temp = null;  
  39.              Category category = null;  
  40.              foreach (HtmlNode categoryNode in categoryNodeList)  
  41.              {  
  42.                  temp = HtmlNode.CreateNode(categoryNode.OuterHtml);  
  43.                  if (temp.SelectSingleNode(CategoryNameXPath).InnerText != "所有文章")  
  44.                  {  
  45.                      category = new Category();  
  46.                      category.Subject = temp.SelectSingleNode(CategoryNameXPath).InnerText;  
  47.                      Uri.TryCreate(UriBase, temp.SelectSingleNode(CategoryNameXPath).Attributes["href"].Value, out uriCategory);  
  48.                      category.IndexUrl = uriCategory.ToString();  
  49.                      category.PageUrlFormat = category.IndexUrl + "/page/{0}";  
  50.                      list.Add(category);  
  51.                      Category.CategoryDetails.Add(category.IndexUrl, category);  
  52.                  }  
  53.              }  
  54.              return list;  
  55.          }  
  56.      }  
  57.  } 


  在上面的代碼中出現了一個Category類,該類的定義以下:
  爲了鼓勵你們動手嘗試以及在本項目中使用了周公的私家類庫,因此不提供所有源代碼下載,這裏提供周公操做的最終軟件界面:網絡


 
 總結:HtmlAgilityPack確實是一個功能強大、體積小的開源HTML解析類庫,在本篇僅僅是介紹了其中幾個類的用法,但光這些就足以供周公快速實現了許久沒有實現的功能,若是讓周公用正則表達式來實現相似的功能,時間確定要比用這個長得多。
 說明:周公最近也在琢磨一些關於微博的應用,若是有相同愛好者或者在使用微博的讀者,請圍觀周公的微博,網址是:http://weibo.com/zhoufoxcn
 
 2011-06-24
 周公ide

相關文章
相關標籤/搜索