我最優惠網系列(1)——HTML 解析類庫HtmlAgilityPack

0. 序言

在開發我最優惠網的過程當中,遇到一些問題和技術點,寫出來和你們分享,也是我本身對近期工做的整理和記錄,預計會有解析HTML類庫、本地緩存、連接跳轉和C#中執行js代碼技巧等方面。html

 

1. HtmlAgilityPack簡介

網站中首先遇到的問題是爬蟲和解析HTML的問題,通常狀況在獲取頁面少許信息的狀況下,咱們可使用正則來精確匹配目標。不過自己正則表達式就比較複雜,同時正則表達式的精確程度很難拿捏,太精確和原網頁耦合太嚴重,頁面代碼稍改動就會使正則無效;太寬泛的正則由可能會匹配目標過多。因此咱們今天介紹的是經過解析HTML結構來獲取目標的方式——HtmlAgilityPack。node

HtmlAgilityPack是一個解析HTML的類庫,支持用XPath來解析HTML,能夠像XML同樣來解析HTML。正則表達式

HtmlAgilityPack的代碼託管在codeplex上:http://htmlagilitypack.codeplex.com/,不過建議經過Nuget來獲取最新版本。緩存

 

 

2. XPath簡介

XPath即爲XML路徑語言,它是一種用來肯定XML文檔中某部分位置的語言。XPath基於XML的樹狀結構,提供在數據結構樹中找尋節點的能力。下圖列舉了XPath主要的路徑表達式:網絡

這種針對XML的路徑能在解析HTML中用的緣由是HtmlAgilityPack將下載下來的HTML頁面進行規格化處理,讓本來對語義支持並很差的HTML文檔格式變爲更嚴謹的Xhtml格式,甚至能夠轉換爲XML格式;並使用XPath來選擇、處理dom中的element。下圖表示HTML格式化以後的節點示意圖:數據結構

 

3. HtmlAgilityPack中經常使用的API

 在HtmlAgilityPack中經常使用到的類有HtmlDocument、HtmlNodeCollection、HtmlNode和HtmlWeb。app

首先是加載HTML,若是是已經存在的靜態HTML代碼,能夠用HtmlDocument的Load()或LoadHtml()來加載,若是是網絡上的URL則須要用HtmlWeb的Get()或Load()方法來加載。dom

不論是哪一種加載方式,咱們獲得的都是HtmlDocument的實例。此時咱們須要獲得的是HtmlNode或者HtmlNodeCollection對象,使用HtmlDocument的DocumentNode屬性,它整個HTML文檔的根節點,它自己也是一個HtmlNode。網站

獲得文檔根節點後便可使用前一節介紹的XPath獲得你想要的文檔中任意一個節點的信息。ui

 

下面是一個典型的得到有效內容的例子:

HtmlWeb htmlWeb = new HtmlWeb();
HtmlDocument htmlDoc = htmlWeb.Load("http://www.baidu.com");
HtmlNode htmlNode = htmlDoc.DocumentNode.SelectSingleNode("//title");
string title = htmlNode.InnerText;

  

因爲使用最多的類是HtmlNode,這裏將它經常使用的屬性和方法列在下面,方便各位查閱。

屬性:

Attributes              獲取節點的屬性集合
ChildNodes             獲取子節點集合(包括文本節點)
FirstChild              獲取第一個子節點
HasAttributes            判斷該節點是否含有屬性
HasChildNodes          判斷該節點是否含有子節點
Id                 獲取該節點的Id屬性
InnerHtml             獲取該節點的Html代碼
InnerText             獲取該節點的內容,與InnerHtml不一樣的地方在於它會過濾掉Html代碼,而InnerHtml是連Html代碼一塊兒輸出
LastChild              獲取最後一個子節點
Name                Html元素名
NextSibling            獲取下一個兄弟節點
ParentNode            獲取該節點的父節點
PreviousSibling          獲取前一個兄弟節點
XPath                根據節點返回該節點的XPath

 

方法:

HtmlNode AppendChild(HtmlNode newChild);              將參數元素追加到爲調用元素的子元素(追加在最後)
void AppendChildren(HtmlNodeCollection newChildren);       將參數集合中的元素追加爲調用元素的子元素(追加在最後)
HtmlNode PrependChild(HtmlNode newChild);              將參數中的元素做爲子元素,放在調用元素的最前面
void PrependChildren(HtmlNodeCollection newChildren);       將參數集合中的全部元素做爲子元素,放在調用元素前面
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);               建立節點的副本。
static HtmlNode CreateNode(string html);                靜態方法,容許用字符串建立一個新節點
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。

 

4. 實戰

 基礎的都熟悉了,咱們來作練習。例子就是在開發我最優惠網中實際使用的代碼,獲取什麼值得買發現頻道的商品名稱、價格和詳情頁網址。代碼片斷以下:

        static void Main(string[] args)
        {
            HtmlWeb htmlWeb = new HtmlWeb();
            HtmlDocument htmlDoc = htmlWeb.Load("http://faxian.smzdm.com");
            HtmlNode htmlNode = htmlDoc.DocumentNode.SelectSingleNode("//ul[@class='leftWrap discovery_list']");

            foreach (var li in htmlNode.SelectNodes("child::li"))
            {
                Console.WriteLine("名稱:" + li.SelectSingleNode("child::div[@class='listItem']/h2/a/span[1]").InnerText);
                Console.WriteLine("價格:" + li.SelectSingleNode("child::div[@class='listItem']/h2/a/span[2]").InnerText);
                Console.WriteLine("網址:" + li.SelectSingleNode("child::div[@class='listItem']/h2/a").GetAttributeValue("href", ""));
                Console.WriteLine("---------------------------------------------------------------------------");
                Thread.Sleep(1000);
            }
            Console.ReadLine();
        }    

  

代碼下載:http://pan.baidu.com/s/1pLkq6E3

 

5. 後記

HtmlAgilityPack 的確是一個功能強大的HTML解析類庫,我目前僅僅使用了它的一小部分功能,可是已經能徹底知足個人需求。若是童鞋們有相似需求,能夠試試。

最後再次打個廣告:網購前記得去我最優惠網查查最低價哦^_^

 

 

參考文檔:

http://www.cnblogs.com/oec2003/p/3322956.html

http://zhoufoxcn.blog.51cto.com/792419/595344/

http://www.cnblogs.com/kissdodog/archive/2013/02/28/2936950.html

http://www.tuicool.com/articles/YZ3uau

相關文章
相關標籤/搜索