我的应用程序要求我将大量网页下载到内存中以进行进一步的解析和处理.最快的方法是什么?我目前的方法(如下所示)似乎太慢,偶尔会导致超时.
for (int i = 1; i<=pages; i++)
{
string page_specific_link = baseurl + "&page=" + i.ToString();
try
{
WebClient client = new WebClient();
var pagesource = client.DownloadString(page_specific_link);
client.Dispose();
sourcelist.Add(pagesource);
}
catch (Exception)
{
}
}
Run Code Online (Sandbox Code Playgroud)
解决此问题的方式将在很大程度上取决于您要下载的页面数以及所引用的站点数。
我将使用一个很好的整数,例如1000。如果要从一个站点下载那么多页面,则比要下载分布在数十个或数百个站点中的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, ...);
}
Run Code Online (Sandbox Code Playgroud)
初始化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);
}
Run Code Online (Sandbox Code Playgroud)
第一个分配一个WebClient
实例用于所有请求。第二个WebClient
为每个请求分配一个。区别是巨大的。WebClient
使用大量的系统资源,并且在相对较短的时间内分配成千上万的资源将影响性能。相信我...我遇到了这个。您最好只分配10或20 WebClient
秒(与并发处理所需的时间一样多),而不是每个请求分配一个。