web-dev-qa-db-fra.com

Python et BeautifulSoup

J'écris un robot avec Python utilisant BeautifulSoup, et tout se passait bien jusqu'à ce que je tombe sur ce site:

http://www.elnorte.ec/

J'obtiens le contenu avec la bibliothèque de requêtes:

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

Si je fais une impression de la variable de contenu à ce stade, tous les caractères spéciaux espagnols semblent bien fonctionner. Cependant, une fois que j'essaie d'alimenter la variable de contenu vers BeautifulSoup, tout est gâché:

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">
...

C'est apparemment brouiller tous les caractères spéciaux espagnols (accents et ainsi de suite). J'ai essayé de faire content.decode ('utf-8'), content.decode ('latin-1'), j'ai également essayé de jouer avec le paramètre fromEncoding à BeautifulSoup, en le définissant sur fromEncoding = 'utf-8' et fromEncoding = 'latin-1', mais toujours pas de dés.

Tout pointeur serait très apprécié.

23
David

pourriez-vous essayer:

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

print x.prettify('latin-1')

J'obtiens la sortie correcte. Oh, dans ce cas particulier, vous pouvez également x.__str__(encoding='latin1').

Je suppose que c'est parce que le contenu est en ISO-8859-1 (5) et que le type de contenu meta http-equiv dit incorrectement "UTF-8".

Pourriez-vous confirmer?

18
Gaikokujin Kun

Dans votre cas, cette page contient des données utf-8 erronées qui confond BeautifulSoup et fait penser que votre page utilise Windows-1252, vous pouvez faire cette astuce:

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

ce faisant, vous supprimerez tous les mauvais symboles de la page source et BeautifulSoup devinera l'encodage correctement.

Vous pouvez remplacer "ignorer" par "remplacer" et vérifier le texte pour "?" symboles pour voir ce qui a été jeté.

En fait, c'est une tâche très difficile d'écrire un robot qui peut deviner l'encodage des pages à chaque fois avec 100% de chance (les navigateurs sont très bons dans ce domaine de nos jours), vous pouvez utiliser des modules comme 'chardet' mais, par exemple, dans votre cas, il devinera l'encodage comme ISO-8859-2, ce qui n'est pas correct non plus.

Si vous avez vraiment besoin d'obtenir un encodage pour n'importe quelle page que l'utilisateur peut éventuellement fournir - vous devez soit créer une fonction de détection à plusieurs niveaux (essayez utf-8, essayez latin1, essayez etc ...) (comme nous l'avons fait dans notre projet ) ou utilisez du code de détection de Firefox ou de chrome comme module C.

23
Riz

Vous pouvez essayer ceci, qui fonctionne pour chaque encodage

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

Je suggérerais d'adopter une approche infaillible plus méthodique.

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

Vous pouvez raisonner, peu importe ce que vous lancez, cela enverra toujours un unicode valide à bs.

Par conséquent, votre arbre analysé se comportera beaucoup mieux et n'échouera pas de façon plus récente et plus intéressante chaque fois que vous aurez de nouvelles données.

Les essais et les erreurs ne fonctionnent pas dans le code - Il y a tout simplement trop de combinaisons :-)

2
vpathak

La première réponse est juste, ces fonctions sont parfois efficaces.

    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
2
Tabares