c# 使用HtmlAgilityPack解析Html
作者:傅小灰 发布时间:2021-06-22 11:50:11
HtmlAgilityPack 是一个开源的快速解析Html的C#类库。简单理解,它可以像解析Xml一样,将Html根据XPATH转化为一个个Node节点,并支持调整节点以及节点的各种属性。
传送门:官网 | Github源码
多种方式加载Html
主要加载方式有3类:从网络链接加载、从字符串文本中加载、从文件加载
var doc = new HtmlDocument();
//直接通过url加载
doc = new HtmlWeb().Load("https://www.baidu.com/");
//通过字符串加载
doc.LoadHtml(result);
//通过html文件加载 可指定编码方式
doc.Load(@"c://index.html",Encoding.UTF8)
HtmlNode常用方法
使用SelectNodes()和SelectSingleNode()方法(类似解析XML格式数据的XmlDocument)来获取的目标节点,分别对应HtmlNodeCollection和HtmlNode两个类。
"//"表示从根节点开始查找,两个斜杠"//"表示查找所有childnodes;一个斜杠"/"表示只查找第一层的childnodes(即不查找grandchild);点斜杠"./"表示从当前结点而不是根结点开始查找(只在xpath最开始出现)
注意:
id class 属性匹配大小写敏感
xpath匹配下标从1开始
1. 通过属性和路径匹配来选择对应的节点
var node = doc.DocumentNode;
//选择不包含class属性的div节点
var result = node.SelectNodes(".//div[not(@class)]");
//选择不包含class和id属性的div节点
var result = node.SelectNodes(".//div[not(@class) and not(@id)]");
//选择class中包含"expire"的span节点
var result = node.SelectNodes(".//span[contains(@class,'expire')]");
//选择class中不包含"expire"的span节点
var result = node.SelectNodes(".//span[not(contains(@class,'expire'))]");
//选择class="expire"的span节点
var result = node.SelectNodes(".//span[@class='expire']");
//选择id="expire"的div节点下第一个div节点
var result = node.SelectSingleNode(".//div[@id='expire']/div[1]");
2. 获取节点文本内容
根据需求不同,通过不同的方式来获取相应的文本内容。
OuterHtml:返回包含当前节点在内的所有Html
InnerHtml:返回当前节点内所有子节点Html
InnerText:返回当前节点内去除所有Html后的文本内容
<div id="title">
<p>
<a class="MainTitle" href="https://www.cnblogs.com/cplemom/" rel="external nofollow" rel="external nofollow" rel="external nofollow" >傅小灰</a>
</p>
</div>
以上面的Html为例
var node= doc.DocumentNode.SelectSingleNode("//div[@id='title'/p]");
node.OuterHtml; //返回结果:<p><a class="MainTitle"href="https://www.cnblogs.com/cplemom/" rel="external nofollow" rel="external nofollow" rel="external nofollow" >傅小灰</a></p>
node.InnerHtml; //返回结果:<a class="MainTitle"href="https://www.cnblogs.com/cplemom/" rel="external nofollow" rel="external nofollow" rel="external nofollow" >傅小灰</a>
node.InnerText; //返回结果:傅小灰
3. 获取/修改节点属性值
以上面的Html举例,我们获取到了a标签为node节点。我们想要获取a标签指向的链接地址,并修改为我们设定的地址。这里以href属性举例,同样可以用在class/src/id等属性上。
var node= doc.DocumentNode.SelectSingleNode("//div[@id='title'/p/a]");
//第二个参数为找不到对应属性时返回的默认值
var url = node.GetAttributeValue("href", "");//返回结果:https://www.cnblogs.com/cplemom/
//设置属性值
node.SetAttributeValue("href", "http://www.cplemom.com/");
//获取所有属性值
var list = node.Attributes.ToList();
4. 删除/替换节点
继续以上面的Html举例,我们获取到了a标签为node节点。
对于我们不需要的内容,我们只需要调用节点Remove方法即可。
var node= doc.DocumentNode.SelectSingleNode("//div[@id='title'/p/a]");
node.Remove();//删除节点
一个很常见的场景就是我们需要移除a标签,但是要在html上下文中保留a标签的文本。
PS : a标签内的文本在HtmlDocument中其实是一个text类型的node节点。所以我们可以通过删除a标签,保留text标签的方式来完成目标。
node.ParentNode.RemoveChild(node,true);
true表示留下a标签的子节点只删除a标签,在这里就表示保留下“傅小灰”text节点; false表示将此结点连同所有子节点一起删除。
换个角度思考下,当前node节点代表的是单个a标签,那么如果p标签下存在多个a标签需要处理,或者node节点指向的就是p标签呢?当然,我们可以通过获取所有a标签然后循环处理的方式来实现,但是还有没有别的更好的处理方式呢?
这里提供一个思路,获取所有的文本内容,新建为一个text节点,然后替换掉当前节点。
node.ParentNode.ReplaceChild(HtmlNode.CreateNode(item.InnerText), node);
几个常见使用场景和解决方案
1. 获取所有的img标签
//通过Descendants获取所有的子后代节点中的img标签
var list = node.Descendants("img");
//通过Xpath匹配获取所有img标签
var list = node.SelectNodes("//img");
2. 通过url访问时需要携带cookie等验证信息
有些页面需要携带验证信息才能访问,比如用户中心,订单列表等,这时候直接通过HtmlWeb类获取html会被拒绝。有个简单的方式就是通过HttpClient请求到对应的html内容,再使用HtmlDocument加载。其实HtmlWeb说白了也是封装的HttpWebRequest进行网络请求的,所以暴露一个委托给外部用以修改请求上下文。
var web = new HtmlWeb();
web.PreRequest = new HtmlWeb.PreRequestHandler(GetRequest);
var node = web.Load("https://www.cplemom.com/");
public static bool GetRequest(HttpWebRequest req)
{
req.Headers.Add("Host", "www.cplemom.com");
req.Headers.Add("Cookie", "xxxxxxxxxxxxx");
return true;
}
总结
用到现在,个人感觉上面的方法已经可以实现90%以上的的Html解析相关需求了,更多方便快捷的方法还是到官网的API文档进行了解吧。
来源:https://www.cnblogs.com/cplemom/p/13388613.html


