C#+HtmlAgilityPack
C#+HtmlAgilityPack—糗事百科桌面版V2.0
最近在浏览以前自己上传的源码,发现在糗事百科桌面端源码评论区中,有人说现在程序不能用了。查看了一下源码运行情况,发现是正则表达式解析问题。由于糗百的网页版链接和网页格式稍有变化,导致解释失败。虽然可以通过更改正则表达,重新获网页的信息,但比较复杂,出错率较高(技术有限)。因此第二个版本采用HtmlAgilityPack类库解析Html。
1. HtmlAgilityPack类库
HtmlAgilityPack是一个解析Html文档的一个类库,当然也能够支持XML文件,该类库比.NET自带的XML解析库要方便灵活多,主要是HtmlAgilityPack支持XPath路径表达式,通过XPath表达式能够快速定位到文档中的某个节点。有了它,解析网页不成问题,接下来简述一下HtmlAgilityPack的使用方法。
HtmlAgilityPack 有3个比较重要的类型。
- HtmlDocument : 加载Html文档(string),并解析成有层次的对象结构。
 - HtmlNode : 元素节点类型,该类型提供许多非常有用的方法,下面会将重点的方法都介绍一遍。
 - HtmlNodeCollection : html节点集合类型。
 
通过例子来讲解这三个类型的使用,可能会更加的清晰,html源码,如下:
<div>
    <div id="content" class="c1">
        <p id="p1" class="pStyle"  title="HtmlAgility">段落1</p>
        <p id="p2" class= "pStyle">段落2</p>
    </div>
    <div id="content2" class="c1">
        <p class="pStyle">段落3</p>
        <span>
            <h1>hello</h1>
        <span>
    <div>
</div>
使用HtmlDocument类型解析上面的html源码。
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlStr);
HtmlNode rootNode = htmlDoc.DocumentNode;//获取文档的根节点
通过该类型的 LoadHtml 方法解析html字符串,然后获取html文档的根节点,当然该类型还有一个 Load方法,支持从文件中,url 或者流中加载html文档。获取了HtmlNode 类型的根节点之后,利用根节点与XPath路径表达可以获取文档中任意的节点。
获取所有class=pStyle的p节点
string xpath = "//p[@class='pStyle']";
HtmlNodeCollection pNodes = rootNode.SelectNodes(xpath);
利用HtmlNode类型的SelectNodes(xpath)方法可以获取所有class为pStyle的p标签。但是这次会把文档中所有的样式类为pStyle的p标签都获取到。如果只想获取“div id="content"”容器下面的。只需要更改xpath路径表达式即可做到。
string xpath = "//div[@id='content']/p[@class='pStyle']";
HtmlNodeCollection pNodes = rootNode.SelectNodes(xpath);
通过在之前的路径上加上div[@id='content']进行限定即可。如果只想找"div id="content"”容器下面的class=pStyle 且 title=HtmlAgility的p标签代码如下。
string xpath = @"//div[@id='content']/p[@class='pStyle' and @title='HtmlAgility']";
HtmlNodeCollection pNodes = rootNode.SelectNodes(xpath);
通过XPath可以很方便的定位到文档中的节点,并通过节点类型的属性或者方法来获取节点的属性值和文本值以及内部html。
获取节点的文本和class属性值,代码如下:
string xpath = @"//div[@id='content']/p[@class='pStyle' and @title='HtmlAgility']";
HtmlNodeCollection pNodes = rootNode.SelectNodes(xpath);
string nodeText=pNodes[0].InnerText;
string classValue = pNodes[0].GetAttributeValue("class", "");
HtmlNode类型的属性和方法如下:
InnerText : 节点的文本值
InnerHtml: 节点内部的html字符串
GetAttributeValue :通过节点的属性名字获取属性值,该方法有三个重载的版本,可以获取分别返回bool,string、int 三种类型的属性值。
上面都在简介HtmlAgilityPack类库的使用,下面简单介绍一下XPath。
- // 所有后代节点,从根部开始,跟SelectNodes方法前面的节点没关系 例子://div: 所有名为div的节点
 - . 表示当前节点,与SelectNodes方法前面的节点相关 例子:./div :当前节点下的所有名为div节点
 - .. 表示父节点,与SelectNodes方法前面的节点相关 例子: ../div :当前节点的父节点下的div节点
 - @ 选取属性 例子://div/@id:所有包含id属性的div
 
XPath不仅仅可以通过路径定位,还可以对定位后的节点集做一些限制,使得选择更加精准。
- /root/book[1] 节点集中的第一个节点
 - /root/book[last()] 节点集中最后一个节点
 - /root/book[position() - 2] 节点集中倒数第三个节点集
 - /root/book[position() < 5] 节点集中前五个节点集
 - /root/book[@id] 节点集中含有属性id的节点集
 - /root/book[@id='chinese'] 节点集中id属性值为chinese的节点集
 - /root/book[price > 35]/title 节点集中book的price元素值大于35的title节点集
 
