[C#] HttpConnection을 이용해서 웹 페이지 가져오기


Development note/C#  2019. 6. 20. 09:00

안녕하세요. 명월입니다.


이 글은 HttpWebRequest 를 이용해서 웹 페이지를 읽어 드리는 방법에 대한 글입니다.


우리가 프로그래밍을 하다보면 웹의 객체를 읽어 드릴 때가 있습니다. 예를 들면 블로그의 rss 혹은 sitemap의 xml을 읽어 드린다거나 또는 웹페이지의 정보를 얻을 때 필요합니다.

그러나 HttpWebRequest는 말 그대로 웹 페이지만 읽어 오는 것이라서 스크래핑과는 약간 틀립니다. 스크래핑은 웹의 렌더링, 즉 javascript로 동적으로 주입된 데이터를 제어하는 기능입니다만, HttpWebRequest는 웹의 렌더링이 되지 않는 순수 페이지만 읽어 오는 것입니다.


C#에서 스크래핑과 관련된 라이브러리는 Gecko 라이브러리를 이용하는 방법이 있습니다.

참조 - [C#] Gecko 라이브러리 (웹 스크래핑)


Http 프로토콜은 단순한 소켓 방식의 요청(Request)와 반환(Response)의 요청이 끝나면 소켓의 접속을 끝내는 형태입니다.

프로토콜 안에 해더를 정의해서 요청을 하면 그 해더값에 맞게 반환하는 게 http 프로토콜 정의입니다.


그러나 Http 프로토콜은 워낙 자주 사용하는 객체이기 때문에 C#에서는 HttpWebRequest라는 클래스를 정의해서 간단하게 사용할 수 있게 만들어져 있습니다.


그럼 HttpWebRequest 사용하는 방법에 대해 설명하겠습니다.

using System;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Http;

namespace Example
{
  class Program
  {
    // HttpRequest를 실행하는 함수
    public static string GetRequest(String url, HttpMethod method, object param = null)
    {
      // 파라미터가 있을 경우, 익명 클래스로 만들기 때문에 Reflection을 이용해서 데이터를 가져온다.
      if (param != null)
      {
        StringBuilder parameter = new StringBuilder();
        // 파라미터키=파라미터값&파라미터키=파라미터값&파라미터키=파라미터값 의 형태로 만든다.
        foreach (var p in param.GetType().GetProperties())
        {
          if (parameter.Length > 0)
          {
            parameter.Append("&");
          }
          parameter.AppendFormat("{0}={1}", p.Name, p.GetValue(param));
        }
        param = parameter.ToString();
      }
      else
      {
        param = "";
      }
      // Http method가 GET 방식의 경우 파라미터를 url 주소 뒤에 붙인다.
      if (HttpMethod.Get.Equals(method))
      {
        if (url.Contains("?"))
        {
          url += "&" + param;
        }
        else
        {
          url += "?" + param;
        }
      }
      
      // url를 통해 HttpWebRequest 클래스를 생성한다.
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
      // 해더의 메소드를 정의한다.
      request.Method = method.ToString();
      // 해더의 ContentType를 정의한다.
      request.ContentType = "application/x-www-form-urlencoded";
      // request에 프로퍼티로 정의되지 않는 해더의 경우는 Indexer의 형식으로 정의할 수 있다.
      // 프로퍼티로 정의된 해더의 경우, 아래와 같이 정의할 경우 에러가 발생한다.
      request.Headers["Upgrade-Insecure-Requests"] = "1";
      
      // Http method가 POST 방식의 경우, 해더 아래에
      if (HttpMethod.Post.Equals(method))
      {
        byte[] byteArray = Encoding.UTF8.GetBytes((string)param);
        request.ContentLength = byteArray.Length;
        using (Stream dataStream = request.GetRequestStream())
        {
          dataStream.Write(byteArray, 0, byteArray.Length);
        }
      }
      
      // Http 프로토콜을 접속해서 response 받기
      using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
      {
        // 프로토콜의 반환 코드를 받을 수 있다. (200이면 정상이다.)
        Console.WriteLine((int)response.StatusCode);
        // 스트림으로 반환 결과값을 받는다.
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
          return reader.ReadToEnd();
        }
      }
    }
    static void Main(string[] args)
    {
      // localhost에 Get 방식으로 접속해서 파라미터는 param=test이다.
      // http://localhost/index.php?param=test
      String html = GetRequest("http://localhost/index.php", HttpMethod.Get, new { param = "test" });
      Console.WriteLine(html);
      
      // localhost에 Post 방식으로 접속해서 파라미터는 param=test이다.
      html = GetRequest("http://localhost/index.php", HttpMethod.Post, new { param = "test" });
      Console.WriteLine(html);

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

제가 위 소스를 위해서 apache로 php 예제를 만들었습니다.

<?php
  error_reporting(E_ALL & ~E_NOTICE);
  // Post 방식의 경우 Post에서 값을 가져온다.
  echo "POST - ".$_POST["param"];
  echo "<br />";
  // Get 방식의 경우 파라미터에서 주소를 가져온다.
  echo "GET - ".$_GET["param"];
?>

위 php를 http://localhost/index.php로 지정한 다음, 위 C#으로 작성한 소스를 실행하겠습니다.

위와 같은 결과를 얻었습니다.


200은 GetRequest 함수 안에서 response.StatusCode의 값입니다. 200은 정상이란 뜻입니다.

제가 GetRequest를 두 번 실행했기 때문에 두번의 결과를 얻었습니다.

첫번째는 Get 방식으로 접속했기 때문에 Get의 데이터에 test가 있네요.

두번째는 Post 방식으로 접속했기 때문에 Post의 데이터에 test가 있습니다.


웹 스크래핑의 경우는 우리가 자동화 테스트 소스를 만들거나 실제 동적으로 움직이는 데이터를 얻을 때 사용합니다만, 실제로 렌더링의 속도가 걸리기 때문에 매우 느립니다.

그러나 HttpWebRequest의 경우는 렌더링이 움직이지 않기 때문에 매우 빠르고 데이터도 취득할 수 있지만 meta데이터를 가져오는 크롤링을 만들 때 유용할 것 같습니다.


여기까지 HttpWebRequest 를 이용해서 웹 페이지를 읽어 드리는 방법에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.