gimmesilver's blog

Agbird.egloos.com

포토로그



하스켈로 웹 크롤러 구현하기...2 하스켈 스프링노트

요즘은 블로그에 글 쓸 시간이 정말 부족하군요...
그래도 마냥 손놓고 있을 수는 없어서 일단 약간이나마 글을 써보도록 하겠습니다.

우선 백승우님이 이전 글에서 크롤러에 대해 물어 보셨기 때문에 웹 크롤러에 대해 간단하게 설명하겠습니다.
웹 크롤러는 자동으로 웹문서를 수집하는 프로그램입니다. 웹 로봇, 스파이더 등의 이름으로도 불립니다.
기본 원리는 단순합니다. 최초 시작 url 주소리스트를 시작점으로 해서 HTTP 프로토콜을 이용해 웹 문서를 요청하고 그 문서의 내용에서 링크 url 을 추출하여 다시 추가적인 문서 수집을 해나갑니다. 이 과정을 계속 반복하면 됩니다.
물론 실제 사용되는 크롤러에서는 효율성과 사이클링 방지를 위해 중복 문서를 제거하거나 웹 호스팅 서버에 과부하가 걸리는 것을 막기 위해 수집 속도를 조절하는 등의 여러 가지 정책이 사용됩니다.

먼저 크롤링을 위해서는 HTTP 프로토콜을 통한 통신 기능이 필요합니다.
하스켈 컴파일러인  GHC에는 여러 가지 좋은 라이브러리들이 많으며 특히 컴파일러를 설치하면 제공되는 기본 라이브러리 외에도 많은 확장 라이브러리들이 계속 추가 및 발전되고 있습니다.
이런 확장 라이브러리는 마치 Perl의 CPAN과 유사한 HackageDB 라고 하는 곳에 잘 정리되고 있습니다. 그리고 저는 HTTP통신을 위해 HackageDB에 있는 Network.HTTP 라이브러리를 사용했습니다. 이 라이브러리는 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HTTP-3000.0.0 에서 다운받을 수 있습니다.

설치법은 아주 간단합니다. HackageDB에 있는 라이브러리 패키지들은 하스켈의 패키지 포맷인 CABAL(Common Architecture for Building Applications and Libraries) 로 패키징되어 있습니다. 즉, cabal은 간단히 말하면 윈도우즈의 msi처럼 프로그램 배포를 편하게 하기 위한 패키징 포맷이라고 생각하시면 됩니다. 이렇게 패키징된 하스켈 프로그램(혹은 라이브러리)는 다음과 같은 방법으로 설치할 수 있습니다.
우선 당연히 GHC 컴파일러가 있어야 합니다. 참고로 전 현재 시점에서 가장 최신 버전인 GHC 6.6.1 컴파일러를 사용하고 있습니다. 이 컴파일러를 정상적으로 설치했다면 bin 디렉토리에 runghc.exe 라는 실행 파일이 있을 것입니다. 이 프로그램이 cabal 포맷으로 된 패키지를 설치할 수 있는 프로그램입니다.
HTTP라이브러리를 다운받아 압축을 풀고 나면 아마 디렉토리에 setup.lhs라는 파일이 있을 것입니다. 그러면 해당 디렉토리에서 콘솔 모드로 아래와 같이 입력합니다.

runghc setup.lhs configure

그러면 해당 패키지를 설치할 수 있는지 여부를 체크하게 됩니다. 보통 이 패키지 설치를 위해 필요한 라이브러리들의 디펜던시를 체크하는 것이 주 목적입니다. HTTP 라이브러리의 경우 GHC의 기본 라이브러리만 사용하기 때문에 무사히 설정 확인이 끝날 것입니다. 그러면 이제 다음과 같이 차례로 입력합니다.

runghc setup.lhs build
runghc setup.lhs install

이제 정상적으로 라이브러리가 설치되었는지 확인하는 의미에서 아래 샘플을 작성하고 실행시켜 봅니다.

import Network.HTTP
import Network.URI

get url = do
    case parseURI url of
        Nothing -> putStrLn "Invalid url format"
        Just u -> do response <- simpleHTTP $ request u
                     case response of
                         Left error -> putStrLn "HTTP response error"
                         Right repData -> putStrLn $ show $ rspCode repData
       
request uri = Request{ rqURI = uri,
                       rqMethod = GET,
                       rqHeaders = [],
                       rqBody = "" }

