2011-08-28 46 views
23

Estoy escribiendo un rastreador con Python usando BeautifulSoup, y todo estaba yendo viento en popa hasta que me encontré con este sitio:Python y BeautifulSoup problemas de codificación

http://www.elnorte.ec/

estoy recibiendo el contenido con la biblioteca de solicitudes :

r = requests.get('http://www.elnorte.ec/') 
content = r.content 

Si hago una impresión de la variable contenido en ese punto, todos los caracteres especiales españoles parecen estar funcionando bien. Sin embargo, una vez que trato de alimentar la variable contenido a BeautifulSoup todo queda patas arriba:

soup = BeautifulSoup(content) 
print(soup) 
... 
<a class="blogCalendarToday" href="/component/blog_calendar/?year=2011&amp;month=08&amp;day=27&amp;modid=203" title="1009 artículos en este día"> 
... 

al parecer es garbling de seguridad de todos los caracteres especiales (acentos españoles y otras cosas). He intentado hacer content.decode ('utf-8'), content.decode ('latin-1'), también intenté meterme con el parámetro fromEncoding en BeautifulSoup, configurándolo en fromEncoding = 'utf-8' y fromEncoding = 'latin-1', pero aún no hay dados.

Cualquier puntero sería muy apreciado.

Respuesta

17

podría intentar:

r = urllib.urlopen('http://www.elnorte.ec/') 
x = BeautifulSoup.BeautifulSoup(r.read) 
r.close() 

print x.prettify('latin-1') 

puedo obtener la salida correcta. Ah, en este caso especial también podría x.__str__(encoding='latin1').

Supongo que esto se debe a que el contenido está en ISO-8859-1 (5) y el tipo de contenido meta http-equiv dice incorrectamente "UTF-8".

¿Podría confirmar?

+0

Hola Gaikokujin, gracias por su respuesta. Tienes toda la razón, si lo embellece con el parámetro 'latin-1', recupero la cuerda con todos los acentos correctos y todo. Sin embargo, tengo que pasar por la sopa para procesar los enlaces, y si intento hacer una sopa de nuevo, vuelve a enredar los acentos. – David

+0

En realidad, no importa, ahora recibo un error al intentar su sugerencia: UnicodeEncodeError: el códec 'latin-1' no puede codificar caracteres en la posición 62-63: ordinal no está dentro del rango (256) – David

+0

Parece que funciona de nuevo si lo hago: x = BeautifulSoup.BeautifulSoup (r.read(), fromEncoding = 'latin-1'), pero de nuevo, si trato de hacer una nueva sopa de la cuerda embellecida, la vuelve a enredar:/ – David

19

En su caso esta página ha equivocado de datos UTF-8 que confunde BeautifulSoup y lo hace pensar que su página utiliza ventanas-1252, puede hacer este truco:

soup = BeautifulSoup.BeautifulSoup(content.decode('utf-8','ignore')) 

al hacer esto va a descartar cualquier símbolos incorrectos de la fuente de la página y BeautifulSoup codificará a los huéspedes a la derecha.

Puede reemplazar 'ignorar' por 'reemplazar' y verificar el texto para '?' símbolos para ver lo que se ha descartado.

Actualmente es una tarea muy difícil escribir sobre crawler que puede adivinar la codificación de página cada vez con 100% de probabilidad (los navegadores son muy buenos en este momento), puede usar módulos como 'chardet' pero, por ejemplo, en su caso Adivinará la codificación como ISO-8859-2, que tampoco es correcta.

Si realmente necesita poder obtener la codificación de cualquier página que el usuario pueda suministrar, debe construir una función de detección de niveles múltiples (intente con utf-8, intente con latin1, pruebe etc.) (como nosotros lo hizo en nuestro proyecto) o usar algún código de detección de Firefox o cromo como módulo C.

2

La primera respuesta es correcta, esta función algunas veces es efectiva.

def __if_number_get_string(number): 
     converted_str = number 
     if isinstance(number, int) or \ 
      isinstance(number, float): 
       converted_str = str(number) 
     return converted_str 


    def get_unicode(strOrUnicode, encoding='utf-8'): 
     strOrUnicode = __if_number_get_string(strOrUnicode) 
     if isinstance(strOrUnicode, unicode): 
      return strOrUnicode 
     return unicode(strOrUnicode, encoding, errors='ignore') 

    def get_string(strOrUnicode, encoding='utf-8'): 
     strOrUnicode = __if_number_get_string(strOrUnicode) 
     if isinstance(strOrUnicode, unicode): 
      return strOrUnicode.encode(encoding) 
     return strOrUnicode 
0

Sugiero tomar un enfoque más metódico a prueba de tontos.

# 1. get the raw data 
raw = urllib.urlopen('http://www.elnorte.ec/').read() 

# 2. detect the encoding and convert to unicode 
content = toUnicode(raw) # see my caricature for toUnicode below 

# 3. pass unicode to beautiful soup. 
soup = BeautifulSoup(content) 


def toUnicode(s): 
    if type(s) is unicode: 
     return s 
    elif type(s) is str: 
     d = chardet.detect(s) 
     (cs, conf) = (d['encoding'], d['confidence']) 
     if conf > 0.80: 
      try: 
       return s.decode(cs, errors = 'replace') 
      except Exception as ex: 
       pass 
    # force and return only ascii subset 
    return unicode(''.join([ i if ord(i) < 128 else ' ' for i in s ])) 

Puede razonar sin importar lo que arroje a esto, siempre enviará unicode válido a bs.

Como resultado, su árbol analizado se comportará mucho mejor y no fallará en nuevas formas más interesantes cada vez que tenga datos nuevos.

Ensayo y error no funciona en el Código - Hay demasiadas combinaciones :-)

0

Puede probar esto, que trabaja para cada codificación

from bs4 import BeautifulSoup 
    from bs4.dammit import EncodingDetector 
    headers = {"User-Agent": USERAGENT} 
    resp = requests.get(url, headers=headers) 
    http_encoding = resp.encoding if 'charset' in resp.headers.get('content-type', '').lower() else None 
    html_encoding = EncodingDetector.find_declared_encoding(resp.content, is_html=True) 
    encoding = html_encoding or http_encoding 
    soup = BeautifulSoup(resp.content, 'lxml', from_encoding=encoding) 
Cuestiones relacionadas