Codificación en python con lxml – solución compleja

Necesito descargar y analizar la página web con lxml y comstackr la salida xml de UTF-8. Creo que el esquema en pseudocódigo es más ilustrativo:

from lxml import etree webfile = urllib2.urlopen(url) root = etree.parse(webfile.read(), parser=etree.HTMLParser(recover=True)) txt = my_process_text(etree.tostring(root.xpath('/html/body'), encoding=utf8)) output = etree.Element("out") output.text = txt outputfile.write(etree.tostring(output, encoding=utf8)) 

Por lo tanto, el archivo web puede estar en cualquier encoding (lxml debería manejar esto). El archivo de salida tiene que estar en utf-8. No estoy seguro de dónde usar la encoding / encoding. ¿Está bien este esquema? (No puedo encontrar un buen tutorial sobre lxml y encoding, pero puedo encontrar muchos problemas con esto …) Necesito una solución sólida.

Editar:

Así que para enviar utf-8 a lxml yo uso

  converted = UnicodeDammit(webfile, isHTML=True) if not converted.unicode: print "ERR. UnicodeDammit failed to detect encoding, tried [%s]", \ ', '.join(converted.triedEncodings) continue webfile = converted.unicode.encode('utf-8') 

lxml puede ser un poco torpe acerca de las codificaciones de entrada. Es mejor enviar UTF8 y sacar UTF8.

Es posible que desee utilizar el módulo chardet o UnicodeDammit para decodificar los datos reales.

Usted querría hacer algo vagamente como:

 import chardet from lxml import html content = urllib2.urlopen(url).read() encoding = chardet.detect(content)['encoding'] if encoding != 'utf-8': content = content.decode(encoding, 'replace').encode('utf-8') doc = html.fromstring(content, base_url=url) 

No estoy seguro de por qué se está moviendo entre lxml y etree, a menos que esté interactuando con otra biblioteca que ya usa etree.

La detección de encoding lxml es débil .

Sin embargo, tenga en cuenta que el problema más común con las páginas web es la falta de (o la existencia de declaraciones de encoding incorrectas). Por lo tanto, a menudo es suficiente usar solo la detección de encoding de BeautifulSoup, llamada UnicodeDammit, y dejar el rest al propio analizador HTML del lxml, que es varias veces más rápido.

Recomiendo detectar la encoding con UnicodeDammit y analizarla utilizando lxml . Además, puede usar el encabezado http Content-Type (necesita extraer charset = ENCODING_NAME ) para detectar la encoding de manera más precisa.

Para este ejemplo, estoy usando BeautifulSoup4 (también tienes que instalar chardet para una mejor detección automática, porque UnicodeDammit usa chardet internamente ):

 from bs4 import UnicodeDammit if http_charset == "": ud = UnicodeDammit(content, is_html=True) else: ud = UnicodeDammit(content, override_encodings=[http_charset], is_html=True) root = lxml.html.fromstring(ud.unicode_markup) 

O, para que la respuesta anterior sea más completa, puede modificarla a:

 if ud.original_encoding != 'utf-8': content = content.decode(ud.original_encoding, 'replace').encode('utf-8') 

¿Por qué esto es mejor que usar chardet?

  1. No ignoras el encabezado HTTP de tipo de contenido

    Tipo de contenido: texto / html; conjunto de caracteres = utf-8

  2. No ignoras la meta tag http-equiv . Ejemplo:

    … http-equiv = “Content-Type” content = “text / html; charset = UTF-8” …

  3. Además de esto, estás usando el poder de los códecs chardet , cjkcodecs e iconvcodec y muchos más .