爬虫,是一种按照一定的规则,自动地抓取网站的程序或者脚本。`.NET`写爬虫非常简单,并能轻松优化性能。今天我将分享一段简短的代码,爬出博客园前200页精华内容,然后通过微小的改动,将代码升级为多线程爬虫,让爬虫速度提升数倍;最后将对爬到了内容进行一些有趣的分析。

我的演示代码通过LINQPad运行,可以在这里找到最新的LINQPad下载链接:https://www.linqpad.net/Download.aspx

这些代码同样可以运行在Visual Studio中。其中.Dump()方法可以在Visual Studio中搜索并安装NuGet包即可兼容:

Install-Package LINQPad

爬虫的三要素

经过我“多年”的爬虫操作的经验,我认为爬虫无非就是:

  1. 下载网站数据;

  2. 解析/保存网站数据;

  3. 分析数据与下个页面之间的关系,以便继续下载下个页面数据;

下面我将通过代码演示这三点。

下载网站数据

换作以前,有WebRequest/WebClient/RestSharp之类的选择,但如今已经都被HttpClient取代了,HttpClient同时内置于.NET Framework 4.5/netstandard 1.1及以后的版本,不用安装第三方包。

代码使用也非常简单:

var client = new HttpClient();
string response = await client.DownloadStringAsync("https://www.cnblogs.com");

其中response就是从博客园下载的html字符串。

解析网站数据

.NET解析html有多个包可供选择,如HtmlAgilityPack、CsQuery等。但AngleSharp由于其简单好用、功能强大,已经也成为解析html的不错之选。

AngleSharp是开源项目,Github地址是:https://github.com/AngleSharp/AngleSharp。

近期还加入了.NET Foundation(.NET基金会),官网地址是:https://anglesharp.github.io 。

使用AngleSharp解析html过程(在`INQPad`,按Ctrl+Shift+P快速安装NuGet包):

Install-Package AngleSharp
Install-Package Newtonsoft.Json

使用代码如下:

