2010-04-21 23 views
10

Necesito descargar y analizar la página web con lxml y generar la salida UTF-8 xml. Creo esquema en pseudocódigo es más ilustrativa:Codificación en python con lxml: solución compleja

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)) 

Así WebFile puede estar en cualquier codificación (lxml debe manejar esto). El archivo de salida debe estar en utf-8. No estoy seguro de dónde usar la codificación/codificación. ¿Este esquema está bien? (No puedo encontrar un buen tutorial sobre lxml y codificación, pero puedo encontrar muchos problemas con esto ...) Necesito una solución sólida.

Editar:

lo tanto, para el envío de UTF-8 a LXML utilizo

 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') 

Respuesta

18

lxml puede ser un poco torcidas sobre las codificaciones de entrada. Lo mejor es enviar UTF8 y sacar UTF8.

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

Lo que quiere hacer algo vagamente a:

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 se está interactuando con otra biblioteca que ya esté usando etree?

+0

Unicode Dammit parece bueno. Y sobre etree tienes razón, lo he quitado del código. –

+2

¿Por qué no pasar directamente la cadena decodificada (objeto Unicode) a html.fromstring(), en lugar de volver a codificarlo en utf-8? – lajarre

+1

No puedo recordar cuál fue específicamente la motivación hace dos años y medio, pero recuerdo vagamente que a lxml no le gustó la entrada de Unicode en algunos casos. Hay muchas posibilidades de que, sea cual sea el problema, se arregle, por lo que es mejor ignorar esa parte ahora. Sin embargo, a libxml2 (que funciona con lxml) le gusta la entrada UTF-8, por lo que si usted es muy sensible al rendimiento, es posible que desee evitar decodificar esa codificación en particular. –

2

detección de codificación lxml es weak.

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 codificación incorrectas). Es por lo tanto, a menudo suficiente para utilizar únicamente la detección de codificación de BeautifulSoup, llamada UnicodeDammit, y dejar el resto en el propio analizador HTML de lxml , que es varias veces más rápido.

me recomiendan para detectar la codificación usando UnicodeDammit y analizar usando lxml. Además, puede usar el encabezado HTTP Content-Type (debe extraer charset = ENCODING_NAME) para detectar la codificación con mayor precisión.

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

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 hacer más respuesta anterior completo, puede modificarlo a:

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

¿Por qué es mejor que usar chardet?

  1. no ignorar Content-Type cabecera HTTP

    Content-Type: text/html; charset = utf-8

  2. No ignore http-equiv meta tag. Ejemplo:

    ... http-equiv = "Content-Type" content = "text/html; charset = UTF-8" ...

  3. Además de esto, está usando la energía de chardet, cjkcodecs y iconvcodec codecs y many more.