您解決這個問題的方式將非常依賴於您要下載的頁數,以及您引用的網站數。
我會使用一個好的數字,如1,000。如果您希望從單個網站下載多個網頁,則需要花費比您想要下載的跨越數十個或數百個網站的1,000個網頁更長的時間。原因是,如果你用一大堆併發請求單擊一個站點,你最終可能會被阻止。
因此,您必須實施一種「禮貌策略」,即在單個網站上的多個請求之間發出延遲。該延遲的長度取決於許多事情。如果網站的robots.txt文件有crawl-delay
條目,則應該尊重該條目。如果他們不希望您每分鐘訪問多個頁面,那麼這與您應該抓取的速度一樣快。如果沒有crawl-delay
,則應根據您的延遲時間來確定網站響應所需的時間。例如,如果您可以在500毫秒內從網站下載頁面,則將延遲設置爲X.如果需要一整秒,則將延遲設置爲2X。你可以將你的延遲限制在60秒(除非crawl-delay
更長),並且我建議你設置5到10秒的最小延遲。
我不會推薦使用Parallel.ForEach
這個。我的測試表明,它做得不好。有時它會對連接過度徵稅,並且通常不允許足夠的併發連接。我反而創造WebClient
實例的隊列,然後寫類似:
// Create queue of WebClient instances
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>();
// Initialize queue with some number of WebClient instances
// now process urls
foreach (var url in urls_to_download)
{
var worker = ClientQueue.Take();
worker.DownloadStringAsync(url, ...);
}
當初始化WebClient
實例是進入隊列,設置其OnDownloadStringCompleted
事件處理程序指向一個完整的事件處理程序。該處理程序應該將該字符串保存到文件中(或者您應該只使用DownloadFileAsync
),然後客戶端將自己添加回ClientQueue
。
在我的測試中,我已經能夠使用此方法支持10到15個併發連接。除此之外,我遇到了DNS解析的問題(`DownloadStringAsync'不會異步執行DNS解析)。你可以獲得更多的聯繫,但這樣做很多工作。
這就是我過去採用的方法,它可以很快地下載數千頁的頁面。儘管如此,這絕對不是我用我的高性能Web爬蟲所採取的方法。
我也應該注意,在這些代碼兩個塊之間的資源使用一個巨大區別:
WebClient MyWebClient = new WebClient();
foreach (var url in urls_to_download)
{
MyWebClient.DownloadString(url);
}
---------------
foreach (var url in urls_to_download)
{
WebClient MyWebClient = new WebClient();
MyWebClient.DownloadString(url);
}
首先分配一個用於所有請求單WebClient
實例。第二個爲每個請求分配一個WebClient
。差別很大。 WebClient
使用大量的系統資源,並且在相對較短的時間內分配數千個資源將會影響性能。相信我......我碰到過這個。您最好只分配10或20 WebClient
(儘可能多地進行併發處理),而不是爲每個請求分配一個。
你需要一個T1連接 –
由於許多答案都暗示並行抓取,我想提醒你對發送過多的併發請求;如果網站不友好,您可能會被禁止。此外,每增加一個線程會有多大的幫助,並且會超出一定程度會導致性能下降。 –
@Hemal Pandya:這是一個值得關注的問題,那不是*關注的問題; WebClient類最終將使用使用'ServicePointManager'類的'HttpWebRequest' /'HttpWebResponse'類。默認情況下,「ServicePointManager」會將特定域的大多數下載次數限制爲兩次(按照HTTP 1.1規範中的建議)。 – casperOne