위 소스를 파일에 저장한 후 ghci 인터프리터로 다음과 같이 확인해 봅니다.

get "http://agbird.egloos.com"

아마 다음과 같이 결과가 나올 것입니다.

(2,0,0)

위 소스를 간략히 설명하자면,
parseURI 함수는 문자열을 받아 URI 타입으로 반환하는 함수입니다. 하스켈은 자료형을 매우 엄격하게 사용하며 대개의 경우 각 문맥에 맞는 자료형을 정의해 사용합니다. 따라서 HTTP 통신을 위한 url도 그냥 문자열을 사용하는 것이 아니라 이렇게 URI 타입을 따로 정의해 놓았습니다.
parseURI는 Maybe 라는 타입을 반환하는데 입력 문자열을 파싱해서 올바른 형태인 경우에만 Just URI 타입으로 반환하며 잘못된 형식인 경우에는 Nothing 을 반환합니다.
아마 하스켈에 익숙하지 않은 분들은 이 Maybe 타입이 생소할 것입니다. 자세한 것은 차차 알아나가도록 하고 어쨌든 Maybe 타입은 에러 처리를 위해 사용되는 idiom 정도로만 이해하시기 바랍니다. 위의 예제의 parseURI처럼 Maybe 타입을 사용하게 되면 에러 발생 시에는 Nothing을 반환하고 올바르게 처리되면 해당 반환값 앞에 Just를 붙히고 반환합니다. 그러면 해당 함수를 호출한 쪽에서는 Nothing 값인 경우에는 에러 처리를 하고 Just 값인 경우 뒤에 붙은 값만 뽑아서 처리하면 됩니다.
처음에는 어색할지 몰라도 익숙해지면 에러 처리가 매우 간단해지고 에러 상황과 정상 상황을 자연스럽게 분리해서 기술할 수 있는 장점이 있습니다. (이에 대한 좀더 자세한 설명은 귤님이 쓰신 하스켈 모나드에 관한 글을 참고하시기 바랍니다.)

simpleHTTP는 아주 간단한 HTTP 통신을 위해 제공된 라이브러리 함수입니다. 위 에제에서처럼 Request 자료형 값을 넘겨주면 서버와 통신을 수행하고 그 결과값을 넘겨줍니다.

결과값에는 결과 코드값, 결과 코드에 해당하는 문자열, 헤더, 바디 값이 있는데 각각 rspCode, rspReason, rspHeaders, rspBody 함수를 통해 확인할 수 있습니다. 예를 들어 위 소스의

Right repData -> putStrLn $ show $ rspCode repData

를 아래와 같이 바꾸면

Right repData -> putStrLn $ (show $ rspCode repData) ++ " - " ++ rspReason repData

결과는 아래와 같이 나옵니다.

(2,0,0) - OK

다음 글에서는 웹 크롤러 소스를 설명하도록 하겠습니다.


핑백

  • gimmesilver's blog : 하스켈로 웹크롤러 구현하기...3 2007-07-15 14:02:18 #

    ... 사용될 외부 모듈을 기술한 후에 구현 소스가 이어집니다.import Network.HTTP -- HTTP 통신을 위한 각종 함수들이 정의되어 있습니다. 하스켈로 웹크롤러 구현하기...2 참조import Network.URI -- URI 파싱 및 상대 URL을 절대 URL로 변환하기와 같은 URL 파싱 관련 함수들이 ... more

덧글

  • 백승우 2007/07/08 23:28 # 답글

    설명 감사합니다.^^

    요청한 문서내에 존재하는 링크를 추출해내고, 다시 추출해낸 링크로 수집을 해나간다는 말씀인가요?
    그러면, 엄청나게 많은 부하가. ㅡ ㅡ

    (맞는지 모르겠지만) 종단점을 어떻게 처리하실지 궁금해지네요..
    끊임없이 계속 수집하다간 부하가 엄청날 것 같아서..
  • silverbird 2007/07/09 10:01 # 답글

    // 백승우
    제가 설명드릴 크롤러에서는 URL 갯수를 제한하여 처리할 것입니다. 그리고 그 역할을 하는 것이 LimitUrlQueue라는 데이터 타입과 pushQueue, popQueue 함수입니다. 이에 대한 설명은 다음 글에서 하도록 하겠습니다.
댓글 입력 영역