NET 5 爬蟲框架/抓取數據


 爬蟲大家或多或少的都應該接觸過的,爬蟲有風險,抓數需謹慎。

 爬蟲有的是抓請求,有的是抓網頁再解析

本着研究學習的目的,記錄一下在 .NET Core 下抓取數據的實際案例。爬蟲代碼一般具有時效性,當我們的目標發生改版升級,規則轉換后我們寫的爬蟲代碼就會失效,需要重新應對。抓取數據的主要思路就是去分析目標網站的頁面邏輯,利用xpath、正則表達式等知識去解析網頁拿到我們想要的數據。

本篇主要簡單介紹三個組件的使用,HtmlAgilityPack、AngleSharp、PuppeteerSharp,前兩個可以處理傳統的頁面,無法抓取單頁應用,如果需要抓取單頁應用可以使用PuppeteerSharp。

新建一個控制台項目,抓取幾個站點的數據來試試,先做准備工作,添加一個IHotNews的接口。

using System.Collections.Generic;
using System.Threading.Tasks;

namespace SpiderDemo
{
    public interface IHotNews
    {
        Task<IList<HotNews>> GetHotNewsAsync();
    }
}

HotNews模型,包含標題和鏈接

namespace SpiderDemo
{
    public class HotNews
    {
        public string Title { get; set; }

        public string Url { get; set; }
    }
}

最終我們通過依賴注入的方式,將抓取到的數據展示到控制台中。

HtmlAgilityPack:

https://html-agility-pack.net/
https://github.com/zzzprojects/html-agility-pack

在項目中安裝HtmlAgilityPack組件

Install-Package HtmlAgilityPack

這里以博客園為抓取目標,我們抓取首頁的文章標題和鏈接。

using HtmlAgilityPack;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SpiderDemo
{
    public class HotNewsHtmlAgilityPack : IHotNews
    {
        public async Task<IList<HotNews>> GetHotNewsAsync()
        {
            var list = new List<HotNews>();

            var web = new HtmlWeb();

            var htmlDocument = await web.LoadFromWebAsync("https://www.cnblogs.com/");

            var node = htmlDocument.DocumentNode.SelectNodes("//*[@id='post_list']/article/section/div/a").ToList();

            foreach (var item in node)
            {
                list.Add(new HotNews
                {
                    Title = item.InnerText,
                    Url = item.GetAttributeValue("href", "")
                });
            }

            return list;
        }
    }
}

添加HotNewsHtmlAgilityPack.cs實現IHotNews接口,訪問博客園網址,拿到HTML數據后,使用xpath語法解析HTML,這里主要是拿到a標簽即可。

通過查看網頁分析可以得到這個xpath://*[@id='post_list']/article/section/div/a。

然后在Program.cs中注入IHotNews,循環遍歷看看效果。

using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace SpiderDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            IServiceCollection service = new ServiceCollection();

            service.AddSingleton<IHotNews, HotNewsHtmlAgilityPack>();

            var provider = service.BuildServiceProvider().GetRequiredService<IHotNews>();

            var list = await provider.GetHotNewsAsync();

            if (list.Any())
            {
                Console.WriteLine($"一共{list.Count}條數據");

                foreach (var item in list)
                {
                    Console.WriteLine($"{item.Title}\t{item.Url}");
                }
            }
            else
            {
                Console.WriteLine("無數據");
            }
        }
    }
}

Anglesharp

https://anglesharp.github.io/
https://github.com/AngleSharp/AngleSharp

在項目中安裝AngleSharp組件

Install-Package AngleSharp

同樣的,新建一個HotNewsAngleSharp.cs也實現IHotNews接口,這次使用AngleSharp抓取。

using AngleSharp;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace SpiderDemo
{
    public class HotNewsAngleSharp : IHotNews
    {
        public async Task<IList<HotNews>> GetHotNewsAsync()
        {
            var list = new List<HotNews>();

            var config = Configuration.Default.WithDefaultLoader();
            var address = "https://www.cnblogs.com";
            var context = BrowsingContext.New(config);
            var document = await context.OpenAsync(address);

            var cellSelector = "article.post-item";
            var cells = document.QuerySelectorAll(cellSelector);

            foreach (var item in cells)
            {
                var a = item.QuerySelector("section>div>a");
                list.Add(new HotNews
                {
                    Title = a.TextContent,
                    Url = a.GetAttribute("href")
                });
            }

            return list;
        }
    }
}

AngleSharp解析數據和HtmlAgilityPack的方式有所不同,AngleSharp可以利用css規則去獲取數據,用起來也是挺方便的

在Program.cs中注入IHotNews,循環遍歷看看效果。

