1. HtmlAgilityPack简介

网站中首先遇到的问题是爬虫和解析HTML的问题,一般情况在获取页面少量信息的情况下,我们可以使用正则来精确匹配目标。不过本身正则表达式就比较复杂,同时正则表达式的精确程度很难拿捏,太精确和原网页耦合太严重,页面代码稍改动就会使正则无效;太宽泛的正则由可能会匹配目标过多。所以我们今天介绍的是通过解析HTML结构来获取目标的方式——HtmlAgilityPack。

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。

首先是加载HTML,如果是已经存在的静态HTML代码,可以用HtmlDocument的Load()或LoadHtml()来加载,如果是网络上的URL则需要用HtmlWeb的Get()或Load()方法来加载。

不管是哪种加载方式,我们得到的都是HtmlDocument的实例。此时我们需要得到的是HtmlNode或者HtmlNodeCollection对象,使用HtmlDocument的DocumentNode属性,它整个HTML文档的根节点,它本身也是一个HtmlNode。

得到文档根节点后即可使用前一节介绍的XPath得到你想要的文档中任意一个节点的信息。

下面是一个典型的获得有效内容的例子:

1
2
3
4
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. 实战

基础的都熟悉了,我们来做练习。代码片段如下:

  HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);//加载html
string pageNumberPath = @"//*[@id='J_topPage']/span/i";
HtmlNode pageNumberNode = doc.DocumentNode.SelectSingleNode(pageNumberPath);
if (pageNumberNode != null)
{
string sNumber = pageNumberNode.InnerText;
for (int i = ; i < int.Parse(sNumber) + ; i++)
{
string pageUrl = string.Format("{0}&page={1}", category.Url, i);
try
{
List<Commodity> commodityList = GetCommodityList(category, pageUrl.Replace("&page=1&", string.Format("&page={0}&", i)));
commodityRepository.SaveList(commodityList);
}
catch (Exception ex)//保证一页的错误不影响另外一页
{
logger.Error("Crawler的commodityRepository.SaveList(commodityList)出现异常", ex);
}
}

  

5. 后记

HtmlAgilityPack 的确是一个功能强大的HTML解析类库,我目前仅仅使用了它的一小部分功能,但是已经能完全满足我的需求。如果童鞋们有类似需求,可以试试。

HTML 解析类库HtmlAgilityPack的更多相关文章

  1. 我最优惠网系列(1)——HTML 解析类库HtmlAgilityPack

    0. 序言 在开发我最优惠网的过程中,遇到一些问题和技术点,写出来和大家分享,也是我自己对近期工作的整理和记录,预计会有解析HTML类库.本地缓存.链接跳转和C#中执行js代码技巧等方面. 1. Ht ...

  2. HTML解析利器HtmlAgilityPack

    一个.NET下的HTML解析类库HtmlAgilityPack.HtmlAgilityPack是一个支持用XPath来解析HTML的类库,在花了一点时间学习了解HtmlAgilityPack的API和 ...

  3. .Net解析html文档类库HtmlAgilityPack完整使用说明

    在前几篇文章中([搜房网房产数据采集程序demo--GeckoWebBrowser实例] )都有提到一个解析html的C#类库HtmlAgilityPack, 今天终于有时间整理一下,并把Demo分享 ...

  4. iOS平台XML解析类库对比和安装说明

    在iPhone开发中,XML的解析有很多选择,iOS SDK提供了NSXMLParser和libxml2两个类库,另外还有很多第三方类库可选,例如TBXML.TouchXML.KissXML.Tiny ...

  5. 控制台程序的参数解析类库 CommandLine

    C#控制台程序的参数解析类库 CommandLine简单使用说明 前言 C#开发的控制台程序,默认接收string[] args参数.如果有多个参数需要输入时,可以按照顺序依次输入:但如果有些参数不是 ...

  6. C#解析html文档类库HtmlAgilityPack下载地址

    新:http://html-agility-pack.net/?z=codeplex 原:http://htmlagilitypack.codeplex.com/

  7. 黄聪:C#类似Jquery的html解析类HtmlAgilityPack基础类介绍及运用

    Html Agility Pack下载地址:http://htmlagilitypack.codeplex.com/ Html Agility Pack 源码中的类大概有28个左右,其实不算一个很复杂 ...

  8. 【转】C#类似Jquery的html解析类HtmlAgilityPack基础类介绍及运用

    Html Agility Pack下载地址:http://htmlagilitypack.codeplex.com/ Html Agility Pack 源码中的类大概有28个左右,其实不算一个很复杂 ...

  9. HTML解析组件HtmlAgilityPack使用

    HtmlAgilityPack是一个开源的解析HTML元素的类库,最大的特点是可以通过XPath来解析HMTL,如果您以前用C#操作过XML,那么使用起HtmlAgilityPack也会得心应手.目前 ...

随机推荐

  1. Java学习day7面向对象编程1-对象和类

    一.Java编程中对象和类的概念 1,什么是类? 答:类是客观存在的,抽象的,概念的东西. 2,什么是对象? 答:对象是具体的,实际的,代表一个事物.例如:车是一个类,汽车,自行车就是他的对象. 关于 ...

  2. 单调栈 && 洛谷 P2866 [USACO06NOV]糟糕的一天Bad Hair Day(单调栈)

    传送门 这是一道典型的单调栈. 题意理解 先来理解一下题意(原文翻译得有点问题). 其实就是求对于序列中的每一个数i,求出i到它右边第一个大于i的数之间的数字个数c[i].最后求出和. 首先可以暴力求 ...

  3. python学习第四十八天json模块与pickle模块差异

    在开发过程中,字符串和python数据类型进行转换,下面比较python学习第四十八天json模块与pickle模块差异. json 的优点和缺点 优点  跨语言,体积小 缺点 只能支持 int st ...

  4. webpack打包过程及开发过程

    1.传统: 1)分模块去定义js.js中要导出将来要被打包的方法module.exports 2)定义main.js入口文件(主文件).在此文件中,导入引用的js文件 var {add} = requ ...

  5. Vue 实现文件的上传

    要把文件上传的web,需要分几步? 答:三步 第一步:创建一个上传文件的标签 <input type="file" id="fileExport" @ch ...

  6. Jquery Ajax简单封装(集中错误、请求loading处理)

    Jquery Ajax简单封装(集中错误.请求loading处理) 对Jquery Ajax做了简单封装,错误处理,请求loading等,运用到项目中集中处理会很方便. 技术层面没有什么好说的,请求是 ...

  7. 总结const、readonly、static三者的区别【收藏、转载】20190614

    总结const.readonly.static三者的区别 const:静态常量,也称编译时常量(compile-time constants),属于类型级,通过类名直接访问,被所有对象共享! a.叫编 ...

  8. 2018-8-10-win10-uwp-读写XML

    title author date CreateTime categories win10 uwp 读写XML lindexi 2018-08-10 19:16:51 +0800 2018-2-13 ...

  9. 本地主机访问远程linux系统服务器上的jupyter notebook

    1,机器情况:服务器 centos python环境已经配置好了,在虚拟环境下安装了anaconda 并且在里面安装了jupyter notebook 2,主机是 windows     ipytho ...

  10. RedisTemplate 事务处理方法 watch multi exec 的使用

    @Autowired RedisTemplate<String,String> redisTemplate; redisTemplate.execute(new SessionCallba ...