在.NET 4.0中并行化网络爬虫的最佳实践

3

我需要通过代理下载大量页面。构建多线程网络爬虫的最佳实践是什么?

Parallel.For\Foreach是否足够好,还是对于重度CPU任务来说有更好的选择?

你对以下代码有何看法?

var multyProxy = new MultyProxy();

   multyProxy.LoadProxyList();


   Task[] taskArray = new Task[1000];

        for(int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = new Task( (obj) =>
                {                                                             
                       multyProxy.GetPage((string)obj);
                },

            (object)"http://google.com"
            );
            taskArray[i].Start();
        }


   Task.WaitAll(taskArray);

它的表现非常糟糕。它非常慢,我不知道为什么。

这段代码也表现很差。

 System.Threading.Tasks.Parallel.For(0,1000, new System.Threading.Tasks.ParallelOptions(){MaxDegreeOfParallelism=30},loop =>
            {
                 multyProxy.GetPage("http://google.com");
            }
            );

我认为我做错了什么。

当我启动我的脚本时,它只使用2%-4%的网络。

3个回答

8

您基本上是在使用CPU绑定线程来处理IO绑定任务——即使您并行执行操作,它们仍然使用了ThreadPool线程,这主要用于CPU绑定操作。

基本上,您需要使用异步模式下载数据并将其更改为使用IO完成端口——如果您使用WebRequest,则使用BeginGetResponse()和EndGetResponse()方法。

我建议使用响应式扩展来实现此目的,例如:

IEnumerable<string> urls = ... get your urls here...;
var results = from url in urls.ToObservable()
             let req = WebRequest.Create(url)
             from rsp in Observable.FromAsyncPattern<WebResponse>(
                  req.BeginGetResponse, req.EndGetResponse)()
             select ExtractResponse(rsp);

如果您想要的是字符串结果,ExtractResponse 可能只是使用 StreamReader.ReadToEnd 来获取这些结果。

此外,您还可以考虑使用 .Retry 操作符,如果出现连接问题等情况,它将轻松允许您重试几次...


谢谢。我在 Rx 中很新,那么如何下载例如100页?我需要创建100个Observable并订阅每个吗? - Neir0
如果您已经拥有完整的URL列表,可以使用.ToObservable从列表中创建IObservable - 请查看http://rxwiki.wikidot.com/101samples#toc11。 - Martin Ernst
使用相同的代码,但在“SelectMany”调用中出现了“类型推断失败”的错误。来自Observable.FromAsyncPattern<WebResponse>的rsp - Vipul

1
在你的主方法开头添加以下内容:
System.Net.ServicePointManager.DefaultConnectionLimit = 100;

因此,您不会受到并发连接数量的限制。


0

当您使用大量连接时,这可能会对您有所帮助(添加到app.config或web.config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="50"/>
    </connectionManagement>
  </system.net>
</configuration>

将您的并发连接数设置为50以外的数字

http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx上了解更多相关信息


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接