using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace SpiderDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            IServiceCollection service = new ServiceCollection();

            service.AddSingleton<IHotNews, HotNewsAngleSharp>();

            var provider = service.BuildServiceProvider().GetRequiredService<IHotNews>();

            var list = await provider.GetHotNewsAsync();

            if (list.Any())
            {
                Console.WriteLine($"一共{list.Count}條數據");

                foreach (var item in list)
                {
                    Console.WriteLine($"{item.Title}\t{item.Url}");
                }
            }
            else
            {
                Console.WriteLine("無數據");
            }
        }
    }
}

PuppeteerSharp

https://www.puppeteersharp.com/
https://github.com/hardkoded/puppeteer-sharp

PuppeteerSharp是基於Puppeteer的,Puppeteer 是一個Google 開源的NodeJS 庫,它提供了一個高級API 來通過DevTools協議控制Chromium 瀏覽器。Puppeteer 默認以無頭(Headless) 模式運行,但是可以通過修改配置運行“有頭”模式。

PuppeteerSharp可以干很多事情,不光可以用來抓取單頁應用,還可以用來生成頁面PDF或者圖片,可以做自動化測試等。

在項目中安裝PuppeteerSharp組件

Install-Package PuppeteerSharp

使用PuppeteerSharp第一次會幫我們在項目根目錄中下載瀏覽器執行程序,這個取決於當前網速的快慢,建議手動下載后放在指定位置即可。

using PuppeteerSharp;
using System.Threading.Tasks;

namespace SpiderDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // 下載瀏覽器執行程序
            await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);

            // 創建一個瀏覽器執行實例
            using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                Args = new string[] { "--no-sandbox" }
            });

            // 打開一個頁面
            using var page = await browser.NewPageAsync();

            // 設置頁面大小
            await page.SetViewportAsync(new ViewPortOptions
            {
                Width = 1920,
                Height = 1080
            });
        }
    }
}

上面這段代碼是初始化PuppeteerSharp必要的代碼,可以根據實際開發需要進行修改,下面以"https://juejin.im"為例,演示幾個常用操作。

獲取單頁面的HTML

var url = "https://juejin.im";
await page.GoToAsync(url, WaitUntilNavigation.Networkidle0);
var content = await page.GetContentAsync();
Console.WriteLine(content);

可以看到頁面上的HTML全部被獲取到了,這時候就可以利用規則解析HTML,拿到我們想要的數據了。

保存為圖片

var url = "https://juejin.im/";
await page.GoToAsync(url, WaitUntilNavigation.Networkidle0);

await page.ScreenshotAsync("juejin.png");

保存為PDF

var url = "https://juejin.im/";
await page.GoToAsync(url, WaitUntilNavigation.Networkidle0);

await page.PdfAsync("juejin.pdf");

PuppeteerSharp的功能還有很多,比如頁面注入HTML、執行JS代碼等,使用的時候可以參考官網示例。

最后使用HtmlAgilityPack獲取博客園的標題內容

List<BlogInfo> blog = new List<BlogInfo>();
            HtmlWeb web = new HtmlWeb(); HtmlNodeCollection node; for (int i = 0; i < 10; i++) { node = web.Load($"https://www.cnblogs.com/#p{i}").DocumentNode.SelectNodes("//a[@class='post-item-title']"); foreach (var item in node) { blog.Add(new BlogInfo { Title = item.InnerText, Content = web.Load(item.GetAttributeValue("href", "")).DocumentNode.SelectSingleNode("//div[@id='cnblogs_post_body']").InnerHtml }); } }

xpath參考https://www.cnblogs.com/cplemom/p/13388613.html

推薦其他幾個爬蟲類庫

https://www.cnblogs.com/cang12138/p/7464226.html

https://www.cnblogs.com/cang12138/p/7486003.html

https://www.cnblogs.com/EminemJK/p/8920364.html

https://github.com/AngleSharp/AngleSharp

https://github.com/TsingJyujing/DataSpider

https://github.com/sjdirect/abot

https://github.com/zzzprojects/html-agility-pack

爬蟲29問

1、爬蟲很簡單嗎?

如果只是抓取幾個普通的網頁,爬蟲確實很簡單。在網絡上,我們經常可以看到,30分鍾入門python爬蟲這樣的教程。如果目標網站有反爬,或是需要抓取海量數據,那這就不是一件簡單的事了。本書就從爬蟲常見問題,爬蟲技巧,以及反爬蟲,海量爬取等方面,來講一下如何應對各種各樣的爬蟲狀況。

2、POST和GET有什么區別?

