HTML Agility Pack:簡單好用的快速 HTML Parser

Codeplex 軟體套件(Package)資訊
套件名稱 HTML Agility Pack
作者 Simon Mourier
目前版本 1.4.0 Beta 2
URL http://htmlagilitypack.codeplex.com/
使用難易度
使用此套件時可用的輔助工具 HAP Explorer(可在上述 URL 找到)
Internet Explorer 8 開發者工具
基礎知識 HTML
XML 和 XPath
最好有使用過 System.Xml 命名空間中的 XmlDocument 類別,以及其 SelectNodes() 或 SelectSingleNode() 方法。

解析 HTML:Web 開發人員心中的痛

自從 Web 應用程式自 1993 年 W3C 設立以來就開始發展,而且 HTML 也歷經了數個版本的演化(1.0 – 2.0 – 3.0 – 3.2 – 4.0 – 4.01),現在也已經成為Web網頁或應用程式的最基礎,想要學習如何設計 Web 網頁或開發 Web 應用程式,這已經是絕對必須要學的東西了,就算是方便的控制項充斥(例如 ASP.NET),但 HTML 仍然有學習它的必要性,因此如果不會 HTML,就等於沒學過 Web 網頁般。

拜 HTML 與 Web 瀏覽器蓬勃發展之賜,各式各樣的應用都在網路上迅速發展,舉凡電子商務、企業入口、線上下單、企業間協同應用等,乃至於社群、個人化、Web 2.0 等商務與組織運用等能力,而在資訊爆炸的時代,很多資訊整合的應用也隨之出爐,而這些資訊整合的應用程式都會連接到不同的網站下載其資訊,並且在重重的 HTML 中剖析出想要的資料(例如每股價格、漲跌幅、成交量等)。

但是 HTML 本身並不是一個結構嚴謹的語言,它允許標籤(tag)可以在不 close 的情況下繼續使用。這也是因為瀏覽器設計的高容錯性(Fault Tolerance)所致,如此一來,想要依照規則來剖析 HTML 文件幾乎變得不可能,而且對方的網站的 HTML 結構也可能會隨時變化,在這種情況下,剖析 HTML 變得非常辛苦,雖然 W3C 有另外推展 XHTML(遵守 XML 嚴謹格式的 HTML),但使用它來設計網頁的案例仍為少數,大多數的網站仍然是使用 HTML。因此我們會需要一個工具,能夠有方法快速的解析 HTML 以取出我們需要的資料。

傳統解析 HTML 的方法

大家都知道,HTML 本身其實只是一個 HTML 標記的字串而已,因此一般說到要解析 HTML,第一個會想到的大概就是字串比對(string comparison),自己針對 HTML 的結構寫一個 pattern,然後由函式去做逐一的比對,例如:

[C#]
  1. string pattern = "<td id='stockPrice'>";
  2. html.IndexOf(pattern);

不過傳統的字串比對效能太差,也沒有一個規則性,因而才發展出規則運算式(Regular Expression)技術,例如下列這樣的語法:

[Regular Expression]
  1. </?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)/?>

來源: http://haacked.com/archive/2005/04/22/Matching_HTML_With_Regex.aspx 

但 Regular Expression 的學習曲線很高,若要使用它來解析 HTML,並且再加以客制化(Customization)的話,對於一般開發人員來說,實在沒有什麼親和力。

HTML 還有一個特色,就是它是具階層性(Hierarchy)的,因此瀏覽器在解譯它的時候都會以文件樹(document tree)的方式,再用遞迴(recursive)的方法來處理它,但 Regular Expression 沒有支援階層性的剖析,而最接近階層剖析又好用的工具,莫過於 XML Parser 了,它的 DOM 以及 XPath 的特性,都可以讓解析 XML 的工作變得輕鬆,然而 XML Parser 無法讀取一般的 HTML(XHTML 可以),因為一般的 HTML 是結構鬆散的類型,XML Parser 會在讀入時檢查語法結構是否完整(也就是 Well-known 的結構),若讀入的是結構鬆散的內容的話會擲出例外訊息,因此無法直接使用 XML Parser 來輔助。


