项目需求经常会遇到一种场景,需要对远程网站特定页面自动抓取内容保存下来,比如抓取大网站的新闻存到本地作为自己网站的内容发布。本文将介绍使用HtmlAgilityPack组件来手动实现该功能,文章底部有该Demo的源码下载。
HtmlAgilityPack简介:HtmlAgilityPack是一款开源的Html解析类库,可方便地解析Html节点(包括批量节点和单个节点)。
抓取内容比较常见的情形是给定新闻列表页地址,从列表中批量抓取具体内容,比如一次性抓取该列表页20条记录的标题、详情等。
本例解析“国家电网北京市公司”网站新闻动态栏目的内容,地址为http://www.bj.sgcc.com.cn/html/main/col34/column_34_1.html
1、实现思路:
1)通过WebRequest获取列表页源码。
2)找到列表位置,找出循环内容的规律。
3)循环从每条记录详情的链接地址获取Html源代码,解析出需要的内容后插入到数据库中。
2、创建.NET项目
创建ASP.NET MVC项目,通过NuGet包管理器引用HtmlAgilityPack,安装到项目中。
当然也可以采用手动引用dll文件的方式实现对该类库的引用,在此不做细述。
3、解析列表
列表页地址为http://www.bj.sgcc.com.cn/html/main/col34/column_34_1.html,网页源代码找到列表位置的html,是这样的:
在开始解析这段html之前,需要稍微了解下XPath 语法。
XPath(XML Path Language) 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。
可以参考https://www.w3school.com.cn/xpath/xpath_syntax.asp来深入了解XPath,本文将通过实际应用来解释其用法。
新闻列表的循环体是个ul,我们通过他的class=”list”来定位到唯一的元素块。
- “titlenum:25,pagesize:20” tag=
“_columninfolist”
- objid=
“6016”class
- =
“list”
- >
<li> <a target=“_self” href=“/html/main/col8/2020-09/04/20200904083721729904716_1.html”>公司全力确保服贸会安全可靠供电 a><span>
- 2020-09-04
span
- >
li
- >
<li> <a target=“_self” href=“/html/main/col8/2020-09/01/20200901170631439110530_1.html”>华灯“体检”工作启动 喜迎国庆节到来 a><span>
- 2020-09-01
span
- >
li
- >
用以下代码获得所有
- 的集合:
string url = "http://www.bj.sgcc.com.cn/html/main/col34/column_34_1.html"; //要抓取页面的地址
//使用WebRequest获取指定网页的源代码
System.Net.WebRequest rGet = System.Net.WebRequest.Create(url);
System.Net.WebResponse rSet = rGet.GetResponse();
System.IO.Stream s = rSet.GetResponseStream();
System.IO.StreamReader reader = new System.IO.StreamReader(s, System.Text.Encoding.GetEncoding("utf-8")); //注意编码与源网页一致
//sourceHtml为页面返回的html
string sourceHtml = reader.ReadToEnd();
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(sourceHtml);
HtmlAgilityPack.HtmlNodeCollection list = doc.DocumentNode.SelectNodes("//ul[@class=\'list\']/li");
最后一句doc.DocumentNode.SelectNodes(“//ul[@class=\’list\’]/li”);得到了所有
- 的集合。我们看一下SelectNodes方法里面的参数“//ul[@class=\’list\’]/li”。
首先双斜杠//含义:从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
//ul[@class=\’list\’]的含义是,找到文档中class等于list的ul元素。也可以通过元素的id、style等定位,原则就是能得到唯一的元素。
后面紧跟的/li会匹配到上个节点内的所有
- 元素,这样就得到了所有的循环内容集合。
4、通过循环分别解析出新闻详情
在这一步,在循环内分别获取每条新闻的详情页地址,然后WebRequest获取内容页源代码,再解析出标题、发表时间和详情。
先获取一条新闻详情页的源代码,看看Html内容,找到需要的部分:
看得出,包含它们最近的节点是
HtmlAgilityPack.HtmlNode sectionNode = docContent.DocumentNode.SelectSingleNode("//div[@class=\'txtcon\']");
title = sectionNode.SelectSingleNode("h1/span").InnerText;
dateandtime = sectionNode.SelectSingleNode("div[2]/span/span").InnerText;
detail = sectionNode.SelectSingleNode("div[@class=\'cont\']").InnerText;
1)解析标题的内容时,节点内的元素块只有唯一的
,通过h1/span可以得到准确的标题内容。
2)解析发布时间时,div[2]/span/span其中的div[2]表示该节点内的第二个div元素,也就是发表时间所在的
3)解析详情时用了div[@class=\’cont\’],通过class属性来定位,当然也可以用类似解析发布时间时的div[3]。
用一个单独的详情页测试解析没问题后,下一步就可以嵌套到上一步的循环中了。
5、组合列表页和详情页,完成循环解析插入
首先将获取网页源代码的方法提取出来,方便调用。方法为string GetUrlContent(string url),传入参数为页面地址。
HtmlAgilityPack.HtmlNodeCollection list = doc.DocumentNode.SelectNodes("//ul[@class=\'list\']/li");
foreach (HtmlAgilityPack.HtmlNode item in list)
{ HtmlAgilityPack.HtmlNode a = item.SelectSingleNode("a");
href = a.Attributes["href"].Value;
string contentUrl = "http://www.bj.sgcc.com.cn/" href;
string contentDetail = GetUrlContent(contentUrl);
docContent.LoadHtml(contentDetail); HtmlAgilityPack.HtmlNode sectionNode = docContent.DocumentNode.SelectSingleNode("//div[@class=\'txtcon\']");
title = sectionNode.SelectSingleNode("h1/span").InnerText;
dateandtime = sectionNode.SelectSingleNode("div[2]/span/span").InnerText;
detail = sectionNode.SelectSingleNode("div[@class=\'cont\']").InnerText;
Response.Write(title "
" dateandtime "
" detail "
");
}
循环体内简单粗暴的用Response.Write()显示出解析到的内容,此处更换成插入数据操作即可。
看一下运行后的结果:
本实例完整的代码下载地址(百度网盘):
链接:https://pan.baidu.com/s/1l_ivgv3mKXm8JY-8NIs-iA
提取码:6ipg
内容出处:,
声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时修改或删除。文章链接:http://www.yixao.com/share/11694.html