var parser = new HtmlParser();
IHtmlDocument dom = parser.ParseDocument(@"<ul>    <li>  <a href=""cnblogs.com"">博客园</a>    <a href=""baidu.com"">百度</a>   <a href=""google.com"">谷歌</a>  </li>
<ul>");
var data = dom.QuerySelectorAll("ul li a").Select(x => new
{   Link = x.GetAttribute("href"),   Title = x.TextContent
}).Dump();

运行效果:

Link Title
cnblogs.com 博客园
baidu.com 百度
google.com 谷歌

然后这些数据可以通过JSON序列化,保存到桌面上:

File.WriteAllText(@"C:\Users\sdfly\Desktop\cnblogs.json",     JsonConvert.SerializeObject(data));

注意:在解析网页数据时,可能还需要灵活运用`正则表达式`,来抓取没那么直观的信息。

页面与页面之间的关系

我们找到博客园的分页器,打开F12开发者工具,用鼠标定位到分页器:

如图,注意到,每一个页面按钮,都对应了一个不同的链接地址,如第2页,对应的的链接是:/sitehome/p/2,第3页,对应的是:/sitehome/p/3。

博客园首页内容一共有200页,因此只需将在每一页拼接一个$"/sitehome/p/{页面数码}"即可。

代码与优化

根据上面的知识,可以轻松将博客园首页200页数据爬出来:

var http = new HttpClient();
var parser = new HtmlParser(); for (var page = 1; page <= 200; ++page)
{   string pageData = await http.GetStringAsync($"https://www.cnblogs.com/sitehome/p/{page}");   IHtmlDocument doc = await parser.ParseDocumentAsync(pageData); doc.QuerySelectorAll(".post_item").Select(tag => new  {   Title = tag.QuerySelector(".titlelnk").TextContent,  Page = page,   UserName = tag.QuerySelector(".post_item_foot .lightblue").TextContent,  PublishTime = DateTime.Parse(Regex.Match(tag.QuerySelector(".post_item_foot").ChildNodes[2].TextContent, @"(\d{4}\-\d{2}\-\d{2}\s\d{2}:\d{2})", RegexOptions.None).Value),    CommentCount = int.Parse(tag.QuerySelector(".post_item_foot .article_comment").TextContent.Trim()[3..^1]),   ViewCount = int.Parse(tag.QuerySelector(".post_item_foot .article_view").TextContent[3..^1]),    BriefContent = tag.QuerySelector(".post_item_summary").TextContent.Trim(),   }).Dump(page);
}

运行结果如下:

多线程优化

这个爬虫将200页数据全部爬完,根据我的网速,需要76秒,任务管理器显示如下(接收带宽只有1.7Mbps):

在.NET/C#中,只需对此代码的for循环修改为LINQ,然后而加以使用Parallel LINQ,即可将代码并行化:

Enumerable.Range(1, 200)  // for循环转换为LINQ .AsParallel()         // 将LINQ并行化   .AsOrdered()          // 按顺序保存结果(注意并非按顺序执行)   .SelectMany(page => {   return Task.Run(async() => // 非异步代码使用async/await,需要包一层Task   {   string pageData = await http.GetStringAsync($"https://www.cnblogs.com/sitehome/p/{page}".Dump());    IHtmlDocument doc = await parser.ParseDocumentAsync(pageData); return doc.QuerySelectorAll(".post_item").Select(tag => new   {   Title = tag.QuerySelector(".titlelnk").TextContent,  Page = page,   UserName = tag.QuerySelector(".post_item_foot .lightblue").TextContent,  PublishTime = DateTime.Parse(Regex.Match(tag.QuerySelector(".post_item_foot").ChildNodes[2].TextContent, @"(\d{4}\-\d{2}\-\d{2}\s\d{2}:\d{2})", RegexOptions.None).Value),    CommentCount = int.Parse(tag.QuerySelector(".post_item_foot .article_comment").TextContent.Trim()[3..^1]),   ViewCount = int.Parse(tag.QuerySelector(".post_item_foot .article_view").TextContent[3..^1]),    BriefContent = tag.QuerySelector(".post_item_summary").TextContent.Trim(),   }); }).GetAwaiter().GetResult(); // 等待Task执行完毕  })

通过这个非常简单的优化,在我的电脑上,即可将运行时间降低为14.915秒,速度快了5倍!同时任务管理器显示网络下载流量为(16.5Mbps):

数据简单分析

现在我们得到了博客园首页博客简要数据,我将其保存到桌面的一个json文件中(大家也可以试着保存为其它格式,如数据库中)。当然少不了分析一番。使用LINQPad,可以很轻松地分析这些数据,并生成图表。

分析基础

所有的分析,都基于以下代码:

void Main()
{   var data = JsonConvert.DeserializeObject<List<CnblogsItem>>(
File.ReadAllText(@"C:\Users\sdfly\Desktop\cnblogs.json"));
}   class CnblogsItem
{   public string TItle { get; set; }   public int Page { get; set; }   public string UserName { get; set; }    public DateTime PublishTime { get; set; }   public int CommentCount { get; set; }   public int ViewCount { get; set; }  public string BriefContent { get; set; }
}

我创建了一个CnblogsItem的类,用来反序列号桌面上json文件的数据。返序列化完成后,这些数据保存在data变量中。

什么时间发文章浏览量最高?

Util.Chart(data    .GroupBy(x => x.PublishTime.Hour)   .Select(x => new { Hour = x.Key, ViewCount = 1.0 * x.Sum(v => v.ViewCount) }) .OrderByDescending(x => x.Hour),    x => x.Hour,    y => y.ViewCount).Dump();

结果:

可见,每天上午9点发文章浏览量最高,凌晨3-4点发文章浏览量最低(谁会

在晚上3-4点爬起来看东西呢?)

星期几发的文章多?

Util.Chart(data   .GroupBy(x => x.PublishTime.DayOfWeek)  .Select(x => new { WeekDay = x.Key, ArticleCount = x.Count() })   .OrderBy(x => x.WeekDay),   x => x.WeekDay.ToString(),  y => y.ArticleCount).Dump();

结果:

可见:星期一、二、三的文章最多,星期四、五逐渐冷淡,星期六、星期日最少。——但星期六比星期日又多一点。(是因为996造成的吗?)

哪位大佬发文最多(取前9名)?

结果:

可见,大佬周国通竟然在前200页博客中,独占54篇,我点开了他的博客(https://www.cnblogs.com/tylerzhou/)看了一下,竟然都有相当的质量——绝无放水可言,深入讲了许多.NET的测试系列教程,确实是大佬!

结语

实际应用的爬虫可能不像博客园这么简单,爬虫如果深入,可以遇到很多很多非常有意思的情况。

今天谨希望通过这个简单的博客园爬虫,让大家多多享受写.NET/C#代码的乐趣?。

出处:微信公众号【DotNet骚操作】微信可能无法留言,可点击“阅读原文”转到博客园留言。原文链接:https://www.cnblogs.com/sdflysha/p/20190826-cnblogs-crawler-home.html

.NET轻松写博客园爬虫相关推荐

  1. Splinter学习--模拟攥写博客园随笔

    为了验证Splinter的多种动作,这里将使用Splinter模拟博客园里面新增随笔的操作. 第一步:登录博客园,方法同Splinter学习--初探2,模拟博客园登录 第二步:跳转到"添加随 ...

  2. python 装饰器写博客园

    需求 1),启动程序,首页面应该显示成如下格式: 欢迎来到博客园首页 1:请登录 2:请注册 3:文章页面 4:日记页面 5:评论页面 6:收藏页面 7:注销 8:退出程序 2),用户输入选项,3~6 ...

  3. 用markdown写博客园

    /p/cnblogs_markdown.html markdown的使用:https://blog.csdn.net/a1b2c300/article/details/53891125 吗都是 不是真 ...

  4. 用Word写博客园文章

    http://www.cnblogs.com/dunitian/services/metablogapi.aspx 效果如下: 啦啦啦啦啦啦我是卖报的小行家 /// <summary> / ...

  5. python教程是用什么博客写的-Python爬虫入门教程:博客园首页推荐博客排行的秘密...

    1. 前言 虽然博客园注册已经有五年多了,但是最近才正式开始在这里写博客.(进了博客园才知道这里面个个都是人才,说话又好听,超喜欢这里...)但是由于写的内容都是软件测试相关,热度一直不是很高.看到首 ...

  6. 『Python开发实战菜鸟教程』实战篇:爬虫快速入门——统计分析CSDN与博客园博客阅读数据

    文章目录 0x01:引子 首先介绍一下网络爬虫是什么,可以用来做什么? 这里简单探讨一下网络爬虫的合法性 正式进入爬虫实战前,需要我们了解下网页结构 HTML CSS JScript 写一个简单的 H ...

  7. nodejs爬虫与python爬虫_【nodeJS爬虫】前端爬虫系列 -- 小爬「博客园」

    写这篇 blog 其实一开始我是拒绝的,因为爬虫爬的就是cnblog博客园.搞不好编辑看到了就把我的账号给封了:). 言归正传,前端同学可能向来对爬虫不是很感冒,觉得爬虫需要用偏后端的语言,诸如 ph ...

  8. Python爬虫入门教程 54-100 博客园等博客网站自动评论器

    爬虫背景 爬虫最核心的问题就是解决重复操作,当一件事情可以重复的进行的时候,就可以用爬虫来解决这个问题,今天要实现的一个基本需求是完成"博客园" 博客的自动评论,其实原理是非常简单 ...

  9. java 模拟登陆exe_Java简单模拟登陆和爬虫实例---博客园老牛大讲堂

    鉴于有人说讲的不清楚,我这里再详细补充一下:更新日期:2017-11-23 本片文章适合初学者,只简单说了一下爬虫怎么用,和一个简单的小实例.不适合你的就可以不看了.----博客园老牛大讲堂 1.什么 ...

