背景
C#是我2012年在大学课程中接触的,.NET Framework 我也一直使用至今。从2014年.NET 开源,2019年发布.NET Core 3 的时候,公司刚好有 Nvidia Jetson 平台 Linux 嵌入式设备的开发任务,.NET 又刚是适用于 Windows, Linux, 和 macOS 的跨平台解决方案。对于我个人来说也容易上手,就又开始了对其新的认识历程,按照微软的官方.NET 文档也做了几个练手的项目。
21年底的时候帮大学的老师开发了一个园林的信用评价系统,这个任务可以说是相当紧急了,前后端全部一个人完成,还只有元旦休息的这三四天的时间,虽然任务艰巨,算是个挑战,刚好那个时候在学完了.NET的《创建自己的第一个Web应用》,那就让挑战更猛烈些吧 —— 用.NET。其实也不是为了调整,学习一门语言更好的方式是跟着项目边学边写,硬着头皮爆肝午夜十二点,还好是按时完成了交付。
那个时候自己写的认证和授权的中间件还把Jwt认证实现了一遍。好吧,这就是没有老师带的后果吧。然后当我撸完项目闲下来,就在朋友圈看到杨中科老师发的.NET录播分享。真,真是及时雨啊。
再然后收到杨老师的朋友圈邀请,我真的不是觊觎奖品里有杨老师还没发的.NET的新书。
就这样我参加了 《.NET 20周年学习挑战赛|寻找 .NET 技术达人》 ,并完成了三个模块的挑战。通过这三个挑战,系统性的对.NET的能力有了更全的了解。发现.NET 可以搞更多有意思的事情,通过Xamarin技术的初步认识,我玩起了在桌边因为疫情居家落灰的小米手表,最近在研究用Xamarin技术怎么通过蓝牙模拟HID设备控制电脑,做个腕间飞鼠。
回到挑战赛,挑战赛在5月21日结束,我就想问问举办方关于杨老师的书,啊,不,是奖品,您看我有机会嘛?
自己动手丰衣足食,自己抓取分析下排行榜不就完了嘛。
所需知识
基于我已经完成的代码,我整理了一下,完成这个小项目(一切皆项目)所需要的知识点。
首先我们需要获取到排行榜的数据,就可能需要用到爬虫技术,进行网页的抓取和分析,之后在分析网页请求的时候,我发现官方的排行榜数据是通过API获取到的,并且不需要鉴权,这可就太方便啦。我们稍微改一下参数就可以一次性拉取到排行数据了。
代码语言:javascript复制https://docs.microsoft.com/api/challenges/17c618cc-3c82-4a29-b2c6-d78b1de10b98/leaderboard?$top=100&$skip=0
F12打开网页调试,拿到对应挑战赛排行页面的如上API地址,将参数top改为100,skip改为0即可。
这样我们需要的其它技术或知识点如下:
•通过 HttpClient 使用 REST 服务•异步编程 async await 基本使用•使用 System.Text.Json 分析 Json 数据•LINQ 的基本方法
当然还有一些其它非必要,如匿名对象等。
实现思路
准备数据
先将拿到的三个挑战赛排名数据创建一个匿名的对象
代码语言:javascript复制// API 里有课程名,直接写出了为了好认
var apiInfo = new[]{
(
title:"C# 学习",
url:"https://docs.microsoft.com/api/challenges/17c618cc-3c82-4a29-b2c6-d78b1de10b98/leaderboard?$top=100&$skip=0"
),
(
title:"ASP.NET Core 开发",
url:"https://docs.microsoft.com/api/challenges/b64cc891-e999-4652-909b-d545698a2e60/leaderboard?$top=100&$skip=0"
),
(
title:".NET 移动应用",
url:"https://docs.microsoft.com/api/challenges/38ec3c07-3ce6-4fb8-b423-b79166202364/leaderboard?$top=100&$skip=0"
)
};
设计存储数据的对象
创建一个存储挑战者学习信息的类,里面含有挑战者名称 userName
,完成挑战的数量 learnNum
和所完成的挑战 learnName
,以及一个添加新的完成挑战类目的方法 addClass
public class StudyInfo {
public StudyInfo(string userName, int learnNum, List<string> learnName)
{
this.userName = userName;
this.learnNum = learnNum;
this.learnName = learnName;
}
public string userName { get;private set; }
public int learnNum { get; private set; }
public List<string> learnName{ get; private set; }
public void addClass(string className) {
this.learnNum ;
this.learnName.Add(className);
}
}
排行榜数据获取
使用 HttpClient 获取挑战排行榜的数据
代码语言:javascript复制var client = new HttpClient();
foreach (var item in apiInfo)
{
string jsoninfo = await client.GetStringAsync(item.url);
// 分析
}
数据解析和输出
进行 json 数据的解析,通过具体返回的内容我们可以很容易的发现字段所代表的含义。
totalScoreUnits
表示本次挑战的小节数,只有完成这么多小节的学习才算完成了挑战,使用 var user = new List<StudyInfo>();
来存储用户的挑战信息,根据解析的结果进行新建或者更新数据。最后将结果进行逆序排列输出到文件。
在 System.Text.Json
的使用上我们需要注意几点:
•获取的 json 的值时,我们需要进行强制数据转换,保存用户学习课程的 score
需要用 float 类型•遍历 json 数组时,需要先将其 JsonNode 对象进行 AsArray()
处理,JsonNode 是不支持 foreach 的•最后输出 json 序列化时,需要进行 options
的设置,这样才不会将中文字符进行编码
var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };
代码语言:javascript复制
JsonNode jsonNode = JsonNode.Parse(jsoninfo)!;
// 合集总课程数
int classnum = (int)jsonNode["totalScoreUnits"]!;
foreach (var uinfo in jsonNode["results"]!.AsArray())
{
// 学完的用户
if ((float)uinfo["score"]! == classnum)
{
// 看看有没有这个用户的信息
var temp = user.FirstOrDefault(e => e.userName == (string)uinfo["userDisplayName"]!);
if (temp is null)
{
// 初始创建这个用户
user.Add(new StudyInfo((string)uinfo["userDisplayName"]!, 1, new List<string>() { item.title }));
}
else
{
// 有,更新学习数据
temp.addClass(item.title);
}
}
}
// 逆序排序后转为json字符存入文件
var jsonRes = JsonSerializer.Serialize(user.OrderByDescending(x => x.learnNum), options);
File.WriteAllText("output.json", jsonRes);
进行简单的分析
这里主要使用了 Linq 操作,如果对 Linq 不了解,可以去看一下杨老师的相关课程。
代码语言:javascript复制// 简单分析一下
Console.WriteLine($"完成《C# 学习》{user.Where(x=>x.learnName.IndexOf("C# 学习")>-1).Count()}人");
Console.WriteLine($"完成《ASP.NET Core 开发》{user.Where(x => x.learnName.IndexOf("ASP.NET Core 开发") > -1).Count()}人");
Console.WriteLine($"完成《.NET 移动应用》{user.Where(x => x.learnName.IndexOf(".NET 移动应用") > -1).Count()}人");
Console.WriteLine($"仅完成 1 次挑战 {user.Where(x => x.learnNum == 1).Count()}人");
Console.WriteLine($"仅完成 2 次挑战 {user.Where(x => x.learnNum ==2).Count()}人");
Console.WriteLine($"共完成 3 次挑战 {user.Where(x => x.learnNum == 3).Count()}人n 分别是");
foreach (var item in user.Where(x => x.learnNum == 3)) {
Console.WriteLine(item.userName);
}
最后
详细的代码实现我放在 .Net 达人挑战赛参与分析 github[1] ,感兴趣的同学可以自取。
.NET 是一个免费的跨平台开源开发人员平台,希望 .NET 可以发展的越来越好。如果你希望可以更深入的了解一下 .NET ,强烈推荐去B站关注杨中科老师[2]去学习他的相关视频课程。你也可以关注“dotNet跨平台”公众号了解更多的.NET技术;.NET 学习和社区活动 可以关注:MSReactor 。
References
[1]
.Net 达人挑战赛参与分析 github: https://github.com/marin1993/.NetChallengesInfo.git
[2]
B站关注杨中科老师: https://space.bilibili.com/27948784