Python 2 UnicodeEncodeError: ‘ascii’ codec can’t encode character — str() 대신 encode()

1. 문제 정의

웹 페이지(여러 사이트)에서 가져온 유니코드 텍스트를 처리하다가 다음과 같은 에러가 발생하는 상황입니다.

1
2
3
4
Traceback (most recent call last):
  File "foobar.py", line 792, in <module>
    p.agent_info = str(agent_contact + ' ' + agent_telno).strip()
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 20: ordinal not in range(128)

문제가 되는 코드는 다음과 같습니다.

1
2
3
4
5
from bs4 import BeautifulSoup

agent_telno = agent.find('div', 'agent_contact_number')
agent_telno = '' if agent_telno is None else agent_telno.contents[0]
p.agent_info = str(agent_contact + ' ' + agent_telno).strip()

핵심 증상은 다음과 같습니다.

증상설명
에러 유형UnicodeEncodeError
실패 코드str(...) 호출 과정에서 발생
실패 문자\xa0 (줄 바꿈 없는 공백, non-breaking space)
재현성사이트에 따라 간헐적으로 발생 — 어떤 페이지는 되고 어떤 페이지는 실패

에러가 항상 재현되지 않는다는 것이 이 문제의 까다로운 점입니다. 어떤 페이지는 정상 동작하고, 다른 페이지에서는 에러를 던집니다.

2. 원인 탐구

에러 메시지를 분해해 보면 원인을 추적할 수 있습니다.

  • 'ascii' codec can't encode — ASCII 코덱이 이 문자를 인코딩할 수 없다는 뜻입니다.
  • character u'\xa0'\xa0는 유니코드의 줄 바꿈 없는 공백(non-breaking space)으로, 웹 페이지 HTML에 자주 등장하는 문자입니다. ASCII에는 존재하지 않습니다.
  • position 20 — 20번째 위치에 해당 문자가 있다는 뜻입니다.

즉, agent_contact 또는 agent_telno에 포함된 유니코드 문자열(예: \xa0)을 str()로 변환하는 순간, Python 2의 str()이 기본 인코딩인 ASCII를 사용하려다 실패한 것입니다.

왜 간헐적으로 발생할까요?

  • 일부 페이지의 텍스트는 순수 ASCII 문자만 포함 → str()이 성공
  • 다른 페이지의 텍스트는 \xa0처럼 ASCII 밖의 유니코드 문자를 포함 → str()이 실패

입력 데이터에 따라 성공/실패가 갈리기 때문에, 코드 자체는 똑같은데 에러가 항상 나지 않는 것입니다.

3. 근본 원인 분석

근본 원인은 Python 2에서 str()로 유니코드를 변환하는 방식에 있습니다.

Python 2에는 두 가지 문자열 타입이 있습니다.

타입설명기본 인코딩
str바이트 문자열ASCII
unicode유니코드 문자열

str()unicodestr 변환 과정에서 시스템 기본 인코딩(ASCII) 을 사용합니다. 따라서 변환 대상에 ASCII 범위 밖의 문자가 하나라도 있으면 UnicodeEncodeError가 발생합니다.

Python 공식 문서(Python Unicode HOWTO)는 이 상황을 바로 이 예외 케이스로 소개합니다. ASCII 기본 인코딩으로는 \xa0와 같은 유니코드 문자를 바이트로 표현할 수 없기 때문에 에러가 나는 것이며, 이는 코드가 아니라 str() 사용 방식의 문제입니다.

즉 근본 원인은 다음 두 가지가 결합된 것입니다.

  1. 웹에서 가져온 텍스트에 \xa0 등 ASCII 밖 유니코드 문자가 존재함
  2. str()이 기본 코드(ASCII)로 유니코드를 인코딩하려 함

4. 코드 해결책

해결 방법은 두 가지입니다. 핵심 원칙은 유니코드 → 바이트 변환에 str()을 쓰지 말고 .encode()를 쓰는 것입니다.

방법 1: .encode('utf-8')로 명시적 인코딩

1
p.agent_info = u' '.join((agent_contact, agent_telno)).encode('utf-8').strip()
  • u' '.join(...)로 유니코드 문자열을 만듭니다.
  • .encode('utf-8')명시적으로 UTF-8 인코딩합니다.
  • .strip()으로 앞뒤 공백을 제거합니다.

\xa0도 UTF-8에서는 정상적으로 인코딩되므로 에러가 발생하지 않습니다.

방법 2: 전체를 유니코드로 처리

가능하다면 프로그램 전체에서 유니코드 문자열로만 작업하고, 입출력 경계(파일 쓰기, 네트워크 전송 등)에서만 인코딩하는 방식이 더 깔끔합니다.

1
p.agent_info = (agent_contact + ' ' + agent_telno).strip()  # str() 제거, 유니코드 유지

이렇게 하면 중간 단계에서 ASCII 강제 변환이 일어나지 않아 에러가 원천적으로 사라집니다.

5. 향후 예방 조치

같은 에러를 반복하지 않으려면 다음을 지키세요.

조치설명
str() 대신 .encode() 사용유니코드 → 바이트 변환은 반드시 .encode('utf-8')로 명시
전체를 유니코드로 유지입출력 경계에서만 인코딩/디코딩
파이썬 3 사용Python 3에서는 str이 유니코드가 되어 이 계열 에러가 크게 줄어듦
문자 규범화\xa0 같은 특수 문자가 필요하면 \xa0 → 일반 공백 등으로 정규화

특히 \xa0(줄 바꿈 없는 공백)는 웹 파싱에서 매우 흔하게 등장하므로, HTML에서 추출한 텍스트를 처리할 때는 항상 유니코드 인코딩을 염두에 두어야 합니다.

출처