HTML 文件樹(IE8 開發者工具)

不過,現在已經有人發展出可以在 HTML 上面使用類似於 XPath 的方式來存取鬆散結構的 HTML 的工具,並且在 Codeplex 上以開放原始碼的方式公開給外界使用,這個工具就是本文所要介紹的 HTML Agility Pack。

HTML Agility Pack 簡介

HTML Agility Pack 是由法國的一位軟體架構師 Simon Mourier 所發展,並且由 DarthObiwan 以及 Jessynoo 輔助開發出來的一個軟體工具,它可以讓剖析鬆散格式 HTML 的工作就像剖析 XML 一樣簡單,它也有類似於 System.Xml 命名空間中的 XML DOM 的許多類別,除了可以使用階層的方式存取 HTML 以外,它也支援使用 XPath 的方式來搜尋 HTML,這會較以往使用文字比對或是 Regular Expression 的比對方式來得更明確,例如:

上圖中以藍色方框框住的是 W3C 的最新消息公告區,而它的 HTML 階層樹是這個樣子:

以往要使用 Regular Expression 剖析時可能要走很多步驟(Match 會回傳很多資料,除非寫的夠精準),才會到達方框所在的位置,但使用 HTML Agility Pack 元件時,我們能用這樣的語法:

[XPath]
  1. /html[1]/body[1]/div[1]/div[2]/div[3]/div[2]/div[1]/div[1]/div[1]

就到達我們想要的地點,這個語法和 XPath 相當類似,對於熟悉 XPath 或是 DOM 的開發人員會比較有利。HTML Agility Pack 元件的類別階層和 XML DOM Parser 其實蠻像的,若先前有用過 XML DOM 的開發人員會覺得很熟悉:


HTML Agility Pack 元件的類別階層

这是个很好的的东西,以前做Html解析都是在用htmlparser,用的虽然顺手,但解析速度较慢,碰巧今天找到了这个,就拿过来试,一切出乎意料,非常爽,推荐给各位使用。

下面是一些简单的使用技巧,希望对大家有用,我个人也是个学习过程。

Why Html Agility Pack? (以下简称HAP)

.Net下解析HTML文件有很多种选择,包括微软自己也提供MSHTML用于manipulate HTML文件。但是,经过我一段时间的搜索,Html Agility Pack浮出水面:它是Stackoverflow网站上推荐最多的C# HTML解析器。HAP开源,易用,解析速度快。

How to use HAP?

1. 下载http://htmlagilitypack.codeplex.com/

2. 解压

3. 在Visual Studio Solution里,右击project -> add reference -> 选择解压文件夹里的HTMLAgilityPack.dll -> 确定

4. 代码头部加入 using HtmlAgilityPack;

HtmlWeb webClient = new HtmlWeb();  
    HtmlDocument doc = webClient.Load("http://xxx");  
      
    HtmlNodeCollection hrefList = doc.DocumentNode.SelectNodes(".//a[@href]");  
      
    if (hrefList != null)  
    {  
         foreach (HtmlNode href in hrefList)  
         {  
            HtmlAttribute att = href.Attributes["href"];  
            doSomething(att.Value);  
      
         }  
      
    }

Q: 如何根据ID选择HTML结点?

A: 利用@id='xxx', e.g.,

HtmlNode bugSum = doc.DocumentNode.SelectSingleNode("//h2[@id='summary']");

Q: 如何得到结点的文字内容或Html内容?

node.InnerText.Trim()  
    node.InnerHtml  
    node.OuterHtml

Q: 如何在html树结构下查找结点?

A: 比如从根节点查找id=container的div下的第一个table:

HtmlNode table = doc.DocumentNode.SelectSingleNode("//div[@id='container']/table[1]");

注意路径里"//"表示从根节点开始查找,两个斜杠‘//’表示查找所有childnodes;一个斜杠'/'表示只查找第一层的childnodes(即不查找grandchild);点斜杠"./"表示从当前结点而不是根结点开始查找。接上一行代码,比如要查找table所有直接子结点的tr:

HtmlNodeCollection tr = table.SelectNodes("./tr");

Q: 如何得到结点的ID?

A: 很简单: node.ID

Q: 如果一段html存在字符串里,是否可以用Html Agility Pack进行处理?

A:可以,先将字符串load进来,之后的处理方法一样:

<pre name="code" class="csharp">//load the original html  
    string html = "some html stuff"  
    HtmlDocument doc = new HtmlDocument();  
    doc.LoadHtml(@html);

Q: 我对load进来的html进行了一些处理,比如改变了一些结点内容,删除了一些结点什么的,为什么结果却没有变化?

A: 也许你忘记save你对html的改变了,假设html存在字符串中:

//load the original html  
    string html = "some html stuff"  
    HtmlDocument doc = new HtmlDocument();  
    doc.LoadHtml(@html);  
      
    //make some changes  
    doSomething();  
      
    //save the change  
    var sb = new StringBuilder();  
    using (var writer = new StringWriter(sb))  
    {  
        doc.Save(writer);  
    }

Q: 如何去掉外层的html tag只留下内容?

A: 用remove方法。假设结点<a href=xxx>ABCD</a>,你想留下ABCD而不要<a></a>,那你需要先得到这个Html结点,假设叫link:

link.ParentNode.RemoveChild(link,true);

参数true表示留下grandchild,在这里即内容ABCD; false表示将此结点连同其grandchilds一起删除。

规则有很多,网上提供了源代码,可以研究一下,还有源代码有乱码问题,是字符集的问题,只需要写一个方法来自动判断就可以解决了

如上面的說明,我們可以撰寫這樣的程式碼來讀取 W3C 首頁公布的最新消息的清單:

[C#]
    1. using HtmlAgilityPack;
    2. public static void Main(string[] args)
    3. {
    4. HtmlWeb webClient = new HtmlWeb();
    5. HtmlDocument doc = webClient.Load("http://www.w3.org/");
    6. HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("/html[1]/body[1]/div[1]/div[2]/div[3]/div[2]/div[1]/div[1]/div[1]/div");
    7. foreach (HtmlNode node in nodes)
    8. {
    9. Console.WriteLine(node.InnerText.Trim());
    10. }
    11. doc = null;
    12. nodes = null;
    13. webClient = null;
    14. Console.WriteLine("Completed.");
    15. Console.ReadLine();
    16. }

HTML Agility Pack:簡單好用的快速 HTML Parser的更多相关文章

  1. Html Agility Pack 解析Html

    Hello 好久不见 哈哈,今天给大家分享一个解析Html的类库 Html Agility Pack.这个适用于想获取某网页里面的部分内容.今天就拿我的Csdn的博客列表来举例. 打开页面  用Fir ...

  2. 簡單工廠模式-之-什麼是產品線 And 抽象工廠模式-之-什麼是產品族

    簡單工廠模式-之-什麼是產品線 簡單工廠模式中,有一個概念就是使用了多層次的產品結構,那麼什麼是產品結構或者說什麼是產品線? 假定我們有一個基準的產品標準Product,那麼所有繼承該基類或者傳遞基類 ...

  3. [Xamarin] 簡單使用Fragment 靜態篇 (转帖)

    新的Android 開發,非常會使用到Fragment,不過官方範例有點小複雜,對初學者來說有點難消化,所以就記錄一下心得,這邊部落格將使用靜態的方法使用Fragment,Fragment 有自己的生 ...

  4. 开源项目Html Agility Pack实现快速解析Html

    这是个很好的的东西,以前做Html解析都是在用htmlparser,用的虽然顺手,但解析速度较慢,碰巧今天找到了这个,就拿过来试,一切出乎意料,非常爽,推荐给各位使用. 下面是一些简单的使用技巧,希望 ...

  5. [Xamarin] 簡單使用AlertDialog (转帖)

    這東西跟Toast 很像,有方便提示的作用 像是Windows 上面的MessageBox 或是 Javascript 的 Alert 會先阻斷使用者並且下一個決定 很簡單我就不贅述,基本上透過 Al ...

  6. 簡單SQL存儲過程實例

    簡單SQL存儲過程實例 摘自:http://blog.csdn.net/libra6956/article/details/5589173 实例1:只返回单一记录集的存储过程. 银行存款表(bankM ...

  7. 转:[ASP.NET]重構之路系列v4 – 簡單使用interface之『你也會IoC』

    前言 上次v3版本,我們將Entity, Service, Dao, Utility都放到了類別庫裡面,讓我們可以輕鬆的在不同專案中用同一份組件.雖然文章沒有獲得太多的讚賞,不過相信那一定是太多人會這 ...

  8. Html Agility Pack基础类介绍及运用

    第一篇只对Html Agility Pack做了一个大概的介绍,在接下来的章节会比较深入的介绍Html Agility Pack. Html Agility Pack 源码中的类大概有28个左右,其实 ...

  9. HTML WEB 和HTML Agility Pack结合

    现在,在不少应用场合中都希望做到数据抓取,特别是基于网页部分的抓取.其实网页抓取的过程实际上是通过编程的方法,去抓取不同网站网页后,再进行分析筛选的过程.比如,有的比较购物网站,会同时去抓取不同购物网 ...

随机推荐

  1. python Popen卡死问题

    程序经常卡死,定位了半天才定位到原因,原来是Popen导致的卡死: 程序如下: s = subprocess.Popen([*,*,*], stdout=subprocess.PIPE) ret = ...

  2. SQL语句(十二)分组查询

    (十二)分组查询 将数据表中的数据按某种条件分成组,按组显示统计信息 查询各班学生的最大年龄.最小年龄.平均年龄和人数 分组 SELECT <字段名表1> FROM <表名> ...

  3. 网页中创建音频、视频和Flash等多媒体:object元素

    <object>元素:它主要用于定义网页中的多媒体,比如音频.视频.Java applets.PDF.ActiveX和Flash.Object标签是成对出现的,在object标签内可以使用 ...

  4. [BZOJ 1260][CQOI2007]涂色paint 题解(区间DP)

    [BZOJ 1260][CQOI2007]涂色paint Description 假设你有一条长度为5的木版,初始时没有涂过任何颜色.你希望把它的5个单位长度分别涂上红.绿.蓝.绿.红色,用一个长度为 ...

  5. 让浏览器重新下载css文件,解决不刷新缓存的问题

    网站页面源代码中的css文件和js文件后面带一个问号,后面跟着一连串数字或字符,问号起不到实际作用,仅能当作后缀,如果用问号加参数的方法,可以添加版本号等信息 它的作用有:1.作为版本号,让自己方便记 ...

  6. golang container heap&sort

    go语言也自己的容器数据结构.主要有list.heap和ring package main import ( "container/heap" "fmt" &q ...

  7. 通俗理解决策树中的熵&条件熵&信息增益

    参考通俗理解决策树算法中的信息增益 说到决策树就要知道如下概念: 熵:表示一个随机变量的复杂性或者不确定性. 假如双十一我要剁手买一件衣服,但是我一直犹豫着要不要买,我决定买这件事的不确定性(熵)为2 ...

  8. 用《舌尖2》去理解C#中的多态和开闭原则

    昨天晚上看了<舌尖上的中国2>第一集,特别的感人,尤其是看到帮别人割麦子的麦客,一亩地开价200,雇主只肯给100,脸上的那种纠结和无可奈何.还有长着大眼睛的跳跳鱼,很可爱,不过最终还是被 ...

  9. Java 连接远程Linux 服务器执行 shell 脚本查看 CPU、内存、硬盘信息

    pom.xml jar 包支持 <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch& ...

  10. python3 之__str__

    当某个类定义了__str__方法是,打印该类的实例对象就是打印__str__方法return出来的数据 示例: class Cat: """定义了一个Cat类" ...