HtmlAgilityPack基础类介绍及运用

Html Agility Pack源码中的类大概有28个左右,其实不算一个很复杂的类库,但它的功能确不弱,为解析DOM已经提供了足够强大的功能支持,可以跟jQuery操作DOM媲美:)

基础类和基础方法介绍

Html Agility Pack最常用的基础类其实不多,对解析DOM来说,就只有HtmlDocument和HtmlNode这两个常用的类,还有一个 HtmlNodeCollection集合类。

HtmlDocument类

当然在解析DOM前需要加载html原始文件或者html的字符串,HtmlDocument类封装了支持此功能的方法,下面是加载html的方法介绍。

HtmlDocument类定义了多个重载的Load方法来实现以不同方式加载html,其实主要分为两种,一种是从Stream中加载html,另外一种是从物理路径加载html,分别见下面:
方法:public void Load(TextReader reader)
说明:从指定的 TextReader对象中加载Html

示例:
HtmlDcument doc = new HtmlDocument();
StreamReader sr = File.OpenText("file path");
doc.Load(sr);

基于上面方法,衍生出了几个不同重载方法。

以指定的Stream对象为主的有:
1. 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)

HtmlDocument类中还定义了直接从html字符串中加载Html,如下:
方法:public void LoadHtml(string html)
说明:从指定的html字符串中加载html

示例:
HtmlDocument doc = new HtmlDocument();
string html = "<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span></div>";
doc.LoadHtml(html);

HtmlDocument类还有其他写DOM方法的定义,这里不作详细介绍,留作以后专门介绍Html Agility Pack写DOM章节介绍吧,这里着重介绍Html Agility pack解析DOM的细节。

HtmlNode类和HtmlNodeCollection类

通过HtmlDocument把html加载进来后,接着是要做什么呢?当然是对html解析了,解析DOM就需要提到HtmlNode类了。 HtmlDocument类由属性DocumentNode属性返回当前Html解析后的一个全局的HtmlNode对象;如果想获取某一个元素的 HtmlNode,可以通过HtmlDocument类的GetElementbyId(string Id)方法来获取,返回指定某一个html元素的HtmlNode对象。如何通过HtmlNode对象来访问DOM呢?介绍之前先对它的功能了解下。

HtmlNode类实现了IXPathNavigable接口,这说明了它可以通过xpath来查询DOM了,如果对System.Xml命名空间下的 XmlDocument类了解的,特别是使用过了SelectNodes()和SelectSingleNode()方法的朋友对使用HtmlNode类将会很熟悉。其实Html Agility Pack内部是把html解析成xml文档格式了的,所以支持xml中的一些常用查询方式。下面对HtmlNode的一些主要的常用成员作简要的说明。

HtmlNode类的主要属性

  1. Attributes属性
    获取当前Html元素的属性的集合,返回的是一个HtmlAttributeCollection对象。如一个div元素,它可能会定义一些属性,如:
<div id="title" name="title" class="class-name" title="title div">***</div>```
那Attributes返回的HtmlAttributeCollection就包含了 “id,name,class,title”的信息。HtmlAttributeCollection类是实现了接口 IList<HtmlAttribute>的一个集合类,故此可以通过下面代码方式访问每一个成员。
```C#
HtmlNode node = doc.GetElementbyId("title");
string titleValue = node.Attributes["title"].Value;
或者
foreach(HtmlAttribute attr in node.Attributes)
{
    Console.WriteLine("{0}={1}",attr.Name,attr.Value);
}

在获取属性值时,如果某一个属性名称不存在的话,Attributes["name"]返回的是null值。

2)FirstChild,LastChild,ChildNodes,ParentNode属性

FirstChild属性:返回所有子节点的第一个节点,如下面代码:

string html = "<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span><div id="innerDiv">inner div</div></div>";
FirstChild则返回的是“<span style="color:red;"><h1>Hello World!</h1></span>” 的节点。
LastChild属性:返回所有子节点的最后一个节点,以上面的html为例,则返回“<div id="innerDiv">inner div</div>”节点。
ChildNodes属性:返回当前节点所有直接一代的子节点的集合,不包括跨代子节点,以上面的html为例,则返回“<span style="color:red;"><h1>Hello World!</h1></span>” 和“<div id="innerDiv">inner div</div>”两个节点。
ParentNode属性:返回当前节点的直接父节点。
  1. 获取Html源码和文本

HtmlNode类设计了OuterHtml属性和InnerHtml属性用于获取当前节点的Html源码。两者不同之处是,OuterHtml属性返回的是包含当前节点的Html代码在内的所有Html代码,而InnerHtml属性返回的是当前节点里面子节点的所有Html代码。如下面:

HtmlDocument doc = new HtmlDocument();
string html = "<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span></div>";
doc.LoadHtml(html);
HtmlNode node = doc.HtmlDocument;
Console.WriteLine(node.OuterHtml); /// return "<div id="demo"><span style="color:red;"><h1>Hello World!</h1></span></div>";
Console.WriteLine(node.InnerHtml); /// return "<span style="color:red;"><h1>Hello World!</h1></span>";

如要获取节点的文本值,通过InnerText属性来获取,InnerText属性过滤掉了所有的Html标记代码,只返回文本值,如下面:

Console.WriteLine(node.InnerText);/// return "Hello World!";

HtmlNode类的主要方法

HtmlNode类提供了足够丰富的方法供查询当前节点下的子节点(元素),当然也包括查询当前节点的父节点(元素)的方法,下面列出主要的方法和使用说明。

获取父节点的系列方法:
1. public IEnumerable<HtmlNode> Ancestors()
获取当前节点的父节点列表(不包含自身)。
2. public IEnumerable<HtmlNode> Ancestors(string name)
以指定一个名称来获取父节点的列表(不包含自身)。
3. public IEnumerable<HtmlNode> AncestorsAndSelf()
获取当前节点的父节点列表(包含自身)。
4. public IEnumerable<HtmlNode> AncestorsAndSelf(string name)
以指定一个名称来获取父节点的列表(包含自身)。
获取子节点的系列方法:
1. public IEnumerable<HtmlNode> DescendantNodes()
获取当前节点下的所有子节点的列表,包括子节点的子节点(不包含自身)。
2. public IEnumerable<HtmlNode> DescendantNodesAndSelf()
获取当前节点下的所有子节点的列表,包括子节点的子节点(包含自身)。
3. public IEnumerable<HtmlNode> Descendants()
获取当前节点下的直接子节点的列表(不包含自身)。
4. public IEnumerable<HtmlNode> DescendantsAndSelf()
获取当前节点下的直接子节点的列表(包含自身)。
5. public IEnumerable<HtmlNode> Descendants(string name)
获取当前节点下的以指定名称的子节点列表。
6. public IEnumerable<HtmlNode> DescendantsAndSelf(string name)
获取当前节点下的以指定名称的子节点的列表(包含自身)。
7. public HtmlNode Element(string name)
获取第一个符合指定名称的直接子节点的节点元素。
8. public IEnumerable<HtmlNode> Elements(string name)
获取符合指定名称的所有直接子节点的节点列表。
9. public HtmlNodeCollection SelectNodes(string xpath)
获取符合指定的xpath的子节点列表。
10. public HtmlNode SelectSingleNode(string xpath)
获取符合指定的xpath的单个字节点元素。

查询节点的方法主要是上面10个方法,该类还有其他写节点的系列方法,这里不详细介绍写操作的方法,留作以后详细介绍。
结合Xpath进行查询节点是功能比较强大,这像操作xml那样方便。
简单例子的代码
下面例子是把博客园的精华区博客列表查询出来。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using HtmlAgilityPack;

namespace DemoCnBlogs
{
  class Program
  {
    static void Main(string[] args)
    {
      HtmlWeb web = new HtmlWeb();
      HtmlDocument doc = web.Load("http://www.cnblogs.com/pick/");
      HtmlNode node = doc.GetElementbyId("post_list");
      StreamWriter sw = File.CreateText("log.txt");
      foreach(HtmlNode child in node.ChildNodes)
      {
        if (child.Attributes["class"] == null || child.Attributes["class"].Value != "post_item")
          continue;
          HtmlNode hn = HtmlNode.CreateNode(child.OuterHtml);
          ///如果用child.SelectSingleNode("//*[@class=\"titlelnk\"]").InnerText这样的方式查询,是永远以整个document为基准来查询,
          ///这点就不好,理应以当前child节点的html为基准才对。
          Write(sw, String.Format("推荐:{0}", hn.SelectSingleNode("//*[@class=\"diggnum\"]").InnerText));
          Write(sw, String.Format("标题:{0}", hn.SelectSingleNode("//*[@class=\"titlelnk\"]").InnerText));
          Write(sw, String.Format("介绍:{0}", hn.SelectSingleNode("//*[@class=\"post_item_summary\"]").InnerText));
          Write(sw, String.Format("信息:{0}", hn.SelectSingleNode("//*[@class=\"post_item_foot\"]").InnerText));
          Write(sw, "----------------------------------------");
      }
        sw.Close();
        Console.ReadLine();
    }
    static void Write(StreamWriter writer, string str)
    {
        Console.WriteLine(str);
        writer.WriteLine(str);
    }
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,273评论 25 707
  • 他们是对兄弟,十年前,一起结伴来北京求职,在一家饭店做服务员。 领班是个工作很严谨的人,表兄和表弟刚来店里的时候,...
    想走遍世界的橘子树阅读 558评论 0 0
  • 简书上已经有了很多对runtime讲解的文章,有深有浅,但大多有一个共同的特点,它们都好长啊…… 有句话叫书到用时...
    王梓舟阅读 695评论 0 7
  • 之前从来都没有想到过要坚持跑步,只是觉得这事是吃饱了没事做的人才去做的。可是当生活打击了你,而你决定开始反击的时候...
    黑脸阿康阅读 887评论 0 2
  • 最近在某单位审计,又见到了小高。其实小高真名不姓高,只是我虽每年都会看到他,却不知道他的具体姓名,只见身材...
    秋水遥mam阅读 203评论 0 0