從本質上,沒有什么區別,都是http協議中的Method方法。只是POST在上傳數據時,使用了Body。同樣的,http協議中可以使用PUT,HEAD等方法,也可以自造協議如ABCD,只要服務器支持即可。

3、靜態網頁和動態網頁抓取有什么區別?

很久以前,大家會將html后綴的網頁叫靜態網頁,asp,php,jsp這類后綴的網頁叫動態網頁。但這種分法其實不科學,因為偽靜態技術是可以讓網頁后綴任意顯示的。我們可以查看服務器端返回的header信息,查看服務器軟件是Apache還是IIS,由這個信息判斷,當然,這個信息其實很多情況下也是可以更改的。

從爬蟲角度,沒有所謂的動態和靜態。網頁在服務器端生成,通過網絡傳送到客戶端,如果是在瀏覽器,瀏覽器渲染頁面並再加載網頁相關的css,js及圖片等資源。現在,人們更多的將通過js加載數據的網頁稱為動態網頁。

4、請求抓取一個網頁,需要注意哪些事情?

一般需要注意User-Agent以及Cookie,User-Agent代表瀏覽器類型,很多網站針對不同的瀏覽器使用不同的顯示輸出方式,Cookie攜帶了一些登錄信息,如果登錄成功一個網站,在下次請求中不使用Cookie,就會提示你沒有登錄了。在請求時,還需要注意請求的編碼和返回代碼的編碼,如果選錯編碼,看到的可能就是亂碼。

5、如何學習http協議

極力推薦大家使用fiddler軟件來分析http請求。這個工具自帶很多功能,方便查看請求的所有細節,還可以偽造請求,進行調試,統計等功能。

6、防盜鏈是怎么回事

在服務器上可以設置或是檢測請求Header看的Referer參數,如果為空,或是非本站域名,則不顯示數據。最常見的是圖片顯示禁止盜鏈或是不顯示。只需要在抓取時設置Referer即可。

7、網站數據沒列表的怎么抓取?

如果網站是通過搜索獲取的,則可以使用各種各樣的搜索詞來搜索,比如常見的文字,字母以及數字的組合,作為搜索條件搜索。

如果返回結果的內容是有數字id的,可以通過窮舉的方式進行爬取。

8、Silverlight類型的網站如何抓取:

可以打開Silverlight網站的頁面,查看源代碼,尋找xap的文字,看到類似<param name="Source" value="xxx/abc.xap" />的文字,然后下載xap文件,解壓后就可以看到很多dll文件,使用ilspy反編譯即可看到網站的調用過程。

9、類似12306類型的驗證碼怎么辦?

窮舉所有圖片樣本,只要量夠大,再結合人工標識,就可處理。

10、被封IP怎么辦?

被封IP后可以做以下調整:

1)減少請求頻率

2)更換User-Agent,可以試一下用百度等搜索引擎的

3)更換IP,可以adsl撥號,也可以代理服務

11、爬取海量的數據需注意什么?

比如一天抓取1000萬,就要分解到每秒,每機器,帶寬等方面,要使用分布式集群來抓取,可以使用redis這樣的列隊存儲任務。

12、返回的數據是加密的,怎么解密?

如果是網頁源代碼中加密,而頁面上正常顯示,這就是數據通過JavaScript渲染了,這時,可以通過瀏覽器的開發者工具,調試代碼,查看數據處理過程。如果不想深入研究,直接上PhantomJS或其它瀏覽器,操作網頁元素獲取數據。13、爬取中出現有驗證碼怎么辦?

普通的驗證碼,比如純字母或數字,不相互粘連,使用字庫或二值化,很容易處理。現在的驗證碼都是比較高級的,比如復雜的字符,拖動,點擊等,需要接入打碼平台進行人工打碼。

14、手機app數據要如何抓取

可以在手機上設置http代理,看看有無數據經過http協議傳送。如果沒有,那抓取難度會提高不少,有人通過反編譯或是群控點擊來抓取,但成本較高。很多小型的app,基本沒有安全意識,很多是直接調用的api,得到的是非常干凈的json數據。

15、如何上傳文件至網站?

上傳文件需要使用POST方法並設置Content-Type為multipart/form-data,發送的格式如下:

POST http://www.example.com HTTP/1.1

Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryA

------WebKitFormBoundaryA

Content-Disposition: form-data; name="text"

title

------WebKitFormBoundaryA

Content-Disposition: form-data; name="file"; filename="chrome.png"

Content-Type: image/png

二進制文件內容 ...

------WebKitFormBoundaryA--

以上部分為發送格式,其中WebKitFormBoundaryA是可以使用任意內容的,只要按這種格式即可。常見的Content-Type還有application/x-www-form-urlencoded ,application/json,text/xml。

