2012-06-13 8 views
5

Mi experiencia es en Perl, pero estoy probando Python y BeautifulSoup para un nuevo proyecto.Python, .format() y UTF-8

En este ejemplo, estoy tratando de extraer y presentar los objetivos del enlace y el texto del enlace contenido en una sola página. Aquí está la fuente:

table_row = u'<tr><td>{}</td><td>{}</td></tr>'.encode('utf-8') 
link_text = unicode(link.get_text()).encode('utf-8') 
link_target = link['href'].encode('utf-8') 
line_out = unicode(table_row.format(link_text, link_target)) 

Todas esas llamadas explícitas a .encode ('UTF-8') son mi intento de hacer este trabajo, pero no parece ayudar - lo más probable es que estoy completamente malentendido acerca de cómo Python 2.7 maneja cadenas Unicode.

De todos modos. Esto funciona bien hasta que encuentra U + 2013 en una URL (sí, realmente). En ese momento, las bombas fuera con:

Traceback (most recent call last): 
File "./test2.py", line 30, in <module> 
    line_out = unicode(table_row.encode('utf-8').format(link_text, link_target.encode('utf-8'))) 
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 79: ordinal not in range(128) 

Presumiblemente .formato(), incluso aplicada a una cadena Unicode, está jugando tontos sodomiza y tratando de hacer una operación .decode(). Y como ASCII es el valor predeterminado, lo está usando y, por supuesto, no puede asignar U + 2013 a un carácter ASCII, y por lo tanto ...

Las opciones parecen ser eliminarlo o convertirlo en otra cosa , pero realmente lo que quiero es simplemente preservarlo. En última instancia (este es solo un pequeño caso de prueba) necesito poder presentar enlaces en los que se pueda hacer clic.

La documentación de BS3 sugiere cambiar la codificación predeterminada de ASCII a UTF-8, pero leer comentarios sobre preguntas similares parece ser una muy mala idea, ya que va a ensuciar los diccionarios.

En lugar de usar Python 3.2 en su lugar (lo que significa que no hay Django, lo que estamos considerando para parte de este proyecto) ¿hay alguna manera de hacer que esto funcione limpiamente?

+0

como una regla básica, decodificar (a Unicode) en la entrada y codificación (codificación deseada a) en la salida. – monkut

Respuesta

8

En primer lugar, tenga en cuenta que los dos ejemplos de código no están de acuerdo sobre el texto de la línea problemática:

line_out = unicode(table_row.encode('utf-8').format(link_text, link_target.encode('utf-8'))) 

vs

line_out = unicode(table_row.format(link_text, link_target)) 

La primera es la de la explotación de origen, de modo que es la de mirar. Suponiendo que el resto del primer ejemplo de código es preciso, table_row es una cadena de bytes, porque tomó una cadena Unicode y la codificó. Las cadenas de bytes no se pueden codificar, por lo que Python 2 convierte implícitamente table_row de byte-string a unicode decodificándolo como ascii. De ahí el mensaje de error "UnicodeDecodeError from ascii".

Debe decidir qué cadenas serán cadenas de bytes y cuáles serán cadenas unicode, y será disciplinado al respecto. Recomiendo mantener todo el texto como cadenas Unicode tanto como sea posible.

Aquí hay una presentación que di en PyCon que lo explica todo: Pragmatic Unicode, or, How Do I Stop The Pain?

+0

Aha. Bien gracias. Parece que un intento anterior de entender esto me llevó a pegar .encode() en todas partes, sin darme cuenta de que esto convertiría de una cadena Unicode a una cadena de bytes. ¡Eliminar todos esos parece haber solucionado el problema! –

+0

Además, mea culpa en los fragmentos de código inconsistentes. Saqué las líneas relevantes del código tal como era, y el error de una ejecución anterior que había tenido el mismo problema. –

+0

¡Esta presentación es tan buena! Gracias por compartir (¡y por darlo!). –