1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        .NET輕松寫博客園爬蟲

        共 6100字,需瀏覽 13分鐘

         ·

        2020-09-06 21:45

        爬蟲,是一種按照一定的規(guī)則,自動(dòng)地抓取網(wǎng)站的程序或者腳本。`.NET`寫爬蟲非常簡單,并能輕松優(yōu)化性能。今天我將分享一段簡短的代碼,爬出博客園前200頁精華內(nèi)容,然后通過微小的改動(dòng),將代碼升級(jí)為多線程爬蟲,讓爬蟲速度提升數(shù)倍;最后將對(duì)爬到了內(nèi)容進(jìn)行一些有趣的分析。


        本文演示的代碼,可以從這里下載:https://github.com/sdcb/blog-data/tree/master/2019/20190826-cnblogs-crawler-home


        我的演示代碼通過LINQPad運(yùn)行,可以在這里找到最新的LINQPad下載鏈接:https://www.linqpad.net/Download.aspx


        這些代碼同樣可以運(yùn)行在Visual Studio中。其中.Dump()方法可以在Visual Studio中搜索并安裝NuGet包即可兼容:

        Install-Package LINQPad



        爬蟲的三要素

        經(jīng)過我“多年”的爬蟲操作的經(jīng)驗(yàn),我認(rèn)為爬蟲無非就是:

        1. 下載網(wǎng)站數(shù)據(jù);

        2. 解析/保存網(wǎng)站數(shù)據(jù);

        3. 分析數(shù)據(jù)與下個(gè)頁面之間的關(guān)系,以便繼續(xù)下載下個(gè)頁面數(shù)據(jù);


        下面我將通過代碼演示這三點(diǎn)。


        下載網(wǎng)站數(shù)據(jù)

        換作以前,有WebRequest/WebClient/RestSharp之類的選擇,但如今已經(jīng)都被HttpClient取代了,HttpClient同時(shí)內(nèi)置于.NET Framework 4.5/netstandard 1.1及以后的版本,不用安裝第三方包。


        代碼使用也非常簡單:

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

        其中response就是從博客園下載的html字符串。


        解析網(wǎng)站數(shù)據(jù)

        .NET解析html有多個(gè)包可供選擇,如HtmlAgilityPack、CsQuery等。但AngleSharp由于其簡單好用、功能強(qiáng)大,已經(jīng)也成為解析html的不錯(cuò)之選。


        AngleSharp是開源項(xiàng)目,Github地址是:https://github.com/AngleSharp/AngleSharp。


        近期還加入了.NET Foundation(.NET基金會(huì)),官網(wǎng)地址是:https://anglesharp.github.io 。


        使用AngleSharp解析html過程(`INQPad`,按Ctrl+Shift+P快速安裝NuGet):

        Install-Package AngleSharpInstall-Package Newtonsoft.Json


        使用代碼如下:

        var parser = new HtmlParser();IHtmlDocument dom = parser.ParseDocument(@"

        運(yùn)行效果:

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


        然后這些數(shù)據(jù)可以通過JSON序列化,保存到桌面上:

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


        注意:在解析網(wǎng)頁數(shù)據(jù)時(shí),可能還需要靈活運(yùn)用`正則表達(dá)式`,來抓取沒那么直觀的信息。


        頁面與頁面之間的關(guān)系

        我們找到博客園的分頁器,打開F12開發(fā)者工具,用鼠標(biāo)定位到分頁器:


        如圖,注意到,每一個(gè)頁面按鈕,都對(duì)應(yīng)了一個(gè)不同的鏈接地址,如第2頁,對(duì)應(yīng)的的鏈接是:/sitehome/p/2,第3頁,對(duì)應(yīng)的是:/sitehome/p/3。


        博客園首頁內(nèi)容一共有200頁,因此只需將在每一頁拼接一個(gè)$"/sitehome/p/{頁面數(shù)碼}"即可。



        代碼與優(yōu)化

        根據(jù)上面的知識(shí),可以輕松將博客園首頁200頁數(shù)據(jù)爬出來:

        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);}

        運(yùn)行結(jié)果如下:


        多線程優(yōu)化

        這個(gè)爬蟲將200頁數(shù)據(jù)全部爬完,根據(jù)我的網(wǎng)速,需要76秒,任務(wù)管理器顯示如下(接收帶寬只有1.7Mbps):

        .NET/C#中,只需對(duì)此代碼的for循環(huán)修改為LINQ,然后而加以使用Parallel LINQ,即可將代碼并行化:

        Enumerable.Range(1,?200)??//?for循環(huán)轉(zhuǎn)換為LINQ    .AsParallel()         // 將LINQ并行化    .AsOrdered()          // 按順序保存結(jié)果(注意并非按順序執(zhí)行)    .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執(zhí)行完畢    })

        通過這個(gè)非常簡單的優(yōu)化,在我的電腦上,即可將運(yùn)行時(shí)間降低為14.915秒,速度快了5倍!同時(shí)任務(wù)管理器顯示網(wǎng)絡(luò)下載流量為(16.5Mbps):



        數(shù)據(jù)簡單分析

        現(xiàn)在我們得到了博客園首頁博客簡要數(shù)據(jù),我將其保存到桌面的一個(gè)json文件中(大家也可以試著保存為其它格式,如數(shù)據(jù)庫中)。當(dāng)然少不了分析一番。使用LINQPad,可以很輕松地分析這些數(shù)據(jù),并生成圖表。


        分析基礎(chǔ)

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

        void Main(){    var data = JsonConvert.DeserializeObject>(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; }}

        我創(chuàng)建了一個(gè)CnblogsItem的類,用來反序列號(hào)桌面上json文件的數(shù)據(jù)。返序列化完成后,這些數(shù)據(jù)保存在data變量中。


        什么時(shí)間發(fā)文章瀏覽量最高?

        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();

        結(jié)果:


        可見,每天上午9點(diǎn)發(fā)文章瀏覽量最高,凌晨3-4點(diǎn)發(fā)文章瀏覽量最低(誰會(huì)


        在晚上3-4點(diǎn)爬起來看東西呢?)


        星期幾發(fā)的文章多?

        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();

        結(jié)果:

        可見:星期一、二、三的文章最多,星期四、五逐漸冷淡,星期六、星期日最少?!瞧诹刃瞧谌沼侄嘁稽c(diǎn)。(是因?yàn)?span style="color: rgb(206, 145, 120);">996造成的嗎?)


        哪位大佬發(fā)文最多(取前9名)?

        Util.Chart(data        .GroupBy(x => x.UserName)        .Select(x => new { UserName = x.Key, ArticleCount = x.Count() })        .OrderByDescending(x => x.ArticleCount)        .Take(9),    x => x.UserName,    y => y.ArticleCount).Dump();

        結(jié)果:

        可見,大佬周國通竟然在前200頁博客中,獨(dú)占54篇,我點(diǎn)開了他的博客(https://www.cnblogs.com/tylerzhou/)看了一下,竟然都有相當(dāng)?shù)馁|(zhì)量——絕無放水可言,深入講了許多.NET的測試系列教程,確實(shí)是大佬!



        結(jié)語

        實(shí)際應(yīng)用的爬蟲可能不像博客園這么簡單,爬蟲如果深入,可以遇到很多很多非常有意思的情況。


        今天謹(jǐn)希望通過這個(gè)簡單的博客園爬蟲,讓大家多多享受寫.NET/C#代碼的樂趣?。

        往期精彩回顧




        【推薦】.NET Core開發(fā)實(shí)戰(zhàn)視頻課程?★★★

        .NET Core實(shí)戰(zhàn)項(xiàng)目之CMS 第一章 入門篇-開篇及總體規(guī)劃

        【.NET Core微服務(wù)實(shí)戰(zhàn)-統(tǒng)一身份認(rèn)證】開篇及目錄索引

        Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)

        .NET Core中的一個(gè)接口多種實(shí)現(xiàn)的依賴注入與動(dòng)態(tài)選擇看這篇就夠了

        10個(gè)小技巧助您寫出高性能的ASP.NET Core代碼

        用abp vNext快速開發(fā)Quartz.NET定時(shí)任務(wù)管理界面

        在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實(shí)現(xiàn)作業(yè)調(diào)度

        現(xiàn)身說法:實(shí)際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化

        關(guān)于C#異步編程你應(yīng)該了解的幾點(diǎn)建議

        C#異步編程看這篇就夠了

        給我好看

        您看此文用

        ??·?

        秒,轉(zhuǎn)發(fā)只需1秒呦~

        好看你就

        點(diǎn)點(diǎn)


        瀏覽 55
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品成人免费视频 | 国产在线欧美豆花 | 五月天激情成人网 | 免费一级A片 | 亚洲色图14p | 国产偷窥在线 | 骚逼视频网站 | 狠狠撸奇米影视 | 99久久婷婷国产综合精品草原 | 性生活三极片 |