🌐 웹페이지 인코딩 방식 추출하기

    반응형

    이 글은 <웹 크롤링과 스크레이핑 (파이썬을 이용한, 데이터 수집과 분석을 위한 실전 가이드) (카토 코타, 한빛미디어)> 를 읽고 이해한 바를 바탕으로 정리하여 작성되었습니다.

     

     

    표준 라이브러리인 urllib.request 모듈을 사용하여 urlopen함수에 url을 넣어 웹페이지를 추출할 때,

    반환되는 객체인 HTTPREsponse에서 read()메서드(HTTPResponse.read())로 추출된 응답 본문의 값은 byte 자료형이다.

    따라서 문자열 자료형으로 다루려면 문자 코드를 지정해서 decoding해야 한다! 당연히, 인코딩된 방식으로 다시 디코딩을 해줘야하는데, 이때 웹페이지 문서가 인코딩된 방식을 알기 위해 추출하는 방법을 2가지로 정리해보겠다.

     

    첫번째, HTTP 헤더에서 인코딩 방식을 추출하는 방법이다.

    HTTP 응답의 Content-Type 헤더에서 인코딩 방식을 추출한다.

    일반적으로, 한국어가 포함된 페이지는

    • text/html (: 인코딩이 명시되어 있지 않음 → UTF-8로 다루면 됨)
    • text/html; charset = UTF-8
    • text/html; charset=EUC-KR
      이렇게 3가지의 Content-Type 헤더를 가진다.

    HTTP 헤더에서 인코딩 방식을 추출할 때는 HTTPREsponse.info() 메서드로 추출할 수 있는 HTTPMessage 객체의 get_content_charset() 메서드를 사용하여 추출하면 된다.
    encoding = f.info().get_content_charset(failobj="utf-8")

    import sys
    from urllib.request import urlopen
    f = urlopen('http://www.hanbit.co.kr/store/books/full_book_list.html')
    
    encoding = f.info().get_content_charset(failobj='utf-8')
    # http헤더를 기반으로 인코딩 방식 추출(명시되어 있지 않을 경우 utf-8 사용)
    
    print('encoding: ', encoding)
    
    text = f.read().decode(encoding)
    # 추출한 인코딩 방식으로 디코딩
    
    print(text)

     

     

    두번째. 메타 태그에서 인코딩 방식을 추출하는 방법이다.
    : http 헤더에서 추출하는 인코딩 정보가 맞지 않을 수 있다. (Content-Type 헤더의 값과 실제 사용되고 있는 인코딩 형식이 다를 수 있다.)

    일반적인 브라우저는 HTML 내부의 meta 태그 또는 응답 본문의 바이트열도 확인하여 최종적인 인코딩 방식을 결정하고 화면에 출력한다.

    meta tag에서는 인코딩 형식이 다음과 같이 명시되어 있다.

    • <meta charset = "utf-8">
    • <meta http-equiv="Content-Type" content = "text / html; charset = EUC_KR">

    meta 태그에서도 html 헤더에서와 마찬가지로 charset = 뒤에 인코딩 방식이 나타나 있는 것을 알 수 있다.

    import re
    import sys
    from urllib.request import urlopen
    
    f = urlopen('http://www.hanbit.co.kr/store/books/full_book_list.html')
    bytes_content = f.read()
    # bytes 자료형의 응답 본문 내용 전체를 변수에 저장
    
    scanned_text = bytes_content[:1024].decode('ascii', errors='replace')
    # meta tag에서 charset은 HTML 앞부분에 적혀 있는 경우가 많으므로 bytes_content의 앞부분만 잘라서 ASCII 문자로 디코딩
    # ASCII 범위 이외의 문자는 U+FFFD(REPLACEMENT CHARACTER)로 변환시켜 예외처리
    
    match = re.search(r'charset=["\']?(\w-)+', scanned_text)
    # 디코딩한 문자열에서 정규 표현식으로 charset 추출
    # 정규표현식 'charset=["\']?(\w-)+': "charset= +" + (") 또는 (') 가 0개 또는 1개 + 아무 알파벳 1개 이상
    
    if match:
        encoding = match.group(1)
    else:
        encoding = 'utf-8'
        #charset이 명시되어 있지 않으면 utf-8 사용
    
    print('encoding: ', encoding, file = sys.stderr)
    # 추출한 encoding을 표준 오류에 출력
    
    text = bytes_content.decode(encoding)
    # 알아낸 encoding 방식으로 HTML 응답 본문 전체를 decoding
    print(text)
    반응형

    댓글