最新文章

  1. 修改centos7的网卡名
  2. Android 图片合成:添加蒙板效果 不规则相框 透明度渐变效果的实现
  3. CompletableFuture详解~join与get的区别
  4. sublime text 3 插件推荐?
  5. python脚本简化jar操作命令
  6. 每日linux命令学习-历史指令查询(history、fc、alias)
  7. 使用 grep 查找所有包含指定文本的文件
  8. 分布式存储系统学习笔记(一)—什么是分布式系统(7)—跨机房部署的三种方案
  9. 伍楼阁使用的WordPress代码高亮插件使用说明
  10. 5G通信演进和常见名词释义
  11. 从产品经理招聘信息分析现代产品经理职责
  12. Go语言%d,%p,%v等占位符
  13. 2022双非计算机保研经验(西电,北邮,厦大,浙软)
  14. openjudge 4978 宠物小精灵之收服
  15. 【原创】C#玩高频数字彩快3的一点体会
  16. 《23种设计模式之原型模式(2种实现)》
  17. 2021年安全员-B证(江西省)新版试题及安全员-B证(江西省)考试技巧
  18. Rasterino 2.3.0版for AI 2020 (Illustrator裁切图片一键PS修图插件)
  19. 一线城市房价的理性思考
  20. JAVA大学实用教程(第四版)课后习题一、二章答案及解析(自整理)

热门文章

  1. 知道第一章计算机基础知识作业答案,大学计算机基础作业答案
  2. 如何更改您的iPhone铃声
  3. sfm点云代码_VisualSFM使用方法与心得
  4. java 简单json和对象相互转换
  5. 知物由学 | 干货!一文了解安卓APP逆向分析与保护机制
  6. 子商城管理之签到活动
  7. MyBatis学习总结(17)——Mybatis分页插件PageHelper
  8. centos7.0搭建svn服务器
  9. centos服务器解决vsftp连接时的“550 Create directory operation failed.”错误
  10. Android添加单元测试的方法与步骤