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) { } }
解决方法
我会使用像1,000这样的好数字.如果您想从单个站点下载那么多页面,那么与需要下载分布在数十个或数百个站点的1,000个页面相比,这将花费更长的时间.原因是,如果您使用大量并发请求访问单个站点,您可能最终会被阻止.
因此,您必须实施一种“礼貌政策”,在单个网站上发出多个请求之间的延迟.延迟的长度取决于许多事情.如果网站的robots.txt文件有一个抓取延迟条目,您应该尊重这一点.如果他们不希望您每分钟访问多个页面,那么这就像您应该抓取的速度一样快.如果没有爬网延迟,您应该根据站点响应的时间延迟.例如,如果您可以在500毫秒内从站点下载页面,则将延迟设置为X.如果需要一整秒,请将延迟设置为2X.您可以将延迟限制为60秒(除非爬行延迟更长),我建议您将最小延迟设置为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(并行处理所需的数量),而不是每个请求分配一个.