.NetCore实践爬虫系统(一)解析网页内容
爬虫系统的意义
爬虫的意义在于采集大批量数据,然后基于此进行加工/分析,做更有意义的事情。谷歌,百度,今日头条,天眼查都离不开爬虫。
今日目标
今天我们来实践一个最简单的爬虫系统。根据Url来识别网页内容。
网页内容识别利器:HtmlAgilityPack
GitHub地址
HtmlAgilityPack官网
HtmlAgilityPack的stackoverflow地址
至今Nuget已有超过900多万的下载量,应用量十分庞大。它提供的文档教程也十分简单易用。
Parser解析器
HtmlParse可以让你解析HTML并返回HtmlDocument
- FromFile从文件读取
/// <summary>
/// 从文件读取
/// </summary>
public void FromFile() {
var path = @"test.html";
var doc = new HtmlDocument();
doc.Load(path);
var node = doc.DocumentNode.SelectSingleNode("//body");
Console.WriteLine(node.OuterHtml);
}
- 从字符串加载
/// <summary>
/// 从字符串读取
/// </summary>
public void FromString()
{
var html = @"<!DOCTYPE html>
<html>
<body>
<h1>This is <b>bold</b> heading</h1>
<p>This is <u>underlined</u> paragraph</p>
<h2>This is <i>italic</i> heading</h2>
</body>
</html> ";
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
var htmlBody = htmlDoc.DocumentNode.SelectSingleNode("//body");
Console.WriteLine(htmlBody.OuterHtml);
}
- 从网络加载
/// <summary>
/// 从网络地址加载
/// </summary>
public void FromWeb() {
var html = @"https://www.cnblogs.com/";
HtmlWeb web = new HtmlWeb();
var htmlDoc = web.Load(html);
var node = htmlDoc.DocumentNode.SelectSingleNode("//div[@id='post_list']");
Console.WriteLine("Node Name: " + node.Name + "\n" + node.OuterHtml);
}
Selectors选择器
选择器允许您从HtmlDocument中选择HTML节点。它提供了两个方法,可以用XPath表达式筛选节点。XPath教程
SelectNodes() 返回多个节点
SelectSingleNode(String) 返回单个节点
简介到此为止,更全的用法参考 http://html-agility-pack.net
查看网页结构
我们以博客园首页为示例。用chrome分析下网页结构,可采集出推荐数,标题,内容Url,内容简要,作者,评论数,阅读数。
编码实现
建立一个Article用来接收文章信息。
public class Article
{
/// <summary>
///
/// </summary>
public string Id { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 概要
/// </summary>
public string Summary { get; set; }
/// <summary>
/// 文章链接
/// </summary>
public string Url { get; set; }
/// <summary>
/// 推荐数
/// </summary>
public long Diggit { get; set; }
/// <summary>
/// 评论数
/// </summary>
public long Comment { get; set; }
/// <summary>
/// 阅读数
/// </summary>
public long View { get; set; }
/// <summary>
///明细
/// </summary>
public string Detail { get; set; }
/// <summary>
///作者
/// </summary>
public string Author { get; set; }
/// <summary>
/// 作者链接
/// </summary>
public string AuthorUrl { get; set; }
}
然后根据网页结构,查看XPath路径,采集内容
/// <summary>
/// 解析
/// </summary>
/// <returns></returns>
public List<Article> ParseCnBlogs()
{
var url = "https://www.cnblogs.com";
HtmlWeb web = new HtmlWeb();
//1.支持从web或本地path加载html
var htmlDoc = web.Load(url);
var post_listnode = htmlDoc.DocumentNode.SelectSingleNode("//div[@id='post_list']");
Console.WriteLine("Node Name: " + post_listnode.Name + "\n" + post_listnode.OuterHtml);
var postitemsNodes = post_listnode.SelectNodes("//div[@class='post_item']");
var articles = new List<Article>();
var digitRegex = @"[^0-9]+";
foreach (var item in postitemsNodes)
{
var article = new Article();
var diggnumnode = item.SelectSingleNode("//span[@class='diggnum']");
//body
var post_item_bodynode = item.SelectSingleNode("//div[@class='post_item_body']");
var titlenode = post_item_bodynode.SelectSingleNode("//a[@class='titlelnk']");
var summarynode = post_item_bodynode.SelectSingleNode("//p[@class='post_item_summary']");
//foot
var footnode = item.SelectSingleNode("//div[@class='post_item_foot']");
var authornode = footnode.ChildNodes[1];
var commentnode = item.SelectSingleNode("//span[@class='article_comment']");
var viewnode = item.SelectSingleNode("//span[@class='article_view']");
article.Diggit = int.Parse(diggnumnode.InnerText);
article.Title = titlenode.InnerText;
article.Url = titlenode.Attributes["href"].Value;
article.Summary = titlenode.InnerHtml;
article.Author = authornode.InnerText;
article.AuthorUrl = authornode.Attributes["href"].Value;
article.Comment = int.Parse(Regex.Replace(commentnode.ChildNodes[0].InnerText, digitRegex, ""));
article.View = int.Parse(Regex.Replace(viewnode.ChildNodes[0].InnerText, digitRegex, ""));
articles.Add(article);
}
return articles;
}
查看采集结果
看到结果就惊呆了,竟然全是重复的。难道是Xpath语法理解不对么?
重温下XPath语法
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的
表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。
XPath 通配符可用来选取未知的 XML 元素
通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
我测试了几个语法如:
//例1,会返回20个
var titlenodes = post_item_bodynode.SelectNodes("//a[@class='titlelnk']");
//会报错,因为这个a并不直接在bodynode下面,而是在子级h3元素的子级。
var titlenodes = post_item_bodynode.SelectNodes("a[@class='titlelnk']");
然后又实验了一种:
//Bingo,这个可以,但是强烈指定了下级h3,这就稍微麻烦了点。
var titlenodes = post_item_bodynode.SelectNodes("h3//a[@class='titlelnk']");
这里就引申出了一个小问题:如何定位子级的子级?用通配符*可以么?
//返回1个。
var titlenodes= post_item_bodynode.SelectNodes("*//a[@class='titlelnk']")
能正确返回1,应该是可以了,我们改下代码看下效果。 然后和博客园首页数据对比,结果吻合。
所以我们可以得出结论:
XPath搜索以//开头时,会匹配所有的项,并不是子项。
直属子级可以直接跟上 node名称。
只想查子级的子级,可以用*代替子级,实现模糊搜索。
改过后代码如下:
public List<Article> ParseCnBlogs()
{
var url = "https://www.cnblogs.com";
HtmlWeb web = new HtmlWeb();
//1.支持从web或本地path加载html
var htmlDoc = web.Load(url);
var post_listnode = htmlDoc.DocumentNode.SelectSingleNode("//div[@id='post_list']");
//Console.WriteLine("Node Name: " + post_listnode.Name + "\n" + post_listnode.OuterHtml);
var postitemsNodes = post_listnode.SelectNodes("div[@class='post_item']");
var articles = new List<Article>();
var digitRegex = @"[^0-9]+";
foreach (var item in postitemsNodes)
{
var article = new Article();
var diggnumnode = item.SelectSingleNode("*//span[@class='diggnum']");
//body
var post_item_bodynode = item.SelectSingleNode("div[@class='post_item_body']");
var titlenode = post_item_bodynode.SelectSingleNode("*//a[@class='titlelnk']");
var summarynode = post_item_bodynode.SelectSingleNode("p[@class='post_item_summary']");
//foot
var footnode = post_item_bodynode.SelectSingleNode("div[@class='post_item_foot']");
var authornode = footnode.ChildNodes[1];
var commentnode = footnode.SelectSingleNode("span[@class='article_comment']");
var viewnode = footnode.SelectSingleNode("span[@class='article_view']");
article.Diggit = int.Parse(diggnumnode.InnerText);
article.Title = titlenode.InnerText;
article.Url = titlenode.Attributes["href"].Value;
article.Summary = titlenode.InnerHtml;
article.Author = authornode.InnerText;
article.AuthorUrl = authornode.Attributes["href"].Value;
article.Comment = int.Parse(Regex.Replace(commentnode.ChildNodes[0].InnerText, digitRegex, ""));
article.View = int.Parse(Regex.Replace(viewnode.ChildNodes[0].InnerText, digitRegex, ""));
articles.Add(article);
}
return articles;
}
感谢apgk也提供了一种办法,也是ok的。
var titlenodes = post_item_bodynode.SelectNodes(post_item_bodynode.XPath+"//a[@class='titlelnk']");
源码
总结
demo到此结束。谢谢观看!
下篇继续构思如何构建自定义规则,让用户可以在页面自己填写规则去识别。
转载于:https://www.cnblogs.com/fancunwei/p/9581168.html
.NetCore实践爬虫系统(一)解析网页内容相关推荐
- 分布式网页爬虫系统 设计和实现
分布式爬虫设计和实现 1.总体设计 功能模块划分: 数据抓取引擎 1.1 调研市面上爬虫框架或库 pholcus colly gocrawl 开发工具:goland 开发语言:golang 数据存储: ...
- 转载自android 开发--抓取网页解析网页内容的若干方法(网络爬虫)(正则表达式)
转载自http://blog.csdn.net/sac761/article/details/48379173 android 开发--抓取网页解析网页内容的若干方法(网络爬虫)(正则表达式) 标签: ...
- Chrome安装爬虫必备插件:Xpath Helper高效解析网页内容(实测有效)
Chrome安装爬虫必备插件:Xpath Helper(最新教程) 1. 谷歌浏览器xpath helper插件的安装和使用 2. 谷歌浏览器xpath helper插件的作用 3. 谷歌浏览器xpa ...
- 手把手教你搭建一个基于Java的分布式爬虫系统
http://blog.51cto.com/xpleaf/2093952 1 概述 在不用爬虫框架的情况,经过多方学习,尝试实现了一个分布式爬虫系统,并且可以将数据保存到不同地方,类似MySQL.HB ...
- 分布式多爬虫系统——架构设计
前言: 在爬虫的开发过程中,有些业务场景需要同时抓取几百个甚至上千个网站,此时就需要一个支持多爬虫的框架.在设计时应该要注意以下几点: 代码复用,功能模块化.如果针对每个网站都写一个完整的爬虫,那其中 ...
- 设计 一个高性能爬虫系统
资料来源 http://www.xuebuyuan.com/1296711.html 开源中国 http://my.oschina.net/eshijia/blog/136595 最近看了一篇来自纽约 ...
- 实际的网络爬虫系统通常是几种爬虫技术相结合实现的。
分类 网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型:通用网络爬虫(General Purpose Web Crawler).聚焦网络爬虫(Focused Web Crawler).增量式网 ...
- 【爬虫】一种基于网页相似度去重的爬虫系统
现有的技术方案: 为解决网页重复抓取的问题,现有的爬虫系统主要是以URL的维度来进行去重.简单的,直接将URL放入数据库中或者放入Redis的Set集合进行去重,复杂一些的,会综合参数名的构成和参数值 ...
- 爬虫系统基础框架 何时使用爬虫框架? requests库 + bs4来实现简单爬虫
转载请注明出处https://www.cnblogs.com/alexlee666/p/10180519.html,谢谢! 文中图片来自于我的简书博客. 一. 爬虫用途和本质: 网络爬虫顾名思义即模仿 ...
- 基于SpringBoot和Vue的分布式爬虫系统(JavaWeb)
前言 本期案例分享,学长给大家上点干货,手把手带你开发一个分布式爬虫系统.通过这个项目,你将学习到下面几点: 架构设计.如果设计一个通用的爬虫系统?一个系统支持爬取所有的网站. 分布式开发经验.分布式 ...
最新文章
- Java的异常:Error与Exception
- 时差法超声波流量计的原理
- 上周热点回顾(5.26-6.1)
- python @符号_Python金三角!python初学者很难绕过的坑,附教程资料
- postgresql安装之后修改默认用户密码
- 路由器上的lookback是什么?有什么作用?
- MySql字符集修改
- Python 学习笔记:class
- php 繁体自动转简体,php 繁体字转化为简体字
- 360极速浏览器插件不见了
- Jump视频实时抠图和语音降噪
- 安装centos 7
- 看图写英语作文关于计算机,看图写话英语作文模板
- JAVA练习243-唯一摩尔斯密码词
- 【百度大脑新品体验】手势识别
- Python学习笔记-Pygame
- Overload 和Override 的区别。
- 【华为机试真题 JAVA】奥运会排行榜-100
- CC2530F256RHAR 可提供规格SDK ZigBee应用
- 服务器定时自动备份MySQL数据库
热门文章
- poythoncode-实战5--excel 文件读取,文本文件,csv文件,存到系统中以大列表方式进行存储
- SQLSERVER函数判断当天是星期几
- mysql5.5安装最后一步一直无反应_吉林电泳型材安装
- linux 内存坏了,Linux的缓存内存 Cache Memory详解
- 安防监控芯片市场高清化趋势愈显 成行业共识
- zabbix批量操作
- ubuntu10下Eclipse中无法输入中文
- BN=批归一化+缩放位移=(batchNorm层+scale层)
- 论文:GeoGebra 在线数学应用函数演示
- 5月25 python3.6—pymouse—pyhook_3安装问题