我正在创建一个Web Crawler,我发现我的一个方法GetHTML非常慢,因为它使用StreamReader从HttpWebResponse对象中获取HTML字符串。
以下是方法:
static string GetHTML(string URL)
{
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
Request.Proxy = null;
HttpWebResponse Response = ((HttpWebResponse)Request.GetResponse());
Stream RespStream = Response.GetResponseStream();
return new StreamReader(RespStream).ReadToEnd(); // Very slow
}
我使用秒表进行了测试,并在YouTube上使用了此方法。
Time it takes to get an HTTP response: 500 MS
Time it takes to convert the HttpWebResponse object to a string: 550 MS
所以HTTP请求没问题,只是ReadToEnd()这么慢。
ReadToEnd()方法是否有任何替代方法可以从响应对象中获取HTML字符串?我尝试使用WebClient.DownloadString()方法,但它只是一个使用流的HttpWebRequest包装器。
编辑:使用套接字尝试它并且速度更快:
static string SocketHTML(string URL)
{
string IP = Dns.GetHostAddresses(URL)[0].ToString();
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Connect(new IPEndPoint(IPAddress.Parse(IP), 80));
s.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"));
List<byte> HTML = new List<byte>();
int Bytes = 1;
while (Bytes > 0)
{
byte[] Data = new byte[1024];
Bytes = s.Receive(Data);
foreach (byte b in Data) HTML.Add(b);
}
s.Close();
return Encoding.ASCII.GetString(HTML.ToArray());
}
与套接字一起使用它的问题是,它大部分时间都会返回错误,例如“永久移动”或“您的浏览器发送了服务器无法理解的请求”。
答案 0 :(得分:5)
当我调用此方法但返回String.Empty而不是ReadToEnd时,该方法大约需要500 MS。
所有这一切都是开始以获得响应需要500毫秒。调用GetResponseStream
不会消耗所有数据。
ReadToEnd
也将从二进制数据转换为文本,但我怀疑这是重要的 - 我强烈怀疑它只是等待数据通过网络到达。要验证这一点,您应该将日志记录添加到代码和运行Wireshark的每个方面 - 然后您应该能够在数据到达时逐个数据包地查看,并将其与伐木。
作为一个副作用,你应该肯定有一个using
的回复声明:
using (var response = ((HttpWebResponse)Request.GetResponse())
{
// The stream will be disposed when the response is.
return new StreamReader(response.GetResponseStream())
.ReadToEnd();
}
如果您没有处理响应,您将绑定连接,直到垃圾收集器完成它们。这可能导致超时。
答案 1 :(得分:2)
我做了这个比较,看看
StreamReader.ReadToEnd()
是否是瓶颈,我已经看到了。
你在这里得出了错误的结论:瓶颈是整个方法,而不仅仅是StreamReader.ReadToEnd()
部分。
当我收到回复并且我不使用
ReadToEnd()
方法时,大约需要500 MS,但如果我使用ReadToEnd()
方法则需要1000 MS。
这就是事情 - 调用Response.GetResponseStream()
的能力并不意味着你得到了回应&#34;。你得到的只是确认响应在那里。
在现实世界中,这类似于收到您必须在邮局签名的包裹。邮局会在你的邮箱里放一张明信片,说邮局里有送货等候你。这是你的Response.GetResponseStream()
电话。但此时你没有包裹,只有明信片上写着包裹在那里。现在你需要去邮局,向他们展示卡片,并检索包裹。这是StreamReader.ReadToEnd()
来电。
时间几乎翻倍,因为大部分1000毫秒用于与远程服务器通信。如果你需要整个响应,你几乎无法做到加快速度。好消息是,由于时间花费在I / O上,因此很有可能您可以并行化此代码以从多个网站检索数据(假设您没有将网络加载到容量中)。 / p>
答案 2 :(得分:1)
ReadToEnd
方法不是很慢,而是在等待需要时间的数据。
ReadToEnd
方法足够快。我刚测试使用流读取器从内存流中读取一兆字节的数据,只需3毫秒。
当您从请求中获得响应流时,它只是开始获取请求的数据。一旦您已经读取了已经收到的数据,它就必须等待其余的数据到达。这是ReadToEnd
电话中的时间。使用任何其他方式阅读流不会使它更快。