[python] Python에서 오류없이 유니 코드를 ASCII로 변환

내 코드는 웹 페이지를 긁은 다음 유니 코드로 변환합니다.

html = urllib.urlopen(link).read()
html.encode("utf8","ignore")
self.response.out.write(html)

그러나 나는 얻는다 UnicodeDecodeError:


Traceback (most recent call last):
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/webapp/__init__.py", line 507, in __call__
    handler.get(*groups)
  File "/Users/greg/clounce/main.py", line 55, in get
    html.encode("utf8","ignore")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2818: ordinal not in range(128)

HTML에 어딘가에 유니 코드에서 잘못 구성된 시도가 포함되어 있다고 가정합니다. 오류를 발생시키는 대신 문제를 일으키는 코드 바이트를 삭제할 수 있습니까?



답변

2018 업데이트 :

2018 년 2 월 현재와 같은 압축 사용 gzip인기를 얻었습니다 (Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow 및 Stack Exchange Network 사이트와 같은 대규모 사이트를 포함한 모든 웹 사이트의 약 73 % 가 압축 사용).
응답이 gzipped 인 원래 답변과 같이 간단한 디코딩을 수행하면 다음과 유사한 오류가 발생합니다.

UnicodeDecodeError : ‘utf8’코덱이 위치 1에서 바이트 0x8b를 디코딩 할 수 없습니다. 예기치 않은 코드 바이트

gzpipped 응답을 디코딩하려면 Python 3에서 다음 모듈을 추가해야합니다.

import gzip
import io

참고 : Python 2에서는 StringIO대신 대신 사용 합니다.io

그런 다음 내용을 다음과 같이 파싱 할 수 있습니다.

response = urlopen("https://example.com/gzipped-ressource")
buffer = io.BytesIO(response.read()) # Use StringIO.StringIO(response.read()) in Python 2
gzipped_file = gzip.GzipFile(fileobj=buffer)
decoded = gzipped_file.read()
content = decoded.decode("utf-8") # Replace utf-8 with the source encoding of your requested resource

이 코드는 응답을 읽고 바이트를 버퍼에 배치합니다. gzip모듈은 다음 판독하여 버퍼 GZipFile기능. 그 후, zip으로 압축 된 파일을 다시 바이트 단위로 읽고 결국 읽을 수있는 텍스트로 디코딩 할 수 있습니다.

2010 년 원문 :

에 사용 된 실제 값을 얻을 수 있습니까 link?

또한 .encode()이미 인코딩 된 바이트 문자열을 시도 할 때 일반적으로이 문제가 발생 합니다. 따라서 다음과 같이 먼저 해독하려고 할 수 있습니다.

html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")

예로서:

html = '\xa0'
encoded_str = html.encode("utf8")

실패

UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)

동안:

html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")

오류없이 성공합니다. “windows-1252″는 제가 예제 로 사용한 것 입니다. 나는 chardet 에서 이것을 얻었고 그것이 옳다 는 0.5 자신감을 가지고있었습니다! (자, 1- 문자 길이 문자열로 주어진 것처럼, 당신은 무엇을 기대합니까?) 당신이 .urlopen().read()검색 한 컨텐츠에 적용되는 것에서 리턴 된 바이트 문자열의 인코딩으로 변경해야 합니다.

내가 본 또 다른 문제는 .encode()문자열 메서드가 수정 된 문자열을 반환하고 소스를 수정하지 않는다는 것입니다. 따라서 self.response.out.write(html)html이 html.encode의 인코딩 된 문자열이 아니기 때문에 소용 이 없습니다 (원래 목표였던 경우).

Ignacio가 제안한 것처럼 소스 웹 페이지에서에서 반환 된 문자열의 실제 인코딩을 확인하십시오 read(). 메타 태그 중 하나 또는 응답의 ContentType 헤더에 있습니다. 그런 다음의 매개 변수로 사용하십시오 .decode().

그러나 다른 개발자가 헤더 및 / 또는 메타 문자 집합 선언이 실제 내용과 일치하는지 확인할 책임이 있다고 가정해서는 안됩니다. (피타 어느, 그래, 내가 알아야 할, 나는 이었다 전에 그 중 하나).


답변

>>> u'aあä'.encode('ascii', 'ignore')
'a'

meta응답 또는 Content-Type헤더 의 해당 태그에있는 문자 세트를 사용하여 돌아온 문자열을 디코딩 한 다음 인코딩하십시오.

이 메소드 encode(encoding, errors)는 오류에 대한 사용자 정의 핸들러를 승인합니다. 이외의 기본값 ignore은 다음과 같습니다.

>>> u'aあä'.encode('ascii', 'replace')
b'a??'
>>> u'aあä'.encode('ascii', 'xmlcharrefreplace')
b'a&#12354;&#228;'
>>> u'aあä'.encode('ascii', 'backslashreplace')
b'a\\u3042\\xe4'

https://docs.python.org/3/library/stdtypes.html#str.encode를 참조 하십시오.


답변

Ignacio Vazquez-Abrams의 답변에 대한 확장으로

