Question

Problème

Lors du scraping d'écran d'une page Web à l'aide de Python, il faut connaître le codage des caractères de la page. Si vous obtenez un mauvais codage des caractères, votre sortie sera gâchée.

Les gens utilisent généralement une technique rudimentaire pour détecter le codage.Soit ils utilisent le jeu de caractères de l'en-tête, soit le jeu de caractères défini dans la balise méta, soit ils utilisent un détecteur de codage (qui ne se soucie pas des balises méta ou des en-têtes).En utilisant une seule de ces techniques, vous n’obtiendrez parfois pas le même résultat que dans un navigateur.

Les navigateurs procèdent de cette façon :

  • Les balises méta sont toujours prioritaires (ou définition XML)
  • L'encodage défini dans l'en-tête est utilisé lorsqu'aucun jeu de caractères n'est défini dans une balise méta
  • Si l’encodage n’est pas défini du tout, il est temps de procéder à la détection de l’encodage.

(Bien...du moins, je pense que c'est ainsi que procèdent la plupart des navigateurs.La documentation est vraiment rare.)

Ce que je recherche, c'est une bibliothèque capable de décider du jeu de caractères d'une page comme le ferait un navigateur. Je suis sûr que je ne suis pas le premier à avoir besoin d'une solution appropriée à ce problème.

Solution (Je ne l'ai pas encore essayé...)

Selon Documentation de Beautiful Soup.

Beautiful Soup essaie les encodages suivants, par ordre de priorité, pour transformer votre document en Unicode :

  • Un encodage que vous transmettez en tant que fromEncoding à la soupe constructeur.
  • Un encodage découvert dans le document lui-même :par exemple, dans une déclaration XML ou (pour les documents HTML) une balise META http-equiv.Si Beautiful Soup trouve ce type d'encodage dans le document, il analyse à nouveau le document depuis le début et essaie le nouvel encodage.La seule exception est si vous avez explicitement spécifié un encodage et que cet encodage a réellement fonctionné :alors il ignorera tout encodage trouvé dans le document.
  • Un encodage reniflé en regardant les premiers octets du fichier.Si un encodage est détecté À ce stade, il s’agira de l’un des Encodages UTF-*, EBCDIC ou ASCII.
  • Un encoding reniflé par la bibliothèque CARCARD, si vous l'avez installé.
  • UTF-8
  • Windows-1252
Était-ce utile?

La solution

j'utiliserais html5lib pour ça.

Autres conseils

Lorsque vous téléchargez un fichier avec urllib ou urllib2, vous pouvez savoir si un en-tête charset a été transmis :

fp = urllib2.urlopen(request)
charset = fp.headers.getparam('charset')

Vous pouvez utiliser BeautifulSoup pour localiser un élément méta dans le HTML :

soup = BeatifulSoup.BeautifulSoup(data)
meta = soup.findAll('meta', {'http-equiv':lambda v:v.lower()=='content-type'})

Si aucun des deux n'est disponible, les navigateurs reviennent généralement à la configuration utilisateur, combinée à la détection automatique.Comme le propose Rajax, vous pouvez utiliser le module chardet.Si une configuration utilisateur est disponible vous indiquant que la page doit être en chinois (par exemple), vous pourrez peut-être faire mieux.

Utilisez le Détecteur d'encodage universel:

>>> import chardet
>>> chardet.detect(urlread("http://google.cn/"))
{'encoding': 'GB2312', 'confidence': 0.99}

L'autre option serait d'utiliser simplement wget :

  import os
  h = os.popen('wget -q -O foo1.txt http://foo.html')
  h.close()
  s = open('foo1.txt').read()

Il semble que vous ayez besoin d'un hybride des réponses présentées :

  1. Récupérer la page en utilisant urllib
  2. Trouver <meta> tags en utilisant une belle soupe ou une autre méthode
  3. Si aucune balise méta n'existe, vérifiez les en-têtes renvoyés par urllib
  4. Si cela ne vous donne toujours pas de réponse, utilisez le détecteur d'encodage universel.

Honnêtement, je ne crois pas que vous trouverez quelque chose de mieux que ça.

En fait, si vous lisez plus en détail la FAQ à laquelle vous avez lié dans les commentaires sur l'autre réponse, c'est ce que préconise l'auteur de la bibliothèque de détecteurs.

Si vous en croyez la FAQ, c'est ce que font les navigateurs (comme demandé dans votre question initiale) car le détecteur est un portage du code de détection de Firefox.

Scrapy télécharge une page et détecte un encodage correct pour celle-ci, contrairement à request.get(url).text ou urlopen.Pour ce faire, il essaie de suivre des règles similaires à celles d'un navigateur - c'est le mieux que l'on puisse faire, car les propriétaires de sites Web sont incités à faire fonctionner leurs sites Web dans un navigateur.Scrapy doit prendre les en-têtes HTTP, <meta> balises, marques de nomenclature et différences dans les noms d'encodage dans le compte.

La supposition basée sur le contenu (chardet, UnicodeDammit) en elle-même n'est pas une solution correcte, car elle peut échouer ;il ne doit être utilisé qu'en dernier recours lorsque les en-têtes ou <meta> ou les marques de nomenclature ne sont pas disponibles ou ne fournissent aucune information.

Vous n'avez pas besoin d'utiliser Scrapy pour obtenir ses fonctions de détection d'encodage ;ils sont publiés (entre autres éléments) dans une bibliothèque distincte appelée w3lib : https://github.com/scrapy/w3lib.

Pour obtenir l'encodage de la page et l'utilisation du corps Unicode w3lib.encoding.html_to_unicode fonction, avec une solution de secours basée sur le contenu :

import chardet
from w3lib.encoding import html_to_unicode

def _guess_encoding(data):
    return chardet.detect(data).get('encoding')

detected_encoding, html_content_unicode = html_to_unicode(
    content_type_header,
    html_content_bytes,
    default_encoding='utf8', 
    auto_detect_fun=_guess_encoding,
)

au lieu d'essayer d'obtenir une page puis de déterminer le jeu de caractères que le navigateur utiliserait, pourquoi ne pas simplement utiliser un navigateur pour récupérer la page et vérifier quel jeu de caractères il utilise.

from win32com.client import DispatchWithEvents
import threading


stopEvent=threading.Event()

class EventHandler(object):
    def OnDownloadBegin(self):
        pass

def waitUntilReady(ie):
    """
    copypasted from
    http://mail.python.org/pipermail/python-win32/2004-June/002040.html
    """
    if ie.ReadyState!=4:
        while 1:
            print "waiting"
            pythoncom.PumpWaitingMessages()
            stopEvent.wait(.2)
            if stopEvent.isSet() or ie.ReadyState==4:
                stopEvent.clear()
                break;

ie = DispatchWithEvents("InternetExplorer.Application", EventHandler)
ie.Visible = 0
ie.Navigate('http://kskky.info')
waitUntilReady(ie)
d = ie.Document
print d.CharSet

BeautifulSoup dose ceci avec UnicodeDammit : Unicode, bon sang

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top