C# वेब स्क्रेपिंग: एक व्यावहारिक गाइड
Expert Network Defense Engineer
TL;DR:
HttpClientऔर AngleSharp आधुनिक C# जोड़ी है। AngleSharp CSS चयनकर्ताओं को लेता है, इसलिएdoc.QuerySelectorAll("div.quote")उसी चयनकर्ता के ब्राउज़र कंसोल में व्यवहार करता है जैसा वह व्यवहार करता है।- स्क्रेपलेस यूनिवर्सल स्क्रेपिंग एपीआई JSON लिफाफे के साथ उत्तर देता है। मार्कअप
dataके अंदर आता है, इसलिए आप पहले JSON पढ़ते हैं और फिर HTML पार्स करते हैं। - PuppeteerSharp का सामान्य पहला कदम एक ब्राउज़र डाउनलोड करता है। एक दूरस्थ ब्राउज़र से कनेक्ट करना इसे पूरी तरह से स्किप करता है। हर क्विकस्टार्ट
BrowserFetcher().DownloadAsync()के साथ शुरू होता है;Puppeteer.ConnectAsyncइसे कभी भी कॉल नहीं करता। मापा गया: प्रमाणीकरण प्रोजेक्ट 7.6 MB पर समाप्त हुआ, बिना डिस्क पर Chromium और बिना PuppeteerSharp ब्राउज़र कैश के। - दूरस्थ CDP पाथ बिना समारोह के काम करता है।
ConnectAsyncने एकwss://एंडपॉइंट को स्वीकार किया, जिसमें एक क्वेरी-स्ट्रिंग क्रेडेंशियल था औरtitle=Quotes to Scrape,quotes on page=10लौटाया। - नि:शुल्क शुरू करें: स्क्रेपलेस डैशबोर्ड एक कुंजी जारी करता है जो नीचे दिए गए हर उदाहरण के साथ काम करती है।
आपको क्या चाहिए
नीचे सब कुछ .NET SDK 8.0.129 पर quotes.toscrape.com के खिलाफ चलाया गया, जो स्क्रेपिंग प्रैक्टिस के लिए प्रकाशित एक साइट है।
bash
dotnet new console -o scraper
cd scraper
dotnet add package AngleSharp # 1.7.1
dotnet add package PuppeteerSharp # 25.5.0
HttpClient और System.Text.Json बेस क्लास लाइब्रेरी में हैं, इसलिए केवल निर्भरताएँ पार्सर और, बाद में, ब्राउज़र ड्राइवर हैं।
पार्सर चयन पर: HtmlAgilityPack वह नाम है जिस पर अधिकांश C# स्क्रेपिंग सामग्री निर्भर करती है, और यह काम करता है। AngleSharp नए कोड के लिए पसंद करने लायक है क्योंकि यह W3C DOM को लागू करता है और CSS चयनकर्ताओं को सीधे लेता है, जिसका अर्थ है कि आप जो चयनकर्ता ब्राउज़र डेवलपमेंट टूल्स से कॉपी करते हैं वो बिना बदलाव के काम करते हैं, बजाय इसके कि उन्हें XPath में अनुवाद की आवश्यकता हो।
फ़ेचिंग और पार्सिंग
csharp
using AngleSharp.Html.Parser;
using System.Text;
var http = new HttpClient();
http.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; csharp-guide/1.0)");
var res = await http.GetAsync("https://quotes.toscrape.com/");
var html = await res.Content.ReadAsStringAsync();
Console.WriteLine($"http={(int)res.StatusCode} bytes={Encoding.UTF8.GetByteCount(html)}");
var parser = new HtmlParser();
var doc = await parser.ParseDocumentAsync(html);
var quotes = doc.QuerySelectorAll("div.quote");
Console.WriteLine($"quotes={quotes.Length}");
Console.WriteLine($"first_author={quotes[0].QuerySelector("small.author")!.TextContent}");
Console.WriteLine($"first_text={quotes[0].QuerySelector("span.text")!.TextContent[..40]}");
text
http=200 bytes=11064
quotes=10
first_author=Albert Einstein
first_text=“The world as we have created it is a pr
तीन बातें कहने लायक हैं।
एक उपयोगकर्ता एजेंट सेट करें। HttpClient डिफ़ॉल्ट रूप से कोई नहीं भेजता है, और बिना उपयोगकर्ता एजेंट के एक अनुरोध साइट के लिए भिन्न तरीके से व्यवहार करने के लिए सबसे सस्ता है।
Encoding.UTF8.GetByteCount(html) html.Length नहीं है। .NET स्ट्रिंग्स UTF-16 हैं, इसलिए Length UTF-16 कोड इकाइयों की गिनती करता है, न कि वायर्ड पर बाइट्स। इस पृष्ठ पर दोनों में अंतर है क्योंकि उद्धरण विन्यासित उद्धरण चिह्नों का उपयोग करते हैं। आप जो निर्धारित करते हैं, उसे रिपोर्ट करें, लेकिन API प्रतिक्रिया के खिलाफ सीधे फ़ेच की तुलना करते समय उन्हें मिलाएं नहीं।
बच्चे के प्रश्नों को पंक्ति पर सीमित करें। quotes[0].QuerySelector(...) उस तत्व के अंदर खोज करता है; एक लूप में doc.QuerySelector(...) को कॉल करना हर पुनरावृत्ति पर पहले उद्धरण का मान लौटाता है, जिससे ऐसा डेटा सेट बनता है जो पूर्ण दिखता है और एकसमान रूप से गलत होता है।
जहाँ साधारण C# रुकता है
स्क्रिप्ट काम करती है क्योंकि लक्षित सर्वर-साइड रेंडर करता है और कॉलर्स को स्क्रीन नहीं करता है। दो चीजें इसे समाप्त करती हैं: सामग्री जो केवल JavaScript के निष्पादन के बाद होती है, और साइटें जो एक नग्न HTTP क्लाइंट को सेवा देने से मना करती हैं। इनमें से कोई भी .NET की सीमा नहीं है — किसी भी भाषा में कोई HTTP क्लाइंट इनमें से किसी का भी ख्याल नहीं रखता है।
यहाँ से बढ़ोतरी में बेहतर उपकरण हैं, बजाय बेहतर के, और साधारण रास्ता वहीं सबसे सस्ता रहता है जहाँ यह काम करता है। एक ब्राउज़र जो जोड़ता है, उसके सामान्य आकार के लिए, ब्राउज़र ऑटोमेशन स्पष्टीकरण श्रेणी को कवर करता है।
बढ़ोतरी एक: यूनिवर्सल स्क्रेपिंग एपीआई
यह आपके कोड को एक साधारण HttpClient कॉलर रखता है और कठिनाई को सर्वर-साइड ले जाता है।
csharp
using System.Text;
using System.Text.Json;
using AngleSharp.Html.Parser;
var key = Environment.GetEnvironmentVariable("SCRAPELESS_API_KEY")!;
var payload = JsonSerializer.Serialize(new
{
actor = "unlocker.webunlocker",
input = new { url = "https://quotes.toscrape.com/", js_render = false }
});
using var req = new HttpRequestMessage(
HttpMethod.Post, "https://api.scrapeless.com/api/v2/unlocker/request")
{
Content = new StringContent(payload, Encoding.UTF8, "application/json")
};
req.Headers.Add("x-api-token", key);
var apiRes = await http.SendAsync(req);
using var json = JsonDocument.Parse(await apiRes.Content.ReadAsStringAsync());
var keys = string.Join(",", json.RootElement.EnumerateObject().Select(p => p.Name));
Console.WriteLine($"http={(int)apiRes.StatusCode} envelope_keys={keys}");
var apiHtml = json.RootElement.GetProperty("data").GetString()!;
var apiDoc = await new HtmlParser().ParseDocumentAsync(apiHtml);
Console.WriteLine($"quotes={apiDoc.QuerySelectorAll("div.quote").Length}");
text
http=200 envelope_keys=code,data
quotes=10
लिफाफा वह हिस्सा है जिसे आंतरिक रूप से लेना है। उत्तर का शरीर JSON है और मार्कअप data पर रहता है। कच्चे शरीर को ParseDocumentAsync को देने से एक दस्तावेज़ उत्पन्न होता है जिसमें कोई मेल खाने वाले तत्व नहीं होते और कुछ नहीं फेंकता — चयनकर्ता चुपचाप शून्य परिणाम लौटाते हैं। JSON पढ़ें, data लें, फिर पार्स करें, और उस अनव्रैप को एक विधि में रखें।
req.Headers.Add("x-api-token", key) की नोटिस लें बजाय Authorization हेडर के। यह एक कस्टम हेडर है, इसलिए यह HttpRequestMessage.Headers पर सीधे जाता है; इसे एक टाइप किए गए प्रमाणीकरण हेडर के रूप में व्यक्त करने की कोशिश एक मोड़ है जो यहाँ लागू नहीं होती है।
बढ़ोतरी दो: बिना डाउनलोड किए एक दूरस्थ ब्राउज़र
यहाँ C# स्पष्ट रूप से बेहतर करता है जितना सामान्य निर्देश सुझाते हैं। हर PuppeteerSharp क्विकस्टार्ट इस तरह शुरू होता है:
csharp
// The standard first step — downloads a Chromium build before anything runs.
await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
Puppeteer.ConnectAsync को इसकी आवश्यकता नहीं है। आप पहले से कहीं और मौजूद एक ब्राउज़र से जुड़ रहे हैं, इसलिए BrowserFetcher कभी भी शामिल नहीं होता है और कुछ भी डिस्क पर नहीं आता है:
csharp
using PuppeteerSharp;
var key = Environment.GetEnvironmentVariable("SCRAPELESS_API_KEY")!;
await using var browser = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = $"wss://browser.scrapeless.com/api/v2/browser?token={key}"
});
var page = await browser.NewPageAsync();
await page.GoToAsync("https://quotes.toscrape.com/");
Console.WriteLine($"title={await page.GetTitleAsync()}");
var n = await page.EvaluateExpressionAsync<int>("document.querySelectorAll('.quote').length");
Console.WriteLine($"quotes on page={n}");
text
title=Quotes to Scrape
quotes on page=10
यह दो चीजें आपको सुविधा के अलावा देती हैं।
प्रोजेक्ट छोटा रहता है। इस लेख में प्रत्येक उदाहरण को चलाने के बाद, प्रमाणीकरण प्रोजेक्ट डिस्क पर 7.6 MB पर मापा गया, इसमें कोई Chromium नहीं था, और ना ही कोई PuppeteerSharp ब्राउज़र कैश छोड़ा। एक LaunchAsync कार्यप्रवाह हर मशीन और हर कंटेनर छवि में एक ब्राउज़र बिल्ड जोड़ता है जो कोड चलाता है।
EvaluateExpressionAsync<T> आपके लिए डेसिरीलाइज़ करता है। <int> के लिए पूछने पर एक int लौटता है, जिससे कोई JsonElement खोलने के लिए नहीं है और आपके चर और ब्राउज़र के बीच कोई मैन्युअल पर्स नहीं है।
जानने के लिए: ConnectAsync ने wss:// एंडपॉइंट को स्वीकार किया जो क्वेरी स्ट्रिंग में इसकी क्रेडेंशियल ले जाता है बिना किसी अतिरिक्त कॉन्फ़िगरेशन के। यह भाषाओं के CDP क्लाइंट्स में सार्वभौमिक नहीं है - कुछ क्वेरी स्ट्रिंग को छोड़ देते हैं या TLS सर्वर-नाम वार्ता को दरकिनार कर देते हैं - इसलिए यदि आप किसी अन्य स्टैक से पोर्ट कर रहे हैं, तो यह चरण संभवतः उस एक से आसान होगा जिसे आपने छोड़ा था।
तीन के बीच चयन करना
| दृष्टिकोण | उपयोग करें जब | लागत |
|---|---|---|
HttpClient + AngleSharp |
सर्वर-रेटर्ड HTML, उदार लक्ष्य | न्यूनतम; एक पैकेज |
| यूनिवर्सल स्क्रैपिंग एपीआई | अवरुद्ध या चुनौतीपूर्ण, कोई JS की आवश्यकता नहीं | एक HTTP कॉल, खोलने के लिए लिफाफा |
| PuppeteerSharp + एक दूरस्थ ब्राउज़र | सामग्री को जावास्क्रिप्ट निष्पादन की आवश्यकता है | प्रति नौकरी एक सत्र, लेकिन डिस्क पर कोई ब्राउज़र नहीं |
सूची के तहत काम करें न कि ऊपर। एक पृष्ठ जो ब्राउज़र की आवश्यकता प्रतीत होता है वह बहुत बार अपने डेटा को <script> टैग में सम्मिलित करता है, और एक QuerySelector प्लस JsonDocument.Parse हर दिशा में एक ब्राउज़र सत्र को मात देता है।
निष्कर्ष
C# एक आरामदायक स्क्रैपिंग भाषा है और मानक पुस्तकालय इससे अधिक को कवर करता है जितना कि पारिस्थितिकी तंत्र के ट्यूटोरियल का संकेत होता है। HttpClient और AngleSharp सर्वर-रेटर्ड पृष्ठों को CSS चयनकर्ताओं के साथ संभालते हैं जिन्हें आप देव उपकरणों से पेस्ट कर सकते हैं, और दो जाल जिन्हें याद रखने लायक हैं वे हैं जब आप बाइट्स से टोकन में UTF-16 इकाइयों की गिनती कर रहे हैं, और दस्तावेज़ को तब पूछना जब आप पंक्ति का मतलब रख रहे हों।
ब्राउज़र की कहानी दस्तावेज़ में अपने से बेहतर है। हर त्वरित शुरुआत में डाउनलोड चरण एक स्थानीय ब्राउज़र को लॉन्च करने की संपत्ति है, PuppeteerSharp की नहीं, और ConnectAsync इसे डिस्क फूटप्रिंट के साथ हटा देता है - एक 7.6 MB प्रोजेक्ट जो अभी भी एक वास्तविक Chrome चला रहा है।
C# के साथ स्क्रैप करने के लिए तैयार?
Scrapeless डैशबोर्ड पर एक कुंजी बनाएं और HttpClient उदाहरण को उस पृष्ठ के खिलाफ चलाएं जिसे आप पहले से एकत्र करते हैं। यदि यह आपके अपेक्षित परिणाम लौटाता है, तो आप समाप्त हैं - एक पैकेज, कोई ब्राउज़र नहीं। यूनिवर्सल स्क्रैपिंग एपीआई उन पृष्ठों को कवर करता है जहाँ यह काम नहीं करता, और वर्तमान दरें किमत पृष्ठ पर हैं।
अक्सर पूछे जाने वाले प्रश्न
प्र: AngleSharp या HtmlAgilityPack?
दोनों वास्तविक दुनिया के HTML को अच्छे तरीके से पार्स करते हैं। AngleSharp W3C DOM को लागू करता है और CSS चयनकर्ताओं को लेता है, इसलिए देव उपकरणों के चयनकर्ता बिना बदलने के पोर्ट होते हैं; HtmlAgilityPack XPath-प्रथम है और इसका एक लंबा इतिहास है। नए कोड के लिए, AngleSharp आमतौर पर कम अनुवाद कार्य का अर्थ है।
प्र: क्या मुझे PuppeteerSharp का उपयोग करने के लिए Chromium डाउनलोड करना है?
सिर्फ तभी जब आप एक स्थानीय ब्राउज़र लॉन्च करें। BrowserFetcher().DownloadAsync() LaunchAsync पथ का हिस्सा है; ConnectAsync एक ब्राउज़र से जुड़ता है जो पहले से मौजूद है और कभी इसे छूता नहीं है। यहाँ सत्यापन परियोजना 7.6 MB पर रही है बिना डिस्क पर कोई ब्राउज़र।
प्र: जब एपीआई कॉल सफल हो जाता है तब मेरी पार्सिंग कुछ भी क्यों नहीं लौटाती?
आप लिफाफे को पार्स कर रहे हैं। प्रतिक्रिया एक JSON है जिसमें मार्कअप data के अंदर है, इसलिए AngleSharp एक JSON स्ट्रिंग प्राप्त करता है, कुछ भी नहीं मिलाता है, और कुछ भी नहीं फेंकता है। पहले JSON से data पढ़ें।
प्र: बाइट गिनती स्ट्रिंग लंबाई से क्यों भिन्न होती है?
.NET स्ट्रिंग UTF-16 हैं, इसलिए string.Length कोड इकाइयों की गिनती करता है जबकि Encoding.UTF8.GetByteCount वर्णानुक्रम में ट्रांसफर की गई बाइट्स की गिनती करता है। किसी भी पृष्ठ में जो गैर-ASCII वर्ण शामिल करता है - जिसमें टाइपोग्राफिक उद्धरण चिह्न भी शामिल हैं - एक भिन्नता दिखाई देगी।
प्र: क्या मैं HttpClient को फिर से उपयोग करना चाहिए?
हाँ। एक बनाएं और साझा करें, या एक Hosted एप्लिकेशन में IHttpClientFactory का उपयोग करें। प्रति अनुरोध एक नया HttpClient बनाना लोड के तहत सॉकेट को समाप्त करता है - Microsoft के अपने HttpClient दिशानिर्देश स्पष्ट रूप से बताते हैं कि ऐसा क्यों है - और ये स्क्रेपर पर भी ठीक उसी तरह लागू होते हैं जैसे कि किसी अन्य .NET HTTP कार्यभार पर।
स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।