>>> u'aあä'.encode('ascii', 'ignore')
'a'

문자에서 악센트를 제거하고 기본 양식을 인쇄하는 것이 때때로 바람직합니다. 이것은 달성 할 수 있습니다

>>> import unicodedata
>>> unicodedata.normalize('NFKD', u'aあä').encode('ascii', 'ignore')
'aa'

다른 문자 (예 : 구두점)를 가장 가까운 문자로 변환 할 수도 있습니다. 예를 들어 인코딩 할 때 RIGHT SINGLE QUOTATION MARK 유니 코드 문자가 ASCII APOSTROPHE로 변환되지 않습니다.

>>> print u'\u2019'

>>> unicodedata.name(u'\u2019')
'RIGHT SINGLE QUOTATION MARK'
>>> u'\u2019'.encode('ascii', 'ignore')
''
# Note we get an empty string back
>>> u'\u2019'.replace(u'\u2019', u'\'').encode('ascii', 'ignore')
"'"

이것을 달성하는 더 효율적인 방법이 있지만. 자세한 내용은이 질문을 참조하십시오. Python의 “이 유니 코드에 가장 적합한 ASCII”데이터베이스는 어디에 있습니까?


답변

unidecode를 사용하십시오 -이상한 문자를 즉시 ​​ASCII로 변환하고 중국어를 음성 ASCII로 변환합니다.

$ pip install unidecode

그때:

>>> from unidecode import unidecode
>>> unidecode(u'北京')
'Bei Jing'
>>> unidecode(u'Škoda')
'Skoda'


답변

모든 프로젝트에서이 도우미 기능을 사용합니다. 유니 코드를 변환 할 수 없으면 무시합니다. 이것은 장고 라이브러리에 연결되어 있지만 약간의 연구만으로도 우회 할 수 있습니다.

from django.utils import encoding

def convert_unicode_to_string(x):
    """
    >>> convert_unicode_to_string(u'ni\xf1era')
    'niera'
    """
    return encoding.smart_str(x, encoding='ascii', errors='ignore')

이것을 사용한 후에 더 이상 유니 코드 오류가 발생하지 않습니다.


답변

cmd.exeHTML 콘솔과 같은 깨진 콘솔의 경우 항상 다음을 사용할 수 있습니다.

my_unicode_string.encode('ascii','xmlcharrefreplace')

이렇게하면 ASCII가 아닌 모든 문자를 그대로 유지하면서 순수한 ASCII HTML로 인쇄 할 수 있습니다 .

경고 : 오류를 피하기 위해 프로덕션 코드에서이 코드를 사용하면 코드에 문제가있을 가능성이 높습니다 . 이를위한 유일한 유스 케이스는 비 유니 코드 콘솔로 인쇄하거나 HTML 컨텍스트에서 HTML 엔티티로 쉽게 변환하는 것입니다.

마지막으로 Windows에 있고 cmd.exe chcp 65001를 사용하는 경우 utf-8 출력을 사용하도록 입력 할 수 있습니다 (Lucida Console 글꼴과 함께 작동). 을 추가해야 할 수도 있습니다 myUnicodeString.encode('utf8').


답변

“” “HTML에 유니 코드를 잘못 입력 한 것이 포함되어 있다고 가정합니다.” “”

HTML은 어떤 형식의 “유니 코드 시도”를 포함하지 않아야합니다. 일부 인코딩으로 인코딩 된 유니 코드 문자를 포함해야합니다. 일반적으로 앞에 제공되는 “charset”을 찾으십시오.

문자셋이 UTF-8이라고 가정하고있는 것 같습니다 … 어떤 근거에서? 오류 메시지에 표시된 “\ xA0″바이트는 단일 바이트 문자 세트 (예 : cp1252)가있을 수 있음을 나타냅니다.

HTML을 시작할 때 선언에 대해 이해가되지 않으면 chardet 을 사용 하여 가능한 인코딩이 무엇인지 찾아 보십시오 .

질문에 “regex”로 태그 한 이유는 무엇입니까?

전체 질문을 질문이 아닌 것으로 바꾼 후 업데이트 하십시오.

html = urllib.urlopen(link).read()
# html refers to a str object. To get unicode, you need to find out
# how it is encoded, and decode it.

html.encode("utf8","ignore")
# problem 1: will fail because html is a str object;
# encode works on unicode objects so Python tries to decode it using 
# 'ascii' and fails
# problem 2: even if it worked, the result will be ignored; it doesn't 
# update html in situ, it returns a function result.
# problem 3: "ignore" with UTF-n: any valid unicode object 
# should be encodable in UTF-n; error implies end of the world,
# don't try to ignore it. Don't just whack in "ignore" willy-nilly,
# put it in only with a comment explaining your very cogent reasons for doing so.
# "ignore" with most other encodings: error implies that you are mistaken
# in your choice of encoding -- same advice as for UTF-n :-)
# "ignore" with decode latin1 aka iso-8859-1: error implies end of the world.
# Irrespective of error or not, you are probably mistaken
# (needing e.g. cp1252 or even cp850 instead) ;-)