Problèmes d'encodage Python et BeautifulSoup
j'écris un crawler avec Python en utilisant BeautifulSoup, et tout se passait à merveille jusqu'à ce que je tombe sur ce site:
j'obtiens le contenu avec la bibliothèque des requêtes:
r = requests.get('http://www.elnorte.ec/')
content = r.content
si je fais une impression de la variable de contenu à ce point, tous les caractères spéciaux espagnols semblent fonctionner très bien. Cependant, une fois que j'ai essayé d'alimenter la variable de contenu à BeautifulSoup tout se foiré:
soup = BeautifulSoup(content)
print(soup)
...
<a class="blogCalendarToday" href="/component/blog_calendar/?year=2011&month=08&day=27&modid=203" title="1009 artÃculos en este dÃa">
...
il est apparemment en train de brouiller tous les caractères spéciaux espagnols (accents et autres). J'ai essayé de faire du contenu.decode('utf-8'), le contenu.decode ('latin-1'), a également essayé de jouer avec le paramètre fromEncoding pour embellir le morceau, le mettant à fromEncoding=' utf-8'et fromEncoding=' latin-1', mais toujours pas de dés.
tout pointeur serait très apprécié.
5 réponses
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 bonne sortie.
OH, dans ce cas particulier vous pouvez aussi x.__str__(encoding='latin1')
.
je suppose que c'est parce que le contenu est dans ISO-8859-1(5) et le meta http-equiv content-type dit incorrectement"UTF-8".
pouvez-vous confirmer?
dans votre cas, cette page a des données utf-8 erronées qui confond BeautifulSoup et lui fait penser que votre page utilise windows-1252, vous pouvez faire ce tour:
soup = BeautifulSoup.BeautifulSoup(content.decode('utf-8','ignore'))
en faisant cela, vous écarterez tous les symboles erronés de la source de la page 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 un tâche très difficile d'écrire crawler qui peut deviner encodage page à chaque fois avec 100% chance (les navigateurs sont très bons à ce jour), vous pouvez utiliser des modules comme "chardet", mais, par exemple, dans votre cas, il va deviner encodage ISO-8859-2, ce qui n'est pas correct trop.
si vous avez vraiment besoin d'être en mesure d'obtenir l'encodage pour n'importe quel utilisateur de page peut éventuellement fournir - vous devriez soit construire un multi-niveau(essayer utf-8, essayer latin1, essayer etc...) fonction de détection(comme nous l'avons fait dans notre projet) ou utilisez un code de détection de firefox ou de chrome comme module C.
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
je suggère une approche 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 à ceci, il enverra toujours unicode valide à bs.
comme résultat votre arbre analysé se comportera beaucoup mieux et ne manquera pas de nouvelles façons plus intéressantes chaque fois que vous avez de nouvelles données.
Essai et Erreur ne fonctionne pas dans le Code - Il y a trop de combinaisons :-)
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)