Tengo problemas para manejar caracteres Unicode de texto obtenido de diferentes páginas web (en diferentes sitios). Estoy usando BeautifulSoup.
El problema es que el error no siempre es reproducible; a veces funciona con algunas páginas y, a veces, vomita al lanzar un UnicodeEncodeError . He intentado casi todo lo que se me ocurre y, sin embargo, no he encontrado nada que funcione de manera consistente sin arrojar algún tipo de error relacionado con Unicode.
Una de las secciones de código que está causando problemas se muestra a continuación:
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()Aquí hay un seguimiento de pila producido en ALGUNAS cadenas cuando se ejecuta el fragmento anterior:
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)Sospecho que esto se debe a que algunas páginas (o más específicamente, las páginas de algunos de los sitios) pueden estar codificadas, mientras que otras pueden no estar codificadas. Todos los sitios tienen su sede en el Reino Unido y proporcionan datos destinados al consumo en el Reino Unido, por lo que no hay problemas relacionados con la internalización o el manejo de texto escrito en otro idioma que no sea inglés.
¿Alguien tiene alguna idea sobre cómo resolver esto para que pueda solucionar este problema CONSTANTEMENTE?
Puede usar unicodedata para evitar UnicodeEncodeError. Aquí hay un ejemplo:
import unicodedata agent_telno = agent.find('div', 'agent_contact_number') agent_telno = unicodedata.normalize("NFKD", agent_telno) #it will remove all unwanted character like '\xa0' agent_telno = '' if agent_telno is None else agent_telno.contents[0] p.agent_info = str(agent_contact + ' ' + agent_telno).strip()Puede establecer la codificación de caracteres en UTF-8 antes de ejecutar su secuencia de comandos:
export LC_CTYPE="en_US.UTF-8"Esto generalmente debería resolver el problema.
Esto funciona para mi:
export LC_CTYPE="en_US.UTF-8"En caso de que sea un problema con una declaración de impresión, muchas veces es solo un problema con la impresión del terminal. Esto me ayudó: export PYTHONIOENCODING=UTF-8
Respuesta tardía, pero este error está relacionado con la codificación de su terminal que no admite ciertos caracteres.
Lo arreglé en python3 usando:
import sys import io sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8') print("é, à, ...")Abra la terminal y ejecute el siguiente comando:
export LC_ALL="en_US.UTF-8"Actualización para python 3.0 y posteriores. Pruebe lo siguiente en el editor de python:
locale-gen en_US.UTF-8 export LANG=en_US.UTF-8 LANGUAGE=en_US.en LC_ALL=en_US.UTF-8Esto establece la codificación local predeterminada del sistema en el formato UTF-8.
Se puede leer más aquí en PEP 538 -- Conversión de la configuración regional C heredada a una configuración regional basada en UTF-8 .
Muchas respuestas aquí (@agf y @Andbdrew, por ejemplo) ya han abordado los aspectos más inmediatos de la pregunta OP.
Sin embargo, creo que hay un aspecto sutil pero importante que se ha ignorado en gran medida y que es muy importante para todos los que, como yo, terminaron aquí mientras intentaban dar sentido a las codificaciones en Python: la gestión de la representación de caracteres de Python 2 y Python 3 es muy diferente. . Siento que una gran parte de la confusión tiene que ver con las personas que leen sobre codificaciones en Python sin conocer la versión.
Sugiero a cualquier persona interesada en comprender la causa raíz del problema OP que comience leyendo la introducción de Spolsky a las representaciones de caracteres y Unicode y luego pase a Batchelder en Unicode en Python 2 y Python 3.
Tuve este problema al tratar de enviar caracteres Unicode a stdout , pero con sys.stdout.write , en lugar de imprimir (para que también pudiera admitir la salida a un archivo diferente).
De la propia documentación de BeautifulSoup , resolví esto con la biblioteca de códecs:
import sys import codecs def main(fIn, fOut): soup = BeautifulSoup(fIn) # Do processing, with data including non-ASCII characters fOut.write(unicode(soup)) if __name__ == '__main__': with (sys.stdin) as fIn: # Don't think we need codecs.getreader here with codecs.getwriter('utf-8')(sys.stdout) as fOut: main(fIn, fOut)En cáscara:
Encuentre la configuración regional compatible con UTF-8 mediante el siguiente comando:
locale -a | grep "UTF-8"Exportarlo, antes de ejecutar el script, por ejemplo:
export LC_ALL=$(locale -a | grep UTF-8)o manualmente como:
export LC_ALL=C.UTF-8 Pruébelo imprimiendo un carácter especial, por ejemplo, ™ :
python -c 'print(u"\u2122");'Anteriormente probado en Ubuntu.
Por desgracia, esto funciona en Python 3 al menos ...
Pitón 3
A veces, el error está en las variables de entorno y acaba así.
import os import locale os.environ["PYTHONIOENCODING"] = "utf-8" myLocale=locale.setlocale(category=locale.LC_ALL, locale="en_GB.UTF-8") ... print(myText.encode('utf-8', errors='ignore'))donde los errores se ignoran en la codificación.
Trate de evitar la conversión de variable a str(variable). A veces, puede causar el problema.
Consejo simple para evitar:
try: data=str(data) except: data = data #Don't convert to StringEl ejemplo anterior también resolverá el error de codificación.
Este problema ocurre a menudo cuando un proyecto Django se implementa usando Apache. Debido a que Apache establece la variable de entorno LANG=C en /etc/sysconfig/httpd. Simplemente abra el archivo y comente (o cambie a su estilo) esta configuración. O use la opción lang del comando WSGIDaemonProcess, en este caso podrá configurar diferentes variables de entorno LANG para diferentes hosts virtuales.
La solución recomendada no funcionó para mí, y podría vivir tirando todos los caracteres que no son ascii, por lo que
s = s.encode('ascii',errors='ignore')lo cual me dejo algo despojado que no arroja errores.
Esto funcionará:
>>>print(unicodedata.normalize('NFD', re.sub("[\(\[].*?[\)\]]", "", "bats\xc3\xa0")).encode('ascii', 'ignore'))Producción:
>>>batsEn el caso general de escribir esta cadena de codificación no admitida (digamos data_that_causes_this_error ) en algún archivo (por ejemplo, results.txt ), esto funciona
f = open("results.txt", "w") f.write(data_that_causes_this_error.encode('utf-8')) f.close()Necesitas leer el Python Unicode HOWTO . Este error es el primer ejemplo .
Básicamente, deje de usar str para convertir de unicode a texto/bytes codificados.
En su lugar, use correctamente .encode() para codificar la cadena:
p.agent_info = u' '.join((agent_contact, agent_telno)).encode('utf-8').strip()o trabajar enteramente en Unicode.
¡Este es un punto de dolor clásico de Python Unicode! Considera lo siguiente:
a = u'bats\u00E0' print a => batsàTodo bien hasta ahora, pero si llamamos a str(a), veamos qué sucede:
str(a) Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)¡Oh, chapuzón, eso no le hará ningún bien a nadie! Para corregir el error, codifique los bytes explícitamente con .encode y dígale a Python qué códec usar:
a.encode('utf-8') => 'bats\xc3\xa0' print a.encode('utf-8') => batsàVoil\u00E0!
El problema es que cuando llama a str(), python usa la codificación de caracteres predeterminada para intentar codificar los bytes que le dio, que en su caso a veces son representaciones de caracteres Unicode. Para solucionar el problema, debe decirle a Python cómo manejar la cadena que le da usando .encode('whatever_unicode'). La mayoría de las veces, debería estar bien usando utf-8.
Para una excelente exposición sobre este tema, vea la charla PyCon de Ned Batchelder aquí: http://nedbatchelder.com/text/unipain.html
De hecho, descubrí que, en la mayoría de mis casos, simplemente eliminar esos caracteres es mucho más simple:
s = mystring.decode('ascii', 'ignore')Un problema sutil que hace que incluso la impresión falle es tener las variables de entorno configuradas incorrectamente, p. aquí LC_ALL establecido en "C". En Debian desaconsejan configurarlo: Debian wiki on Locale
$ echo $LANG en_US.utf8 $ echo $LC_ALL C $ python -c "print (u'voil\u00e0')" Traceback (most recent call last): File "<string>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128) $ export LC_ALL='en_US.utf8' $ python -c "print (u'voil\u00e0')" voilà $ unset LC_ALL $ python -c "print (u'voil\u00e0')" voilàEncontré un trabajo elegante para eliminar los símbolos y continuar manteniendo la cadena como la siguiente:
yourstring = yourstring.encode('ascii', 'ignore').decode('ascii')Es importante tener en cuenta que usar la opción ignorar es peligroso porque elimina silenciosamente cualquier compatibilidad con Unicode (e internacionalización) del código que la usa, como se ve aquí (convertir Unicode):
>>> u'City: Malmö'.encode('ascii', 'ignore').decode('ascii') 'City: Malm'Para mí, lo que funcionó fue:
BeautifulSoup(html_text,from_encoding="utf-8")Espero que esto ayude a alguien.
El problema es que está intentando imprimir un carácter Unicode, pero su terminal no lo admite.
Puede intentar instalar language-pack-en para solucionarlo:
sudo apt-get install language-pack-enque proporciona actualizaciones de datos de traducción al inglés para todos los paquetes compatibles (incluido Python). Instale un paquete de idioma diferente si es necesario (según los caracteres que intente imprimir).
En algunas distribuciones de Linux, es necesario para asegurarse de que las configuraciones regionales predeterminadas en inglés estén configuradas correctamente (para que los caracteres Unicode puedan ser manejados por shell/terminal). A veces es más fácil instalarlo que configurarlo manualmente.
Luego, al escribir el código, asegúrese de usar la codificación correcta en su código.
Por ejemplo:
open(foo, encoding='utf-8')Si aún tiene un problema, vuelva a verificar la configuración de su sistema, como:
Su archivo de configuración regional ( /etc/default/locale ), que debería tener, por ejemplo,
LANG="en_US.UTF-8" LC_ALL="en_US.UTF-8"o:
LC_ALL=C.UTF-8 LANG=C.UTF-8 Valor de LANG / LC_CTYPE en shell.
Compruebe qué configuración regional admite su shell haciendo lo siguiente:
locale -a | grep "UTF-8"Demostración del problema y la solución en una máquina virtual nueva.
Inicialice y aprovisione la VM (por ejemplo, usando vagrant ):
vagrant init ubuntu/trusty64; vagrant up; vagrant sshVer: cajas de Ubuntu disponibles . .
Impresión de caracteres Unicode (como el signo de marca comercial como ™ ):
$ python -c 'print(u"\u2122");' Traceback (most recent call last): File "<string>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode character u'\u2122' in position 0: ordinal not in range(128) Ahora instalando language-pack-en :
$ sudo apt-get -y install language-pack-en The following extra packages will be installed: language-pack-en-base Generating locales... en_GB.UTF-8... /usr/sbin/locale-gen: done Generation complete.Ahora el problema debe ser resuelto:
$ python -c 'print(u"\u2122");' ™De lo contrario, intente con el siguiente comando:
$ LC_ALL=C.UTF-8 python -c 'print(u"\u2122");' ™Funciones auxiliares simples que se encuentran aquí .
def safe_unicode(obj, *args): """ return the unicode representation of obj """ try: return unicode(obj, *args) except UnicodeDecodeError: # obj is byte string ascii_text = str(obj).encode('string_escape') return unicode(ascii_text) def safe_str(obj): """ return the byte string representation of obj """ try: return str(obj) except UnicodeEncodeError: # obj is unicode return unicode(obj).encode('unicode_escape')Acabo de tener este problema, y Google me llevó aquí, así que solo para agregar a las soluciones generales aquí, esto es lo que funcionó para mí:
# 'value' contains the problematic data unic = u'' unic += value value = unicTuve esta idea después de leer la presentación de Ned .
Sin embargo, no pretendo entender completamente por qué esto funciona. Entonces, si alguien puede editar esta respuesta o agregar un comentario para explicar, se lo agradeceré.
Acabo de usar lo siguiente:
import unicodedata message = unicodedata.normalize("NFKD", message)Compruebe lo que dice la documentación al respecto:
unicodedata.normalize(form, unistr) Devuelve el formulario normal para la cadena Unicode unistr. Los valores válidos para el formulario son 'NFC', 'NFKC', 'NFD' y 'NFKD'.
El estándar Unicode define varias formas de normalización de una cadena Unicode, según la definición de equivalencia canónica y equivalencia de compatibilidad. En Unicode, varios caracteres se pueden expresar de varias formas. Por ejemplo, el carácter U+00C7 (LETRA C MAYÚSCULA LATINA CON CEDILLA) también se puede expresar como la secuencia U+0043 (LETRA C MAYÚSCULA LATINA) U+0327 (CEDILLA COMBINADA).
Para cada carácter, hay dos formas normales: la forma normal C y la forma normal D. La forma normal D (NFD) también se conoce como descomposición canónica y traduce cada carácter a su forma descompuesta. La forma normal C (NFC) primero aplica una descomposición canónica, luego vuelve a componer caracteres precombinados.
Además de estas dos formas, hay dos formas normales adicionales basadas en la equivalencia de compatibilidad. En Unicode, se admiten ciertos caracteres que normalmente se unificarían con otros caracteres. Por ejemplo, U+2160 (NÚMERO ROMANO UNO) es realmente lo mismo que U+0049 (LETRA I MAYÚSCULA LATINA). Sin embargo, se admite en Unicode por compatibilidad con juegos de caracteres existentes (p. ej., gb2312).
La forma normal KD (NFKD) aplicará la descomposición de compatibilidad, es decir, reemplazará todos los caracteres de compatibilidad con sus equivalentes. La forma normal KC (NFKC) aplica primero la descomposición de compatibilidad, seguida de la composición canónica.
Incluso si dos cadenas Unicode están normalizadas y parecen iguales para un lector humano, si una tiene caracteres combinados y la otra no, es posible que no se comparen de la misma manera.
Me lo soluciona. Simple y fácil.
bueno, probé todo pero no sirvió de nada, después de buscar en Google me di cuenta de lo siguiente y me ayudó. Python 2.7 está en uso.
# encoding=utf8 import sys reload(sys) sys.setdefaultencoding('utf8')Aquí hay un refrito de algunas otras respuestas llamadas "descartar". Hay situaciones en las que simplemente deshacerse de los caracteres/cadenas problemáticos es una buena solución, a pesar de las protestas expresadas aquí.
def safeStr(obj): try: return str(obj) except UnicodeEncodeError: return obj.encode('ascii', 'ignore').decode('ascii') except: return ""Probándolo:
if __name__ == '__main__': print safeStr( 1 ) print safeStr( "test" ) print u'98\xb0' print safeStr( u'98\xb0' )Resultados:
1 test 98° 98ACTUALIZACIÓN: Mi respuesta original fue escrita para Python 2. Para Python 3 :
def safeStr(obj): try: return str(obj).encode('ascii', 'ignore').decode('ascii') except: return "" Nota: si prefiere dejar un ? indicador donde están los caracteres Unicode "inseguros", especifique replace en lugar de ignore en la llamada para codificar para el controlador de errores.
Sugerencia: ¿es posible que desee nombrar esta función toAscii en su lugar? eso es cuestion de preferencia...
Finalmente, aquí hay una versión PY2/3 más robusta que usa six , donde opté por replace , y agregué algunos intercambios de caracteres para reemplazar las comillas y apóstrofes elegantes de Unicode que se curvan hacia la izquierda o hacia la derecha con los simples verticales que son parte del conjunto ascii . Usted mismo puede ampliar tales intercambios:
from six import PY2, iteritems CHAR_SWAP = { u'\u201c': u'"' , u'\u201D': u'"' , u'\u2018': u"'" , u'\u2019': u"'" } def toAscii( text ) : try: for k,v in iteritems( CHAR_SWAP ): text = text.replace(k,v) except: pass try: return str( text ) if PY2 else bytes( text, 'replace' ).decode('ascii') except UnicodeEncodeError: return text.encode('ascii', 'replace').decode('ascii') except: return "" if __name__ == '__main__': print( toAscii( u'testin\u2019' ) )La siguiente solución funcionó para mí, recién agregada
u "Cadena"
(que representa la cadena como Unicode) antes de mi cadena.
result_html = result.to_html(col_space=1, index=False, justify={'right'}) text = u""" <html> <body> <p> Hello all, <br> <br> Here's weekly summary report. Let me know if you have any questions. <br> <br> Data Summary <br> <br> <br> {0} </p> <p>Thanks,</p> <p>Data Team</p> </body></html> """.format(result_html)Simplemente agregue a una codificación variable ('utf-8')
agent_contact.encode('utf-8')Siempre pongo el siguiente código en las dos primeras líneas de los archivos de python:
# -*- coding: utf-8 -*- from __future__ import unicode_literalsSi tiene algo como packet_data = "This is data" , haga esto en la siguiente línea, justo después de inicializar packet_data :
unic = u'' packet_data = unicEncontramos este error cuando manage.py migrate en Django con accesorios localizados.
Nuestra fuente contenía la declaración # -*- coding: utf-8 -*- , MySQL se configuró correctamente para utf8 y Ubuntu tenía el paquete de idioma y los valores apropiados en /etc/default/locale .
El problema era simplemente que al contenedor de Django (nosotros usamos la ventana acoplable) le faltaba la variable de entorno LANG .
Establecer LANG en en_US.UTF-8 y reiniciar el contenedor antes de volver a ejecutar las migraciones solucionó el problema.