2015-02-03 4 views
-1

웹 크롤러를 만들고 있는데, StreamReader를 사용하여 HTML 문자열을 가져 오기 때문에 GetHTML 중 하나가 매우 느린 것으로 나타났습니다. HttpWebResponse 객체StreamReader.ReadToEnd() 매우 느립니다.

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()입니다.

응답 개체에서 HTML 문자열을 가져 오기 위해 ReadToEnd() 메서드를 사용할 수 있습니까? 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()); 
     } 

소켓 함께 사용의 문제,하지만 시간이 대부분은 "영구적으로 이동"또는 오류를 반환한다는 것입니다 "브라우저가 서버가 이해할 수 없다는 요청을 보냈습니다".

+3

여기에서 비교해보십시오. 원격 사이트에 대한 실제 호출에 대해 빈 문자열을 반환 하시겠습니까? – Steve

+0

StreamReader.ReadToEnd()가 병목 현상인지 확인하기 위해이 비교를 수행했습니다. 응답을 받았는데 ReadToEnd() 메서드를 사용하지 않으면 반환 할 GetHTML (문자열 URL)이 약 500 MS가 걸리지 만 ReadToEnd() 메서드를 사용하면 1000 MS가 걸립니다. 이 경우 (youtube.com에서 테스트했을 때) ReadToEnd() 메소드는 500 MS 완료해야합니다. 매우 느립니다. 요청 자체는 문제가 없으며 OK로 전송되지만 문자열 변환은 매우 느립니다. – BlueRay101

답변

4

이 메서드를 호출하지만 ReadToEnd 대신 String.Empty를 반환하면이 메서드는 약 500 MS 걸립니다.

은 응답을 얻으려면으로 시작하는 것이 모두 500ms가 걸린다 고합니다. GetResponseStream을 호출한다고해서 모든 데이터가 소비되는 것은 아닙니다.

ReadToEnd도 바이너리 데이터를 텍스트로 변환 할 예정이지만 중요한 점은 의심 스럽습니다. 데이터가 네트워크를 통해 도착할 때까지 기다리는 것이 좋습니다. 이를 확인하려면 코드 실행 Wireshark의 모든 측면에 로깅을 추가해야합니다. 그러면 데이터가 도착하면 패킷별로 패킷을보고 로깅과 상호 연관시켜야합니다. 당신이 응답을 처리하지 않는 경우 가비지 컬렉터가 확정 될 때까지

using (var response = ((HttpWebResponse)Request.GetResponse()) 
{ 
    // The stream will be disposed when the response is. 
    return new StreamReader(response.GetResponseStream()) 
     .ReadToEnd(); 
} 

, 당신은 연결을 묶어 것 : 측면의 문제로

, 당신은 확실히using 응답에 대한 문을 가져야한다 그들. 그게 시간 초과로 이어질 수 있습니다.

+0

답변 해 주셔서 감사합니다. TCP 소켓을 사용하고 "GET/HTTP/1.1 \ r \ n \ r \ n"데이터를 직접 보내는 것이 더 빠릅니까? – BlueRay101

+0

@ BlueRay010 : 서버에서 클라이언트로 데이터를 가져 오는 것만으로는 충분하지 않습니다. 어떻게 TCP를 사용하면 도움이 될까요? (그냥 HttpRequest가하는 것과 같을 것입니다.) –

+0

@ BlueRay010 : 사실, 연결 풀링을하지 않기 때문에 소켓을 직접 사용하는 것이 더 나쁩니다. 그러나 현재 응답을 처리하지 않을 때 버그가 있습니다. 제 편집을 참조하십시오. –

1

느린 것은 아니며 시간이 걸리는 데이터를 기다리는 중입니다.

ReadToEnd 방법은 충분히 빠릅니다. 방금 스트림 판독기를 사용하여 메모리 스트림에서 메가 바이트 단위의 데이터를 읽으려고 테스트 한 결과 3 밀리 초 밖에 걸리지 않았습니다.

요청에서 응답 스트림을 가져 오면 요청 된 데이터 만 시작됩니다. 이미 수신 한 데이터를 읽은 후에는 나머지 데이터가 도착할 때까지 기다려야합니다. 그게 바로 ReadToEnd 호출에서 시간이 걸리는 것입니다. 스트림을 읽는 다른 방법을 사용한다고해서 더 빨라지지는 않습니다.

+0

그래, 고마워. 웹 사이트에서 HTML 코드를 더 빨리 얻을 수있는 방법이 없습니까? 이것은 일반적으로 매우 느리며 작은 웹 사이트에서도 단일 요청으로 수백 밀리 초가 소요됩니다. – BlueRay101

+0

@ BlueRay010 백 밀리 초는 아마도 당신과 서버 사이의 대기 시간 일 것입니다. –

+0

사실, TCP 소켓을 사용하여 표준 HTTP GET 요청을 보내려고 시도한 결과, HttpWebRequest 클래스의 시간이 약 25 % 단축되었습니다. 대기 시간에 대해 알고 있지만 왜 소켓이 더 빠릅니까? 나는 때때로 "Moved permanently"와 같은 응답을 받고 그것을 다루고 싶지 않기 때문에 소켓을 사용하고 싶지 않습니다. – BlueRay101

2

나는이 비교를 통해 StreamReader.ReadToEnd()이 병목인지 알아 보았습니다.

여기에 잘못된 결론이 있습니다. 병목 현상은 전체적인 방법이며 StreamReader.ReadToEnd() 부분이 아닙니다. 나는 응답을 받고 난 ReadToEnd() 방법을 사용하지 않는 경우

, 그것은 약 500 MS 걸리지 만 나는 ReadToEnd() 방법을 사용하는 경우 1000 MS 걸립니다.

그건 사실입니다. Response.GetResponseStream()을 호출 할 수 있다고해서 응답이 있다는 의미는 아닙니다. 당신이 얻는 것은 응답이 있다는 것을 확인하는 것입니다.

현실 세계에서는 우체국에 서명해야하는 소포를받는 것과 유사합니다. 우체국은 우편함에 우체국에서 배달을 기다리는 엽서를 넣습니다. 전화 번호는 Response.GetResponseStream()입니다. 그러나이 시점에서 당신은 당신의 소포가 없으며 소포가 있다고 말하는 엽서 만 있습니다. 이제 우체국에 가서 카드를 보여주고 소포를 꺼내야합니다. 전화 번호는 StreamReader.ReadToEnd()입니다.

대부분의 경우 1000ms가 원격 서버와 통신하기 때문에 시간이 거의 두 배가됩니다. 전체 응답이 필요하면 속도를 높이는 것에 대해 할 수있는 일이 거의 없습니다. 좋은 소식은 I/O에서 시간을 보냈기 때문에 여러 웹 사이트에서 데이터를 검색하기 위해이 코드를 병렬화 할 수 있다는 것입니다 (네트워크를 용량에로드하지 않는다고 가정 할 때).

+0

오, 알겠지만, 나는 입출력과 스트림을 잘 모른다. 그래서 이것은 변환을 위해서만 사용되었다고 생각했다. 멀티 스레드 코드가 이미 있습니다. 조금 더 빨리 만들 수 있다면 멋지다고 생각했습니다. 고맙습니다! – BlueRay101