HTTPWebResponse + StreamReader非常慢

21

我正在尝试在C#中实现一个有限的网络爬虫(仅针对几百个网站),使用HttpWebResponse.GetResponse()和Streamreader.ReadToEnd(),也尝试使用StreamReader.Read()和循环来构建HTML字符串。

我只下载大小约为5-10K的页面。

一切都非常缓慢!例如,GetResponse()的平均时间约为半秒,而StreamREader.ReadToEnd()的平均时间约为5秒!

所有的站点都应该非常快,因为它们非常接近我的位置并且具有快速的服务器。(在浏览器中下载几乎不需要时间)而且我没有使用任何代理。

我的爬虫大约有20个线程同时从同一个站点读取。这可能会导致问题吗?

如何大幅度降低StreamReader.ReadToEnd时间?

9个回答

16

HttpWebRequest 可能需要一些时间来检测您的代理设置。尝试将以下内容添加到您的应用程序配置中:

<system.net>
<defaultProxy>
<proxy autoDetect="True" />
</defaultProxy>
</system.net>

<system.net>
  <defaultProxy enabled="false">
    <proxy/>
    <bypasslist/>
    <module/>
  </defaultProxy>
</system.net>

如果缓冲读取以减少对底层操作系统套接字的调用,您可能还会看到轻微的性能提升:

using (BufferedStream buffer = new BufferedStream(stream))
{
  using (StreamReader reader = new StreamReader(buffer))
  {
    pageContent = reader.ReadToEnd();
  }
}

谢谢!这完全将我的代码从几秒钟加速到了毫秒级别! - Cobra_Fast
C++中的等效代码是什么?在C++中,using(...)无法工作。 - Edge

8
WebClient的DownloadString是HttpWebRequest的简单封装,您可以暂时尝试使用它并查看速度是否有所提高。如果速度明显加快,您可以分享您的代码,以便我们查看可能存在的问题。
编辑:似乎HttpWebRequest遵循IE的“最大并发连接”设置,这些URL是否在同一域上?您可以尝试增加连接限制以查看是否有帮助。我找到了关于这个问题的这篇文章

默认情况下,您不能执行超过2-3个异步HttpWebRequest(取决于操作系统)。为了覆盖它(最简单的方法,我认为),不要忘记在应用程序的配置文件中添加以下内容:

<system.net>
  <connectionManagement>
     <add address="*" maxconnection="65000" />
  </connectionManagement>
</system.net>

尝试使用WebClient,结果相同(平均时间没有改变)。我还应该提到,我有一个1.5MBPS的连接,平均下载速度为180KBPS。我在想,也许同时调用20个线程的StreamReader.Read可能与此有关?或者这与此无关? - Roey
根据我的经验,在这样的连接上,使用3-4个线程就可以占用带宽。除非你要ping的网站真的很慢,而且线程在等待I/O时睡眠时间很长,否则没有必要再运行更多线程。 - kgriffs
1
哇!我使用异步HttpWebRequest对测试服务器进行负载测试,每个客户端大约有300个线程,每个线程都是“串行”下载。更改maxconnection设置使每个线程的数据下载速度提高了10倍。 - vivek.m

4

我曾经遇到过同样的问题,但当我将HttpWebRequest的代理参数设置为null时,问题得以解决。

UriBuilder ub = new UriBuilder(url);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create( ub.Uri );
request.Proxy = null;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

1

我发现应用程序配置方法不起作用,但问题仍然是由于代理设置引起的。我的简单请求以前需要花费长达30秒的时间,现在只需要1秒。

public string GetWebData()
{
            string DestAddr = "http://mydestination.com";
            System.Net.WebClient myWebClient = new System.Net.WebClient();
            WebProxy myProxy = new WebProxy();
            myProxy.IsBypassed(new Uri(DestAddr));
            myWebClient.Proxy = myProxy;
            return myWebClient.DownloadString(DestAddr);
}

1
你尝试过使用ServicePointManager.maxConnections吗?我通常将其设置为200,用于类似这样的事情。

1
我曾经遇到过同样的问题,但更糟糕。 在我的代码中,response = (HttpWebResponse)webRequest.GetResponse(); 延迟了约10秒钟才运行更多的代码,之后下载使我的连接饱和。
kurt的答案defaultProxy enabled="false"解决了这个问题。现在响应几乎是即时的,我可以以我的连接最大速度下载任何http文件 :) 对于我的糟糕英语表示抱歉。

0

感谢大家的回答,它们帮助我朝着正确的方向深入了解。虽然我也遇到了同样的性能问题,但是建议更改应用程序配置文件的解决方案(我理解这个解决方案是针对Web应用程序的)并不符合我的需求,我的解决方案如下:

HttpWebRequest webRequest;

webRequest = (HttpWebRequest)System.Net.WebRequest.Create(fullUrl);
webRequest.Method = WebRequestMethods.Http.Post;

if (useDefaultProxy)
{
    webRequest.Proxy = System.Net.WebRequest.DefaultWebProxy;
    webRequest.Credentials = CredentialCache.DefaultCredentials;
}
else
{
    System.Net.WebRequest.DefaultWebProxy = null;
    webRequest.Proxy = System.Net.WebRequest.DefaultWebProxy;
}

0

为什么多线程不能解决这个问题?多线程可以最小化网络等待时间,而且由于您将缓冲区的内容存储在系统内存(RAM)中,所以不会有来自处理文件系统的IO瓶颈。因此,您下载和解析需要82秒的82页应该只需要大约15秒(假设是4倍处理器)。如果我漏掉了什么,请纠正我。

____ 下载线程 _____*

下载内容

生成流

读取内容

_________________________*


0
尝试像这样在您的请求中添加cookie(AspxAutoDetectCookieSupport=1
request.CookieContainer = new CookieContainer();         
request.CookieContainer.Add(new Cookie("AspxAutoDetectCookieSupport", "1") { Domain = target.Host });

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