您解决这个问题的方式将非常依赖于您要下载的页数,以及您引用的网站数。
我会使用一个好的数字,如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