web-dev-qa-db-fra.com

Python corrige le codage du site Web

J'essaie de charger une page HTML et de générer le texte, même si je reçois correctement la page Web, BeautifulSoup détruit en quelque sorte l'encodage. 

La source:  

# -*- coding: utf-8 -*-
import requests
from BeautifulSoup import BeautifulSoup

url = "http://www.columbia.edu/~fdc/utf8/"
r = requests.get(url)

encodedText = r.text.encode("utf-8")
soup = BeautifulSoup(encodedText)
text =  str(soup.findAll(text=True))
print text.decode("utf-8")

Extrait de sortie:

...Odenw\xc3\xa4lderisch...

cela devrait être Odenwälderisch

6
user1767754

Vous faites deux erreurs. vous manipulez mal l'encodage et vous traitez une liste de résultats comme quelque chose qui peut être converti en toute sécurité en chaîne sans perte d'informations.

Tout d'abord, n'utilisez pas response.text! Ici, ce n’est pas BeautifulSoup qui est en cause, vous recodez un Mojibake . La bibliothèque requests utilise par défaut le codage Latin-1 pour les types de contenu text/* lorsque le serveur ne spécifie pas explicitement un codage, car la norme HTTP le définit comme valeur par défaut.

Voir la section Encodage de la Avancée documentation :

La seule fois où Requests ne le fera pas, c'est si aucun jeu de caractères explicite n'est présent dans les en-têtes HTTP et l'en-tête Content-Type contient text. Dans cette situation, RFC 2616 spécifie que le jeu de caractères par défaut doit être ISO-8859-1. Requests suit la spécification dans ce cas. Si vous avez besoin d'un codage différent, vous pouvez définir manuellement la propriété Response.encoding ou utiliser le Response.content brut.

Gras accent mien.

Transmettez les données brutes response.content à la place:

soup = BeautifulSoup(r.content)

Je vois que vous utilisez BeautifulSoup 3. Vous voulez vraiment passer à BeautifulSoup 4 à la place; la version 3 a été arrêtée en 2012 et contient plusieurs bogues. Installez le beautifulsoup4 projet et utilisez from bs4 import BeautifulSoup.

BeautifulSoup 4 fait généralement un excellent travail en trouvant le bon encodage à utiliser lors de l'analyse, à partir d'une balise HTML <meta> ou d'une analyse statistique des octets fournis. Si le serveur fournit un jeu de caractères, vous pouvez toujours le transmettre à BeautifulSoup à partir de la réponse, mais effectuez d'abord un test si requests a utilisé un paramètre par défaut:

encoding = r.encoding if 'charset' in r.headers.get('content-type', '').lower() else None
soup = BeautifulSoup(r.content, from_encoding=encoding)

Last but not least, avec BeautifulSoup 4, vous pouvez extraire tout le texte d'une page à l'aide de soup.get_text():

text = soup.get_text()
print text

Au lieu de cela, vous convertissez un liste de résultats} (la valeur de retour de soup.findAll()) en chaîne. Cela ne peut jamais fonctionner car les conteneurs en Python utilisent repr() sur chaque élément de la liste pour produire un chaîne de débogage, et pour les chaînes, cela signifie que vous obtenez des séquences d'échappement pour tout ce qui n'est pas un caractère imprimable ASCII. .

24
Martijn Pieters

Ce n'est pas la faute de BeautifulSoup. Vous pouvez le voir en imprimant encodedText, avant même d'utiliser BeautifulSoup: les caractères non-ASCII sont déjà du charabia.

Le problème ici est que vous mélangez des octets et des caractères. Pour un bon aperçu de la différence, lisez l'un des articles de Joel , mais Gist est que les octets sont, enfin, des octets (groupes de 8 bits sans autre signification), alors que les caractères sont ce qui constitue les chaînes du texte. L'encodage transforme les caractères en octets et le décodage transforme les octets en caractères.

Un examen de la documentation requests montre que r.text est composé de caractères, pas d'octets. Vous ne devriez pas l'encoder. Si vous essayez de le faire, vous ferez une chaîne d'octets et si vous essayez de traiter cela comme des caractères, de mauvaises choses se produiront.

Il y a deux façons de contourner cela:

  1. Utilisez les octets bruts non décodés, qui sont stockés dans r.content, comme suggéré par Martijn . Ensuite, vous pouvez les décoder vous-même pour les transformer en personnages.
  2. Laissez requests faire le décodage, mais assurez-vous qu'il utilise le bon codec. Puisque vous savez que c'est UTF-8 dans ce cas, vous pouvez définir r.encoding = 'utf-8'. Si vous faites cela avant vous accédez à r.text, puis lorsque vous accédez à r.text, il sera correctement décodé et vous obtiendrez une chaîne de caractères. Vous n'avez pas besoin de jouer avec les encodages de caractères.

Incidemment, Python 3 facilite légèrement la gestion de la différence entre les chaînes de caractères et les chaînes d'octets, car vous devez utiliser différents types d'objets pour les représenter.

4
David Z

Il y a quelques erreurs dans votre code:

  1. Tout d’abord, votre tentative de réencodage du texte n’est pas nécessaire. Les demandes peuvent vous donner l’encodage natif de la page et BeautifulSoup peut prendre cette information et se décoder elle-même:

    # -*- coding: utf-8 -*-
    import requests
    from BeautifulSoup import BeautifulSoup
    
    url = "http://www.columbia.edu/~fdc/utf8/"
    r = requests.get(url)
    
    soup = BeautifulSoup(r.text, "html5lib")
    
  2. Deuxièmement, vous avez un problème d'encodage. Vous essayez probablement de visualiser les résultats sur le terminal. Vous obtiendrez la représentation unicode des caractères du texte pour chaque caractère ne faisant pas partie de l'ensemble ASCII. Vous pouvez vérifier les résultats comme ceci:

    res = [item.encode("ascii","ignore") for item in soup.find_all(text=True)]
    
1
lesingerouge