strings - Convierte Unicode a ASCII sin errores en Python
unicode escape python (11)
Mi código simplemente raspa una página web, luego la convierte a Unicode.
html = urllib.urlopen(link).read()
html.encode("utf8","ignore")
self.response.out.write(html)
Pero obtengo un 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)
Supongo que eso significa que el HTML contiene algún intento mal formado en Unicode en alguna parte. ¿Puedo simplemente descartar los bytes de código que causan el problema en lugar de obtener un error?
Actualización 2018:
A partir de febrero de 2018, el uso de compresiones como gzip
ha vuelto bastante popular (alrededor del 73% de todos los sitios web lo usan, incluidos grandes sitios como Google, YouTube, Yahoo, Wikipedia, Reddit, y Stack Exchange Network).
Si realiza una decodificación simple como en la respuesta original con una respuesta comprimida, obtendrá un error similar o similar a este:
UnicodeDecodeError: el códec ''utf8'' no puede decodificar el byte 0x8b en la posición 1: byte de código inesperado
Para decodificar una respuesta gzpipped necesita agregar los siguientes módulos (en Python 3):
import gzip
import io
Nota: en Python 2 StringIO
lugar de io
Entonces puedes analizar el contenido de esta manera:
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
Este código lee la respuesta y coloca los bytes en un búfer. El módulo gzip
luego lee el buffer usando la función GZipFile
. Después de eso, el archivo gzip puede leerse en bytes nuevamente y decodificarse en texto legible normalmente al final.
Respuesta original de 2010:
¿Podemos obtener el valor real utilizado para el link
?
Además, generalmente encontramos este problema aquí cuando intentamos .encode()
una cadena de bytes ya codificada. Entonces podrías intentar decodificarlo primero como en
html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")
Como ejemplo:
html = ''/xa0''
encoded_str = html.encode("utf8")
Falla con
UnicodeDecodeError: ''ascii'' codec can''t decode byte 0xa0 in position 0: ordinal not in range(128)
Mientras:
html = ''/xa0''
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")
Triunfa sin error Tenga en cuenta que "windows-1252" es algo que utilicé como ejemplo . ¡Esto lo obtuve de chardet y tenía 0.5 de confianza de que es correcto! (bueno, tal como se da con una cadena de 1 carácter de longitud, ¿qué espera) Usted debe cambiar eso a la codificación de la cadena de bytes devuelta desde .urlopen().read()
a lo que se aplica al contenido que recuperó.
Otro problema que veo es que el método de cadena .encode()
devuelve la cadena modificada y no modifica la fuente en su lugar. De modo que es inútil tener self.response.out.write(html)
ya que html no es la cadena codificada de html.encode (si eso es lo que originalmente se pretendía).
Como sugirió Ignacio, compruebe la página web de origen para la codificación real de la cadena devuelta de read()
. Está en una de las etiquetas Meta o en el encabezado ContentType en la respuesta. Use eso entonces como el parámetro para .decode()
.
Sin embargo, tenga en cuenta que no debe suponerse que otros desarrolladores sean lo suficientemente responsables como para asegurarse de que las declaraciones del encabezado y / o del juego de caracteres meta coinciden con el contenido real. (Que es un PITA, sí, debería saber, yo era uno de esos antes).
Como una extensión a la respuesta de Ignacio Vazquez-Abrams
>>> u''aあä''.encode(''ascii'', ''ignore'')
''a''
A veces es deseable eliminar los acentos de los caracteres e imprimir la forma base. Esto se puede lograr con
>>> import unicodedata
>>> unicodedata.normalize(''NFKD'', u''aあä'').encode(''ascii'', ''ignore'')
''aa''
También es posible que desee traducir otros caracteres (como la puntuación) a sus equivalentes más cercanos, por ejemplo, el carácter unicode RIGHT SINGLE QUOTATION MARK no se convierte a un APOSTROPHE ascii al codificar.
>>> 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'')
"''"
Aunque hay formas más eficientes de lograr esto. Consulte esta pregunta para obtener más detalles. ¿Dónde está la "mejor ASCII de Python para esta base de datos Unicode"?
Creo que la respuesta está ahí, pero solo en pedazos, lo que hace que sea difícil solucionar el problema rápidamente, como
UnicodeDecodeError: ''ascii'' codec can''t decode byte 0xa0 in position 2818: ordinal not in range(128)
Tomemos un ejemplo, supongamos que tengo un archivo que tiene algunos datos en la siguiente forma (que contiene caracteres ascii y no ascii)
1/10/17, 21:36 - Land: Bienvenido ��
y queremos ignorar y preservar solo los caracteres ascii.
Este código hará:
import unicodedata
fp = open(<FILENAME>)
for line in fp:
rline = line.strip()
rline = unicode(rline, "utf-8")
rline = unicodedata.normalize(''NFKD'', rline).encode(''ascii'',''ignore'')
if len(rline) != 0:
print rline
y tipo (línea) le dará
>type(rline)
<type ''str''>
Para consolas rotas como cmd.exe
y HTML, siempre puede usar:
my_unicode_string.encode(''ascii'',''xmlcharrefreplace'')
Esto conservará todos los caracteres no ASCII mientras los hace imprimibles en ASCII puro y en HTML.
ADVERTENCIA : Si usa esto en el código de producción para evitar errores, lo más probable es que haya algún error en su código . El único caso de uso válido para esto es imprimir en una consola no unicode o convertirlo fácilmente a entidades HTML en un contexto HTML.
Y finalmente, si está en Windows y usa cmd.exe, puede escribir chcp 65001
para habilitar la salida de utf-8 (funciona con la fuente Lucida Console). Es posible que deba agregar myUnicodeString.encode(''utf8'')
.
Parece que estás usando python 2.x. Python 2.x se establece de manera predeterminada en ascii y no sabe nada de Unicode. De ahí la excepción.
Simplemente pegue la línea debajo de shebang, funcionará
# -*- coding: utf-8 -*-
Si tiene una line
cadena, puede utilizar el .encode([encoding], [errors=''strict''])
para cadenas para convertir tipos de codificación.
line = ''my big string''
line.encode(''ascii'', ''ignore'')
Para obtener más información sobre el manejo de ASCII y Unicode en Python, este es un sitio realmente útil: https://docs.python.org/2/howto/unicode.html
Use unidecode , incluso convierte caracteres extraños en ascii instantáneamente, e incluso convierte el chino en ascii fonético.
$ pip install unidecode
entonces:
>>> from unidecode import unidecode
>>> unidecode(u''北京'')
''Bei Jing''
>>> unidecode(u''Škoda'')
''Skoda''
Uso esta función auxiliar en todos mis proyectos. Si no puede convertir el Unicode, lo ignora. Esto se relaciona con una biblioteca django, pero con un poco de investigación podrías pasar por alto.
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'')
Ya no recibo ningún error Unicode después de usar esto.
Usted escribió "" "Supongo que eso significa que el HTML contiene algún intento de forma incorrecta en unicode en algún lugar." ""
No se espera que el HTML contenga ningún tipo de "intento de unicode", bien formado o no. Debe contener obligatoriamente caracteres Unicode codificados en alguna codificación, que generalmente se suministra por adelantado ... busque "charset".
Usted parece estar asumiendo que el juego de caracteres es UTF-8 ... ¿por qué motivo? El byte "/ xA0" que se muestra en su mensaje de error indica que puede tener un juego de caracteres de un solo byte, por ejemplo, cp1252.
Si no puede encontrar sentido en la declaración al comienzo del HTML, intente usar chardet para descubrir cuál es la codificación probable.
¿Por qué has etiquetado tu pregunta con "regex"?
Actualice después de que reemplazó toda su pregunta con una no-pregunta:
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) ;-)
>>> u''aあä''.encode(''ascii'', ''ignore'')
''a''
EDITAR:
Decodifique la cadena que obtiene, usando el juego de caracteres en la meta
apropiada en la respuesta o en el encabezado Content-Type
, luego codifique.
El método encode()
acepta otros valores como "ignorar". Por ejemplo: ''reemplazar'', ''xmlcharrefreplace'', ''backslashreplace''. Ver https://docs.python.org/3/library/stdtypes.html#str.encode
unicodestring = ''/xa0''
decoded_str = unicodestring.decode("windows-1252")
encoded_str = decoded_str.encode(''ascii'', ''ignore'')
Funciona para mi