[FAQ] 크롤링 데이터의 한글이 깨져요

[FAQ] - 크롤링 데이터의 한글이 깨져요

  • Q: 데이터를 크롤링하는데 한글이 외계어로 표시 됩니다
  • A: 대부분의 경우 한글 인코딩의 문제 입니다. decode('euc-kr') 시도해 보세요.

데이터를 크롤링할 때 한글 데이터가 깨지는 경우가 있다. 다양한 경우가 있지만, 가장 많은 경우가 euc-kr 인코딩된 한글 바이트 문자열 데이터를 그대로 사용하려는 경우이다. 이 경우로 euc-kr 인코딩을 utf-8 인코딩으로 바꾸어 주면 해결 된다.

예를 들어, 데이터를 수집했는데 아래와 같이 데이터가 표시 되는 경우이다.

b'\xc4\xda\xbd\xba\xc7\xc7\xc1\xf6\xbc\xf6'

b로 시작되는 것으로 바이트 나열(byte sequence)라는 의미이며, \x00 모양의 데이터는 1바이트의 16진수 값을 표시한다. 이 데이터가 euc-kr 로 인코딩된 한글이라면 다음과 같이 decode('euc-kr') 함수를 사용해서 유니코드 문자열로 바꿀 수 있다.

In [1]:
s = b'\xc4\xda\xbd\xba\xc7\xc7\xc1\xf6\xbc\xf6'
type(s), s 
Out[1]:
(bytes, b'\xc4\xda\xbd\xba\xc7\xc7\xc1\xf6\xbc\xf6')
In [2]:
u = s.decode('euc-kr')
type(u), u
Out[2]:
(str, '코스피지수')

다량의 데이터라면 아래와 같이 변환한다.

In [3]:
arr = [
    b'K200\xc0\xce\xb5\xa6\xbd\xba',
    b'\xc0\xcf\xb9\xdd\xc1\xd6\xbd\xc4',
    b'\xb9\xe8\xb4\xe7\xc1\xd6\xbd\xc4',
    b'\xc1\xdf\xbc\xd2\xc7\xfc\xc1\xd6\xbd\xc4',
]

for i in arr:
    print(i.decode('euc-kr'))
K200인덱스
일반주식
배당주식
중소형주식

Python2 와 Python3 에서 유니코드

Python 2.x 에서 str과 unicode는 서로 다르다. 이 때문에 str의 인코딩 처리와 unicode 변환이 상당히 번거롭다.

>>> s = "한글ABC"
>>> u = u"한글ABC"
>>> type(s), len(s)
(<type 'str'>, 9)
>>> type(u), len(u)
(<type 'unicode'>, 5)
>>> s == u
False

Python3는 모든 문자열을 유니코드(unicode)로 처리하기 때문에 str과 unicode가 완전히 동일하다. 위와 동일한 코드를 Python3에서 실행한 결과이다.

>>> s = "한글ABC"
>>> u = u"한글ABC"
>>> type(s), len(s)
(<class 'str'>, 5)
>>> type(u), len(u)
(<class 'str'>, 5)
>>> s == u
True

반드시 Python2를 써야만 하는 상황이 아니라면 가급적 Python3를 쓰는 것이 좋다.

EUC-KR 그리고 UTF-8

한글 인코딩과 관련해서 알아 둘만한 인코딩은 크게 4가지 정도(EUC-KR, CP949, UTF-8, UTF-16) 이다.

인코딩 주요핵심 상세설명(위키피디어)
EUC-KR 한글 완성형 https://ko.wikipedia.org/wiki/EUC-KR
CP949 한글 완성형 (마이크로소프트사) https://ko.wikipedia.org/wiki/CP949
UTF-8 유니코드 8비트 (한글 1글자에 3바이트) https://ko.wikipedia.org/wiki/UTF-8
UTF-16 유니코드 16비트 (한글 1글자에 2바이트) https://ko.wikipedia.org/wiki/UTF-16

동일한 문자열도 다른 값으로 인코딩에 따라 다른 값을 갖는다.

>>> '아름다운'.encode('euc-kr')
b'\xbe\xc6\xb8\xa7\xb4\xd9\xbf\xee'
>>> '아름다운'.encode('cp949')
b'\xbe\xc6\xb8\xa7\xb4\xd9\xbf\xee'
>>> '아름다운'.encode('utf-8')
b'\xec\x95\x84\xeb\xa6\x84\xeb\x8b\xa4\xec\x9a\xb4'
>>> '아름다운'.encode('utf-16')
b'\xff\xfeD\xc5\x84\xb9\xe4\xb2\xb4\xc6'

# 참고: utf-16의 시작 2바이트(0xFF 0xFE)는 BOM (Byte Order Mark)  https://ko.wikipedia.org/wiki/바이트_순서_표식

각 인코딩의 주요한 핵심을 간단히 설명하면 아래와 같다.

  • EUC-KR은 한글 완성형 인코딩으로 국내 사이트의 상당수가 EUC-KR 인코딩을 사용하고 있다.
  • CP949는 EUC-KR와 동일한 한글 완성형 인코딩이지만, EUC-KR 보다 확장된 문자 집합을 가지고 있다. 마이크로소프트가 제정하였으며, 주로 윈도우에서 사용한다.
  • UTF-8과 UTF-16은 둘 다 유니코드 인코딩이다. 각각 UTF-8는 8비트 단위로 인코딩하며 한글 1자는 3바이트, 영문자 1자는 1바이트로 표현한다. 자리수는 많이 차지하지만 영문자와 섞어 쓸 때 혹은 특수 문자 사용이 제한적인 인터넷 환경(예: URL)에서 가장 문제가 적다.
가급적 UTF-8 인코딩을 사용하도록 권한다. (데이터 파일, 데이터 송수신, 파이썬 소스 프로그램 등 모두)

인코딩 알아내기

여러 데이터 소스로 부터 데이터를 가져오는 경우 (혹은 텍스트 인코딩이 한가지로 지정되지 않는 데이터인 경우) 인코딩을 추정하는 방법이 있다.

chardet.detect (data) 를 함수를 사용하면 data의 인코딩을 추정해 준다.

In [4]:
import chardet

euc_data = '아름다운 한글'.encode('euc-kr')
print( euc_data )

# 인코딩 알아내기
print (chardet.detect (euc_data))
b'\xbe\xc6\xb8\xa7\xb4\xd9\xbf\xee \xc7\xd1\xb1\xdb'
{'confidence': 0.99, 'encoding': 'EUC-KR'}
In [5]:
euc_data = '아름다운 한글'.encode('utf-8')
print( euc_data )

# 인코딩 알아내기
print (chardet.detect (euc_data))
b'\xec\x95\x84\xeb\xa6\x84\xeb\x8b\xa4\xec\x9a\xb4 \xed\x95\x9c\xea\xb8\x80'
{'confidence': 0.99, 'encoding': 'utf-8'}
{'encoding': 'EUC-KR', 'confidence': 0.99}

결과에서 인코딩('encoding')은 EUC-KR로 추정하였으며 신뢰도(confidence)는 0.99 (99%)라는 의미이다.

결론

  1. 파이썬2 보다 Python3 추천 (Python3는 자체가 유니코드 시스템)
  2. urllib2 보다 requests 추천 (더 적절하게 동작하며 사용도 편리)
  3. 데이터의 인코딩이 EUC-KR 이면, decode('euc-kr') 하여 유니코드로 디코딩
  4. 데이터 저장, 송수신, 프로그램 소스 파일 모두 가능한 UTF-8로 통일

댓글