玩转C#网页抓取
網(wǎng)頁(yè)抓取是通過自動(dòng)化手段檢索數(shù)據(jù)的過程。它在許多場(chǎng)景中都是不可或缺的,例如競(jìng)爭(zhēng)對(duì)手價(jià)格監(jiān)控、房地產(chǎn)清單列表、潛在客戶和輿情監(jiān)控、新聞文章或金融數(shù)據(jù)聚合等。 如果您想了解更多相關(guān)信息,可以前往Oxylabs中文官網(wǎng)Oxylabs.cn參見我們的文章“網(wǎng)絡(luò)抓取合法嗎?”
在編寫網(wǎng)頁(yè)抓取代碼時(shí),您要做出的第一個(gè)決定是選擇您的編程語(yǔ)言。您可以使用多種語(yǔ)言進(jìn)行編寫,例如Python、JavaScript、Java、Ruby或C#。所有提到的語(yǔ)言都提供強(qiáng)大的網(wǎng)絡(luò)抓取功能。
在本文中,我們將探索C#并向您展示如何創(chuàng)建一個(gè)真實(shí)的C#公共網(wǎng)絡(luò)爬蟲。請(qǐng)記住,即使我們使用C#,您也可以將此信息調(diào)整為.NET平臺(tái)支持的所有語(yǔ)言,包括VB.NET和F#。
01.C#網(wǎng)頁(yè)抓取工具
在編寫任何代碼之前,第一步是選擇合適的C#庫(kù)或包。這些C#庫(kù)或包將具有下載HTML頁(yè)面、解析它們以及從這些頁(yè)面中提取所需數(shù)據(jù)的功能。一些最流行的C#包如下:
●ScrapySharp
●Puppeteer Sharp
●Html Agility Pack
Html Agility Pack是最受歡迎的C#包,僅Nuget就有近5,000萬(wàn)次下載。其流行有多種原因,其中最重要的原因是該HTML解析器能夠直接或使用瀏覽器下載網(wǎng)頁(yè)。這個(gè)包可以容忍格式錯(cuò)誤的HTML并支持XPath。此外,它甚至可以解析本地HTML文件;因此,我們將在本文中進(jìn)一步使用這個(gè)包。
ScrapySharp為C#編程添加了更多功能。這個(gè)包支持CSS選擇器并且可以模擬網(wǎng)絡(luò)瀏覽器。雖然ScrapySharp被認(rèn)為是一個(gè)強(qiáng)大的C#包,但程序員使用它進(jìn)行維護(hù)的概率并不是很高。
Puppeteer Sharp是著名的Node.js Puppeteer項(xiàng)目的.NET端口。它使用相同的Chromium瀏覽器來(lái)加載頁(yè)面。此外,這個(gè)包采用了async-await風(fēng)格的代碼,支持異步及預(yù)操作管理。如果您已經(jīng)熟悉這個(gè)C#包并且需要一個(gè)瀏覽器來(lái)呈現(xiàn)頁(yè)面,那么Puppeteer Sharp可能是一個(gè)不錯(cuò)的選擇。
02.使用C#構(gòu)建網(wǎng)絡(luò)爬蟲
如前所述,現(xiàn)在我們將演示如何編寫將使用Html Agility Pack的C#公共網(wǎng)絡(luò)抓取代碼。我們將使用帶有Visual Studio Code的.NET 5 SDK。此代碼已在 .NET Core 3和.NET 5上測(cè)試過,它應(yīng)該適用于其他版本的.NET。
我們將設(shè)置一個(gè)假設(shè)的場(chǎng)景:爬取一家在線書店并收集書名和價(jià)格。
在編寫C#網(wǎng)絡(luò)爬蟲之前,我們先來(lái)設(shè)置下開發(fā)環(huán)境。
03.設(shè)置開發(fā)環(huán)境
對(duì)于C#開發(fā)環(huán)境,請(qǐng)安裝Visual Studio Code。請(qǐng)注意,如果您使用Visual Studio和Visual Studio Code編寫C#代碼,則需要注意它們是兩個(gè)完全不同的應(yīng)用程序。
安裝Visual Studio Code后,安裝.NET 5.0或更高版本。您還可以使用.NET Core 3.1。安裝完成后,打開終端并運(yùn)行以下命令以驗(yàn)證.NET CLI或命令行界面是否正常工作:
dotnet --version該行命令會(huì)輸出安裝的.NET的版本號(hào)。
04.項(xiàng)目結(jié)構(gòu)和依存關(guān)系
該代碼將成為.NET項(xiàng)目的一部分。為簡(jiǎn)單起見,創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序。然后,創(chuàng)建一個(gè)文件夾,您將在其中編寫C#代碼。打開終端并導(dǎo)航到該文件夾。輸入以下命令:
dotnet new console此命令的輸出應(yīng)該是已成功創(chuàng)建控制臺(tái)應(yīng)用程序的信息。
到時(shí)間安裝所需的軟件包了。使用C#抓取公共網(wǎng)頁(yè),Html Agility Pack將是一個(gè)不錯(cuò)的選擇。您可以使用以下命令為該項(xiàng)目安裝它:
dotnet add package HtmlAgilityPack再安裝一個(gè)包,以便我們可以輕松地將抓取的數(shù)據(jù)導(dǎo)出到CSV文件:
dotnet add package CsvHelper如果您使用的是Visual Studio而不是Visual Studio Code,請(qǐng)單擊文件,選擇新建解決方案,然后按控制臺(tái)應(yīng)用程序按鈕。要安裝依賴項(xiàng),請(qǐng)按照下列步驟操作:
●選擇項(xiàng)目;
●單擊管理項(xiàng)目依賴項(xiàng)。這將打開NuGet包窗口;
●搜索HtmlAgilityPack并選擇它;
●最后,搜索CsvHelper,選擇它,然后單擊添加包。
Visual Studio中的Nuget包管理器
安裝了這些包后,我們可以繼續(xù)編寫用于抓取線上書店的代碼。
05.下載和解析網(wǎng)頁(yè)數(shù)據(jù)
任何網(wǎng)頁(yè)抓取程序的第一步都是下載網(wǎng)頁(yè)的HTML。此HTML將是一個(gè)字符串,您需要將其轉(zhuǎn)換為可以進(jìn)一步處理的對(duì)象,也就是第二步,這部分稱為解析。Html Agility Pack可以從本地文件、HTML字符串、任何URL和瀏覽器讀取和解析文件。
在我們的例子中,我們需要做的就是從URL獲取HTML。Html Agility Pack沒有使用.NET本機(jī)函數(shù),而是提供了一個(gè)方便的類–HtmlWeb.這個(gè)類提供了一個(gè)Load函數(shù),它可以接受一個(gè)URL并返回一個(gè)HtmlDocument類的實(shí)例,它也是我們使用的包的一部分。有了這些信息,我們可以編寫一個(gè)函數(shù),接受一個(gè)URL并返回HtmlDocument這個(gè)實(shí)例。
打開Program.cs文件并在類中輸入此函數(shù)Program:
// Parses the URL and returns HtmlDocument object static HtmlDocument GetDocument (string url) {HtmlWeb web = new HtmlWeb();HtmlDocument doc = web.Load(url);return doc; }這樣,代碼的第一步就完成了。下一步是解析文檔。
06.解析HTML:獲取書籍鏈接
在這部分代碼中,我們將從網(wǎng)頁(yè)中提取所需的信息。在這個(gè)階段,文檔現(xiàn)在是一個(gè)類型的對(duì)象HtmlDocument。這個(gè)類公開了兩個(gè)函數(shù)來(lái)選擇元素。這兩個(gè)函數(shù)都接受XPath輸入并返回HtmlNode or HtmlNodeCollection。
下面是這兩個(gè)函數(shù)的簽名:
public HtmlNodeCollection SelectNodes(string xpath); public HtmlNode SelectSingleNode(string xpath);我們就SelectNodes先討論一下。
對(duì)于這個(gè)例子——C#網(wǎng)絡(luò)爬蟲——我們將從這個(gè)頁(yè)面中抓取所有書籍的詳細(xì)信息。
首先,需要對(duì)其進(jìn)行解析,以便可以提取到所有書籍的鏈接。在瀏覽器中打開上述的書店頁(yè)面,右鍵單擊任何書籍鏈接,然后單擊按鈕“檢查”。將打開開發(fā)人員工具。
在了解標(biāo)記后,您要選擇的XPath應(yīng)該是這樣的:
//h3/a現(xiàn)在可以將此XPath傳遞給SelectNodes函數(shù)。
HtmlDocument doc = GetDocument(url); HtmlNodeCollection linkNodes = doc.DocumentNode.SelectNodes("//h3/a");請(qǐng)注意,該SelectNodes函數(shù)是由
HtmlDocument的DocumentNode屬性調(diào)用的。
變量linkNodes是一個(gè)集合。我們可以寫一個(gè)foreach循環(huán),并從每個(gè)鏈接一個(gè)一個(gè)地獲取href值。我們只需要解決一個(gè)小問題——那就是頁(yè)面上的鏈接是相對(duì)鏈接。因此,在我們抓取這些提取的鏈接之前,需要將它們轉(zhuǎn)換為絕對(duì)URL。
為了轉(zhuǎn)換相對(duì)鏈接,我們可以使用Uri該類。我們使用此構(gòu)造函數(shù)來(lái)獲取Uri具有絕對(duì)URL的對(duì)象。
dotnet --version一旦我們有了Uri對(duì)象,我們就可以簡(jiǎn)單地檢查該AbsoluteUri屬性以獲取完整的URL。
我們將所有這些寫在一個(gè)函數(shù)中,以保持代碼的組織性。
static List<string> GetBookLinks(string url) {var bookLinks = new List<string>();HtmlDocument doc = GetDocument(url);HtmlNodeCollection linkNodes = doc.DocumentNode.SelectNodes("//h3/a");var baseUri = new Uri(url);foreach (var link in linkNodes){string href = link.Attributes["href"].Value;bookLinks.Add(new Uri(baseUri, href).AbsoluteUri);}return bookLinks; }在這個(gè)函數(shù)中,我們從一個(gè)空List<string>對(duì)象開始。在foreach循環(huán)中,我們將所有鏈接添加到此對(duì)象并返回它。
現(xiàn)在,就可以修改Main()函數(shù)了,以便我們可以測(cè)試到目前為止編寫的C#代碼。修改函數(shù)如下:
static void Main(string[] args){var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");Console.WriteLine("Found {0} links", bookLinks.Count); }要運(yùn)行此代碼,請(qǐng)打開終端并導(dǎo)航到包含此文件的目錄,然后鍵入以下內(nèi)容:
dotnet run輸出應(yīng)如下所示:
Found 20 links然后我們轉(zhuǎn)到下一部分,我們將處理所有鏈接以獲取圖書數(shù)據(jù)。
07.解析HTML:獲取書籍詳細(xì)信息
此時(shí),我們有一個(gè)包含書籍URL的字符串列表。我們可以簡(jiǎn)單地編寫一個(gè)循環(huán),首先使用我們已經(jīng)編寫的函數(shù)GetDocument來(lái)獲取文檔。之后,我們將使用該SelectSingleNode函數(shù)來(lái)提取書名和價(jià)格。
為了讓數(shù)據(jù)清晰有條理,我們從一個(gè)類開始。這個(gè)類將代表一本書,有兩個(gè)屬性-Title和Price.示例如下:
public class Book {public string Title { get; set; }public string Price { get; set; } }然后,為Title – //h1在瀏覽器中打開一個(gè)書頁(yè)。為價(jià)格創(chuàng)建 XPath 有點(diǎn)棘手,因?yàn)榈撞康母郊訒畱?yīng)用了相同的類。
價(jià)格的XPath
價(jià)格的XPath將是這樣的:
//div[contains(@class,"product_main")]/p[@class="price_color"]請(qǐng)注意,XPath包含雙引號(hào)。我們將不得不通過在它們前面加上反斜杠來(lái)轉(zhuǎn)義這些字符。
現(xiàn)在我們可以使用SelectSingleNode函數(shù)來(lái)獲取節(jié)點(diǎn),然后使用InnerText屬性獲取元素中包含的文本。我們可以將所有內(nèi)容放在一個(gè)函數(shù)中,如下所示:
static List<Book> GetBookDetails(List<string> urls) { var books = new List<Book>(); foreach (var url in urls){HtmlDocument document = GetDocument(url);var titleXPath = "//h1";var priceXPath = "//div[contains(@class,\"product_main\")]/p[@class=\"price_color\"]";var book = new Book();book.Title = document.DocumentNode.SelectSingleNode (priceXPath).InnerText;book.Price = document.DocumentNode.SelectSingleNode(priceXPath).InnerText;books.Add(book);} return books; }此函數(shù)將返回一個(gè)Book對(duì)象列表。是時(shí)候更新Main()函數(shù)了:
static void Main(string[] args) {var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");Console.WriteLine("Found {0} links", bookLinks.Count);var books = GetBookDetails(bookLinks); }這個(gè)網(wǎng)絡(luò)抓取項(xiàng)目的最后一部分是將數(shù)據(jù)導(dǎo)出為CSV。
08.導(dǎo)出數(shù)據(jù)
如果您尚未安裝CsvHelper,則可以通過
dotnet add package CsvHelper在終端內(nèi)運(yùn)行命令來(lái)完成此操作。
導(dǎo)出功能非常簡(jiǎn)單。首先,我們需要?jiǎng)?chuàng)建一個(gè)StreamWriter并發(fā)送CSV文件名作為參數(shù)。接下來(lái),我們將使用此對(duì)象創(chuàng)建一個(gè)CsvWriter.最后,我們可以使用該WriteRecords函數(shù)在一行代碼中編寫所有書籍。
為了確保所有資源都正確關(guān)閉,我們可以使用using塊。我們還可以將所有內(nèi)容包裝在一個(gè)函數(shù)中,如下所示:
static void exportToCSV(List<Book> books) { using (var writer = new StreamWriter("./books.csv")) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)){csv.WriteRecords(books);} }最后,我們可以從Main()函數(shù)中調(diào)用這個(gè)函數(shù):
static void Main(string[] args) {var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");var books = GetBookDetails(bookLinks);exportToCSV(books); }要運(yùn)行此代碼,請(qǐng)打開終端并運(yùn)行以下命令:
dotnet run在幾秒鐘內(nèi),您將創(chuàng)建一個(gè)books.csv文件。
09.結(jié)論
如果您想用C#編寫一個(gè)網(wǎng)絡(luò)爬蟲,您可以使用多個(gè)包。在本文中,我們展示了如何使用Html Agility Pack,這是一個(gè)功能強(qiáng)大且易于使用的包。也是一個(gè)可以進(jìn)一步增強(qiáng)的簡(jiǎn)單示例;例如,您可以嘗試將上述邏輯添加到此代碼中以處理多個(gè)頁(yè)面。
如果您想了解更多有關(guān)使用其他編程語(yǔ)言進(jìn)行網(wǎng)絡(luò)抓取的工作原理,可以查看使用Python進(jìn)行網(wǎng)絡(luò)抓取的指南。我們還有一個(gè)關(guān)于如何使用JavaScript編寫網(wǎng)絡(luò)爬蟲的分步教程
常見問題
Q:C#適合網(wǎng)頁(yè)抓取嗎?
A:與Python類似,C#被廣泛用于網(wǎng)頁(yè)抓取。在決定選擇哪種編程語(yǔ)言時(shí),選擇您最熟悉的一種至關(guān)重要。不過您將能夠在Python和C#中找到示例的網(wǎng)頁(yè)抓取工具。
Q:網(wǎng)絡(luò)抓取合法嗎?
A:如果在不違反任何法律的情況下使用代理,則它們可能是合法的。然而,在與代理進(jìn)行任何活動(dòng)之前,您應(yīng)該就您的特定案件獲得專業(yè)的法律建議。
總結(jié)
- 上一篇: 淘宝 NPM 镜像解决软件下载速度慢的问
- 下一篇: C# 和欧姆龙 Omron PLC 以太