猜你喜欢
- 1.SpringCache的概念首先我们知道jpa,jdbc这些东西都是一些规范,比如jdbc,要要连接到数据库,都是需要用到数据库连接,预
- Java的NIO包中,有一个专门用于发送UDP数据包的类:DatagramChannel,UDP是一种无连接的网络协议,一般用于发送一些准确
- 一、系统介绍本系统实现扑克的分发,抢地主,电脑自动出牌等功能。二、系统展示1.扑克分发2.抢地主3.出牌4.游戏胜利三、系统实现Card.j
- 内存泄漏:是指内存得不到GC的及时回收,从而造成内存占用过多,从而导致程序Crash,也就是常说的OOM。 一、static 先来看下面一段
- SpringMVC异常处理机制(一)项目前准备首先参照文章Spring课程工程构建+SpringMVC简介及其快速入门搭建项目搭建好一个项目
- Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而
- Java异常简介Java异常是Java提供的一种识别及响应错误的一致性机制。Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证
- 开发 Web 应用的思路实现一个简单的 JSP/Servlet。搭建创建 Web 应用工程的环境。创建 Web 应用工程。Web 应用工程的
- 最新需要在项目启动后立即执行某个方法,然后特此记录下找到的四种方式注解@PostConstruct使用注解@PostConstruct是最常
- 前言从windows窗口的概念开始,通过对比去理解Android窗口体系,本文没有深入源码,重在理解概念代码都是抄来抄去,概念也是互相借鉴
- 下面一段代码给大家介绍了android 自定义顶部导航栏控件功能,具体代码如下所示:class HeaderBar @JvmOverload
- 隐式转换可以通过消除不必要的类型转换来提高源代码的可读性。但是,因为隐式转换不需要程序员将一种类型显式强制转换为另一种类型,所以
- springBoot是java开发中会经常用到的框架,那么在实际项目中项目配置了springBoot框架,应该如何在项目中读取配置文件中的参
- 这篇文章主要介绍了springboot+springmvc实现登录拦截,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学
- 本文实例分析了C#中float的取值范围和精度。分享给大家供大家参考。具体分析如下:float类型的表现形式:默认情况下,赋值运算符右侧的实
- 测试类中的问题和解决思路问题在测试类中,每个测试方法都有以下两行代码:ApplicationContext ac = new ClassPa
- final关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值。 通常final定义的变量为常量。如:final double
- Spring @RequestParam对象绑定在Spring中,如果在方法参数列表中使用@RequestParam标注多个参数,会让映射方
- 本文参考文档Add Flutter to existing apps。首先有一个可以运行的原生项目第一步:新建Flutter moduleT
- 如下所示:import org.apache.commons.lang.StringUtils; public class Test {