背景
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獲取到的,并且不需要鑒權,這可就太方便啦。我們稍微改一下參數就可以一次性拉取到排行數據了。
https://docs.microsoft.com/api/challenges/17c618cc-3c82-4a29-b2c6-d78b1de10b98/leaderboard?%24top=100&%24skip=0
F12打開網頁調試,拿到對應挑戰賽排行頁面的如上API地址,將參數top改為100,skip改為0即可。
這樣我們需要的其它技術或知識點如下:
?通過 HttpClient 使用 REST 服務?異步編程 async await 基本使用?使用 System.Text.Json 分析 Json 數據?LINQ 的基本方法
當然還有一些其它非必要,如匿名對象等。
實現思路
準備數據
先將拿到的三個挑戰賽排名數據創建一個匿名的對象
// API 里有課程名,直接寫出了為了好認
var apiInfo = new[]{(title:"C# 學習",url:"https://docs.microsoft.com/api/challenges/17c618cc-3c82-4a29-b2c6-d78b1de10b98/leaderboard?%24top=100&%24skip=0"),(title:"ASP.NET Core 開發",url:"https://docs.microsoft.com/api/challenges/b64cc891-e999-4652-909b-d545698a2e60/leaderboard?%24top=100&%24skip=0"),(title:".NET 移動應用",url:"https://docs.microsoft.com/api/challenges/38ec3c07-3ce6-4fb8-b423-b79166202364/leaderboard?%24top=100&%24skip=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 獲取挑戰排行榜的數據
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) };
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 不了解,可以去看一下楊老師的相關課程。
// 簡單分析一下
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