XPath对于路径匹配,谓语限制都可以使用通配符。
- /div/* div节点下面的所有节点类型
 - //div[@*] div下面的所有属性
 
此外XPath还能进行逻辑运算
- | ——例:/root/book[1] | /root/book[3]:两个节点集的合并—— (
+,*) - //div[@id='test1' and @class='divStyle']—— 找寻所有 id=test1 和class=divStyle 的div节点
 - //div[not(@class)]——找寻不包括class属性div节点 
(or, and, not, =, !=, >, <, >=) 
最后在提供一个小技巧,在网页中打开开发者工具,可以直接定位到相应的html节点,然后右键可以直接获取该节点在文档中的XPath路径,如下图:

好了, HtmlAgilityPack类库的使用就介绍到这里。
2. 使用HtmlAgilityPack解析糗百
关于糗百的网页结构分析,在这篇文章使用HttpGet协议与正则表达实现桌面版的糗事百科 已经详细介绍了,在此就不在赘述了,下面简介一下抓取糗百段子的关键代码。
/// <summary>
/// 获取笑话列表
/// </summary>
/// <param name="htmlContent"></param>
public static  List<JokeItem> GetJokeList(int pageIndex)
{
    string htmlContent=GetUrlContent(GetWBJokeUrl(pageIndex));
    List<JokeItem> jokeList = new List<JokeItem>();
    HtmlDocument htmlDoc = new HtmlDocument();
    htmlDoc.LoadHtml(htmlContent);
    HtmlNode rootNode=htmlDoc.DocumentNode;
    string xpathOfJokeDiv = "//div[@class='article block untagged mb15']";
    string xpathOfJokeContent = "./a/div[@class='content']/span";
    string xpathOfImg = "./div[@class='author clearfix']/a/img";
    try
    {
        HtmlNodeCollection jokeCollection = rootNode.SelectNodes(xpathOfJokeDiv);
        int jokeCount = jokeCollection.Count;
        JokeItem joke;
        foreach (HtmlNode jokeNode in jokeCollection)
        {
            joke = new JokeItem();
            HtmlNode contentNode = jokeNode.SelectSingleNode(xpathOfJokeContent);
            if (contentNode != null)
            {
                joke.JokeContent = Regex.Replace(contentNode.InnerText, "(\r\n)+", "\r\n");
            }
            else
            {
                joke.JokeContent = "";
            }
            HtmlNode imgornameNode = jokeNode.SelectSingleNode(xpathOfImg);
            if (imgornameNode != null)
            {
                joke.NickName = imgornameNode.GetAttributeValue("alt", "");
                joke.HeadImage = GetWebImage("http:"+imgornameNode.GetAttributeValue("src", ""));
                joke.HeadImage = joke.HeadImage != null ? new Bitmap(joke.HeadImage, 50, 50) : null;
            }
            else
            {
                joke.NickName = "匿名用户";
                joke.HeadImage = null;
            }
            jokeList.Add(joke);
        }
    }
    catch{}
    return jokeList;
}
下载图片的代码如下:
private static Image GetWebImage(string webUrl)
{
    try
    {
        Encoding encode = Encoding.GetEncoding("utf-8");//网页编码==Encoding.UTF8
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(webUrl));
        HttpWebResponse ress = (HttpWebResponse)req.GetResponse();
        Stream sstreamRes = ress.GetResponseStream();
        return System.Drawing.Image.FromStream(sstreamRes);
    }
    catch { return null; }
}
需要注意的是:
获取糗百头像的地址为://pic.qiushibaike.com/system/avtnew/3281/32814675/medium/2017070317185381.JPEG" ,一定要在前面加上"http:" 才能正确的下载头像。
效果如下:

3. 小结
通过HtmlAgilityPack来解析文档,很轻巧灵活,主要是不容易出错。觉得这个解析包和Python的Beautiful Soup 库有异曲同工之妙,都是解析网页的好工具。
本文的源码下载链接:https://github.com/StartAction/qbDesktop
C#+HtmlAgilityPack的更多相关文章
- C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子)
		
第一次接触HtmlAgilityPack是在5年前,一些意外,让我从技术部门临时调到销售部门,负责建立一些流程和寻找潜在客户,最后在阿里巴巴找到了很多客户信息,非常全面,刚开始是手动复制到Excel, ...
 - 用WebRequest +HtmlAgilityPack 从外网抓取数据到本地
		
相信大家对于WebRequest 并不陌生,我们在C#中发请求的方式,就是创建一个WebRequest .那么如果我们想发一个请求到外网,比如国内上不了的一些网站,那么该怎么做呢? 其实WebRequ ...
 - 【C#】获取网页内容及HTML解析器HtmlAgilityPack的使用
		
最近经常需要下载一些东西,而这个下载地址又会经过层层跳转,每个页面上都有很多广告,烦不胜烦,所以做了一个一键获得最终下载地址的小工具.使用C#,来获取网页内容,然后通过HtmlAgilityPack获 ...
 - 爬虫技术 -- 进阶学习(十)网易新闻页面信息抓取(htmlagilitypack搭配scrapysharp)
		
最近在弄网页爬虫这方面的,上网看到关于htmlagilitypack搭配scrapysharp的文章,于是决定试一试~ 于是到https://www.nuget.org/packages/Scrapy ...
 - 黄聪:C#类似Jquery的html解析类HtmlAgilityPack基础类介绍及运用
		
Html Agility Pack下载地址:http://htmlagilitypack.codeplex.com/ Html Agility Pack 源码中的类大概有28个左右,其实不算一个很复杂 ...
 - HTML解析器HtmlAgilityPack的一些使用总结(C#)
		
哎~本来这些总结是作为使用时的快速备注,但是用不上了.实际应用当中HtmlAgilityPack的可靠性不太稳定,一主要问题是:-> 一些字符会出现乱码或者变成'?',如韩语字符.由于我是已经有 ...
 - 2012-09-10 23:30 如何解决HtmlAgilityPack得到的InnerText中有残留的script、样式的问题
		
那么如何解决HtmlAgilityPack得到的InnerText中有残留的script.样式的问题呢,在google上搜索“HtmlAgilityPack script innerText”找到了s ...
 - HtmlAgilityPack组件
		
HtmlAgilityPack组件用于解析Html字符串,一个典型的应用场景是用于网页爬虫. 示例程序 using Common.Tools; using Datebase.Entity; using ...
 - 使用HtmlAgilityPack解析Html(非常好用)
		
/// <summary> /// 设计成一个exe,解决WebBrowser控件内存泄漏的问题. /// </summary> public partial class Ma ...
 - HtmlAgilityPack中通过sibling才能得到对应的InnerText和form,option等tag的子节点
		
[背景] 之前使用HtmlAgilityPack期间,遇到了2个bug: 1. InnerText没有包含对应字符串(但是用NextSibling.InnerText却可以得到) 对于html: ? ...
 
随机推荐
- POJ 2427 Smith's Problem Pell方程
			
题目链接 : http://poj.org/problem?id=2427 PELL方程几个学习的网址: http://mathworld.wolfram.com/PellEquation.html ...
 - SQL分页的几种方式
			
1.使用Row_number() over(order by columnName)函数来作为标示分页(下面的例子都是以last_seen来排序的,要求取顺序为20-30行的数据) SELECT Us ...
 - 【hdu 4333】Revolving Digits
			
[链接]http://acm.hdu.edu.cn/showproblem.php?pid=4333 [题意] 就是给你一个数字,然后把最后一个数字放到最前面去,经过几次变换后又回到原数字,问在这些数 ...
 - Java 学习(16):集合框架
			
Java 集合框架 早在Java 2中之前,Java就提供了特设类.比如:Dictionary, Vector, Stack, Properties 这些类用来存储和操作对象组. 虽然这些类都非常有 ...
 - Linux 进程通信之管道
			
管道是单向的.先进先出的,它把一个进程的输出和还有一个进程的输入连接在一起.一个进程(写进程)在管道的尾部写入数据,还有一个进程(读进程)从管道的头部读出数据.数据被一个进程读出后,将被从管道中删除, ...
 - 《Java实战开发经典》第五章5.3
			
package xiti5; public class Third { public static void main(String[] args) { T t=new T("want yo ...
 - amazeui学习笔记一(开始使用4)--Web App 相关
			
amazeui学习笔记一(开始使用4)--Web App 相关 一.总结 1.桌面图标(Touch icon)解决方案:终极方案:link标签的rel和href属性: <link rel=&qu ...
 - 7.zookeeper集群搭建(windows环境下)
			
转自:https://www.cnblogs.com/xuxiuxiu/p/5868481.html 本次zk测试部署版本为3.4.6版本,下载地址http://mirrors.cnnic.cn/ap ...
 - win10系统64位安装git后右键运行git bash here生成一个mintty.exe.stackdump文件后闪退解决方案
			
在其他win10电脑上复制了一个null.sys文件,替换C:\Windows\System32\drivers\null.sys,搞定.
 - COGS——C2098. Asm.Def的病毒
			
http://www.cogs.pro/cogs/problem/problem.php?pid=2098 ★☆ 输入文件:asm_virus.in 输出文件:asm_virus.out ...