16、翻頁限制怎么處理?

多級分類數據很多是有翻頁限制,分類只顯示前多少頁。對於此情況,可以使用這類網站的篩選功能,比如按時間先后,大小,排序來擴大頁面內容。如果有子分類,就不斷的獲取子分類。如果有多個選項,可以使用排列組合的方式,盡可能多的獲取。

17、海量網址如何重復排重?

數據量不大的情況下,可以對網址進行md5一下,然后使用hash進行對比。如果是海量網址排重,可以使用布隆排重算法BloomFilter。

18、如何提高爬取速度

1)使用gzip/deflate壓縮,一般能壓縮到原大小的20%左右。一般服務器不會為你發送壓縮數據,需要傳送Accept-encoding的header。

2)使用鏈接池,C#請求時需要將keep-alive設置為true。

3)設置超時,要對一直無響應的請求果斷關閉。

19、要抓取包含特定關鍵詞的網頁

使用站內搜索,搜索引擎site,全部下載

20、CSS/HTML混淆干擾限制的數據獲取

常見的此類反爬方式有

1)使用圖片替換部分文字

2)使用自定義字體

3)偽元素隱藏

4)元素position位置偏

其中,第一種情況,可以找圖片對應的文字,找到所有后進行替換。第二種可以找到ttf字體文件地址后下載再找其中編碼和文字的對應關系並替換。第三種,找class對應的文本內容進行替換。第四種就涉及到計算了,如果嫌麻煩,也可以截圖識別。

21、數據抓取過程中發現電信劫持,導致數據錯誤怎么辦?

電話投訴電信運營商,可以打電話或是工信部投訴。

有時電腦中病毒也會有這類劫持,這就屬於黑產方面的了。

22、關於eval(function(p,a,c,k,e,d)加密的問題

這是一個很經典的數據加密方式,網上已經有在線加密解密的方法。在本地運行時,需要使用JS引擎執行js獲得結果。

23、PKI證書的驗證的網站如何處理?

PKI證書一般是在登錄時,會去特定地址請求並上傳證書進行驗證,驗證過后生成帶參網址返回網站並生成cookie從而完成身份驗證。也有通過插件實現驗證的,比如吉大正元。

24、HtmlAgilityPack解析網頁需注意什么

在很久以前版本中,HtmlAgilityPack存在溢出漏洞,即節點分析會陷入死循環。新版本解決了這個問題,請用最新版本。在使用HtmlAgilityPack 解析網頁時,需要注意可能頁面錯誤導致無法解析。可以使用替換等方法先對源代碼進行處理,再進行解析。

25、除了fiddler有哪些數據包抓取工具呢?

1)Microsoft Network Monitor

2)Wireshark

3)Anyproxy

26、抓取的數據不全怎么辦?

某些網站,會對外只顯示部分數據,或是只針對部分用戶顯示全部數據,那這種情況下,就是考驗觀察力的能力了。比如很早以前同城網站的聯系方式,只顯示前7位號碼,但是呢,在另外一個地方,顯示了后四位,所以,只要都抓取一下加一塊兒就可以了。還有部分網站,使用json調用api,一看就知道程序員用的是select * ,返回的數據中包含了所有有關和沒關的數據,相當於明顯的漏洞。有時也可以使用不全的數據,再去站內搜索,說不定也會有新的發現。

27、網站使用CDN反爬蟲如何應對?

網站使用cdn技術可以提高訪問速度和安全性,並能提供更高的反爬蟲能力。不過部分網站會暴露真實服務器地址,同時沒判斷cdn來源,導致偽造的cdn服務器可以不斷的抓取數據。同時大多數cdn服務器反爬蟲沒有做聯動,導致cdn服務器越多,反而相當於提供了更多的代理服務器給爬蟲使用。

28、使用Xpath獲取網頁元素時需要注意什么?

經過瀏覽器渲染,然后使用xpath提取Dom元素,再取值,是可以忽略渲染過程,並能實時獲取最新數據的方法。在設置xpath提取規則時,善於使用絕對相對,contains,or,and等符號,可以盡快定位元素。如果最終元素符號是不定的,可以使用父級定位。盡可能使用@id這種唯一性標識。

29、遇到網站投毒怎么辦?

遇到目標網站檢測到了爬蟲,給錯數據的時候,因為無法直接判斷出數據的准確性,所以只能是通過多次,或是多個形式的爬取,對最終的結果進行對比,如果多次都正確,則可以認為是正確的數據。類似的情況都可以使用這樣的思路,比如使用下載軟件下載資源,經常會有下載不了的情況,如果同時對一個資源搜索很多下載鏈接並同時進行下載,就可以很快篩選出可以下載的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM