IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

UnicodeEncodeError: 'ascii' codec can't encode character


Sujet :

Python

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 4
    Par défaut UnicodeEncodeError: 'ascii' codec can't encode character
    Bonsoir,

    Voici mon problème :

    je débute avec Python (python 2.5.2 final, Ubuntu 8.04, éditeur DrPython 165),

    je souhaite récupérer une page d'un site web pour la parser grâce à BeautifulSoup, pour me faire la main j'applique le code-source donné en exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    from BeautifulSoup import BeautifulSoup
    import re
    import urllib
     
    page=urllib.urlopen('http://lapageweb.com')
     
    doc=page.read()
     
    soup = BeautifulSoup(''.join(doc))
     
    for p in soup.findAll(['p', 'div']):
        soup1 = BeautifulSoup(p.renderContents())
        res = ''
        for b in soup1.findAll(text=True):
            res += b
        print res
    A l'exécution, j'obtiens le message suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Traceback (most recent call last):
      File "/home/webtotoche/DevPython/Apprentissage/test.py", line 19, in <module>
        print res
    UnicodeEncodeError: 'ascii' codec can't encode character u'\xb7' in position 590: ordinal not in range(128)
    Je crois avoir compris qu'on a affaire à un charset ou à un mode d'encodage de fichier mais... je suis incapable d'aller plus loin...

    Malgré mes recherches (et notamment ces deux pages http://sebsauvage.net/python/charsets_et_encoding.html et http://python.jpvweb.com/mesrecettespython/encodage, cette dernière prometteuse mais qui s'arrête en chemin :-( ), malgré des tentatives de conversion, je n'ai pas trouvé comment résoudre mon problème.

    J'ignore comment :

    • déterminer l'encodage utilisé dans la page web que je lis
    • déterminer le charset utilisé
    • déterminer l'encodage utilisé par ma plateforme (l'instruction print sys.stdout.encoding me réponde None)
    • convertir d'un encodage à une autre : si au lieu de faire doc = page.read() je fais doc = page.read().decode('utf_8'), j'obtiens le même message d'erreur - à une variante près : la position, 594 au lieu de 590.


    Bref ! Je tourne en rond, j'aimerais en finir une bonne fois pour toutes avec ces questions d'encodage de caractères qui me (nous ? ) pourrissent la vie...

    Si vous avez des pistes...

    Un grand merci ! :-)

  2. #2
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Eh, oui, mon tuto avait bien commencé, mais je suis parti sur d'autres projets plus pressés. Désolé.

    Je ne connais pas BeautifulSoup, mais je viens de l'installer, et une copie conforme à ton code s'exécute sans pb avec le site 'http://www.google.fr' qui contient tout de même quelques caractères accentués.

    Sinon, quelques éléments pour répondre à tes dernières questions:

    Dans une page web, il y a une mention dans l'entête qui donne l'encodage. Par exemple, sur la page de google:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    content="text/html; charset=UTF-8"
    Il faudrait alors que ton parseur commence par lire l'entête pour tenir compte de l'encodage dans la suite de la page.

    sys.stdout.encoding devrait donner, non pas l'encodage de la plateforme de développement, mais l'encodage de sa console d'affichage qui reçoit les résultats d'exécution. J'ai déjà eu un "None" dans une précédente version d'Eclipse-Pydev: il faut alors définir dans la configuration ou lire la notice pour connaître la valeur par défaut.

    Quand tu fais "page.read().decode('utf_8')", tu demandes la conversion en unicode interne, d'un texte encodé en utf-8. Si ce texte n'est pas en utf-8, il peut y avoir erreur (selon son contenu).

    En résumé, ton erreur semble être au niveau de l'affichage ("print res"). Essaye de lancer ton code dans une simple console avec "print res.encode(sys.stdout.encoding)" (ce qui suppose que res est en unicode).

    Tyrtamos

  3. #3
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 696
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 696
    Par défaut
    Salut

    Ce n'est hélas pas si simple car dans cette histoire, nous avons 3 problèmes assez différents et des idées reçues qui n'aident pas.

    1. utf-8 est une suite de "bytes" alors Unicode est une suite de CodePoints.
    L'intérêt d'utf-8 est de permettre la sérialisation/désérialisation de n'importe quelle suite de CodePoints Unicode, i.e de passer de bytes à suite de CodePoints.
    2. L'encodage d'un document en utf-8 dépend du type (mime ou média type) du document: un "à" ne sera pas écrit de la même façon dans un document HTML codé en utf-8 ou dans un document XML,
    C'est ce qui correspond à
    Entity Conversion

    When you parse a document, you can convert HTML or XML entity references to the corresponding Unicode characters. This code converts the HTML entity "&eacute;" to the Unicode character LATIN SMALL LETTER E WITH ACUTE, and the numeric entity "e" to the Unicode character LATIN SMALL LETTER E.
    Dans la documentation de Beautiful Soup
    3. Tout cela est bien joli mais si on veut 'afficher', 'imprimer',... il faudra trouver le dessin (glyph) qui correspondant et ce n'est pas parce que c'est Unicode ou utf-8 que çà le fera dans tous les cas.

    Illustrations.
    Lorsqu'on fait une requête HTTP demandant de retourner le contenu du document associée à l'URL qu'on souhaite, on peut espérer que le serveur HTTP nous retournera une page HTML dont le contenu sera codé en latin-1 (c'est le défaut)... ou d'adapter à tout ce qu'il pourra retourner...
    On s'en sort en renseignant les headers Accept, Accept-Encoding dans lequel on pourra dire:
    • Accept: text/HTML, text/XHTML+XML; q=0.9; */*;q=0.1
    • Accept-Encoding: utf-8, ISO-8859-15; q=0.9, *; q=0.1
    Note: il doit y avoir des "" autour de... mais c'est pas important
    Le serveur nous retourne un document dont le contenu est donné par:
    • Content-Type: text/html; charset=ISO-8859-15

    Si on veut que Beautiful Soup ou autre lxml décodent proprement le contenu, i.e. nous permettre d'accéder au texte traduit en CodePoints Unicode lorsqu'on accède aux tags du document , il faudra utiliser le parseur correspondant au type du document et lui donner le charset 'déclaré'.
    Si le document contient une autre indication sur le charset, elle sera prise en compte.
    S'il n'y a pas d'indication, Beautiful Soup utilise une heuristique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Beautiful Soup tries the following encodings, in order of priority, to turn your document into Unicode:
    
        * An encoding you pass in as the fromEncoding argument to the soup constructor.
        * An encoding discovered in the document itself: for instance, in an XML declaration or (for HTML documents) an http-equiv META tag. If Beautiful Soup finds this kind of encoding within the document, it parses the document again from the beginning and gives the new encoding a try. The only exception is if you explicitly specified an encoding, and that encoding actually worked: then it will ignore any encoding it finds in the document.
        * An encoding sniffed by looking at the first few bytes of the file. If an encoding is detected at this stage, it will be one of the UTF-* encodings, EBCDIC, or ASCII.
        * An encoding sniffed by the chardet library, if you have it installed.
        * UTF-8
        * Windows-1252
    Ce qui est important: "Beautiful Soup Gives You Unicode"
    Et en général, tous les outils qui analysent les documents produisent de l'Unicode (et non de l'utf-8).

    Point 3: comment afficher de l'Unicode...
    Le problème que vous évoquez est dans 'TroobleShooting':
    Why can't Beautiful Soup print out the non-ASCII characters I gave it?

    If you're getting errors that say: "'ascii' codec can't encode character 'x' in position y: ordinal not in range(128)", the problem is probably with your Python installation rather than with Beautiful Soup. Try printing out the non-ASCII characters without running them through Beautiful Soup and you should have the same problem. For instance, try running code like this:

    latin1word = 'Sacr\xe9 bleu!'
    unicodeword = unicode(latin1word, 'latin-1')
    print unicodeword

    If this works but Beautiful Soup doesn't, there's probably a bug in Beautiful Soup. However, if this doesn't work, the problem's with your Python setup. Python is playing it safe and not sending non-ASCII characters to your terminal. There are two ways to override this behavior.

    1. The easy way is to remap standard output to a converter that's not afraid to send ISO-Latin-1 or UTF-8 characters to the terminal.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
          import codecs
          import sys
          streamWriter = codecs.lookup('utf-8')[-1]
          sys.stdout = streamWriter(sys.stdout)
    codecs.lookup returns a number of bound methods and other objects related to a codec. The last one is a StreamWriter object capable of wrapping an output stream.
    2. The hard way is to create a sitecustomize.py file in your Python installation which sets the default encoding to ISO-Latin-1 or to UTF-8. Then all your Python programs will use that encoding for standard output, without you having to do something for each program. In my installation, I have a /usr/lib/python/sitecustomize.py which looks like this:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
          import sys
          sys.setdefaultencoding("utf-8")
    Tout cela peut paraître "compliqué" mais en fait nous avons la chance d'avoir des tas de bibliothèques qui nous mâchent le travail pour peu que nous sachions les utiliser.

    Savoir les utiliser avec "pertinence" demande pas quelques efforts pour en comprendre les divers aspects et les implications...
    Espérer que l'esperanto nommé "UTF-8" se diffusera vite pour faire l'impasse est une gageure...
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  4. #4
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2007
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 4
    Par défaut
    Bonjour à tous,

    Merci encore pour vos réponses et désolé pour le retard de la mienne : entre les ratés de mon FAI et mes horaires de boulot, je n'ai pas souvent eu l'occasion de me connecter.

    Alors : ça marche ! :-)

    Au début du programme exemple, j'ai rajouté les quatre lignes indiquées par wiztricks :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    import codecs
    import sys
    streamWriter = codecs.lookup('utf-8')[-1]
    sys.stdout = streamWriter(sys.stdout)
    et les données s'affichent correctement, je vais pouvoir avancer sur la suite de mon programme :-)


    Le truc que j'ignore encore à ce stade de mes connaissances c'est :

    - faudra-t-il systématiquement inclure ces trois lignes ?

    - ou peut-on passer par un "fichier de config" qui sera automatiquement pris en compte par python, comme dans le sitecustomize.py ?


    Une remarque : j'ai essayé également les deux lignes de code indiquées ensuite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    import sys
    sys.setdefaultencoding("utf-8")
    et j'ai obtenu ce message :

    AttributeError: 'module' object has no attribute 'setdefaultencoding'
    Et après vérif print dir(sys) n'affiche effectivement pas setdefaultencoding. Bon, je suis trop novice pour comprendre pourquoi, une question de version de mon python ?


    Pour la petite histoire, j'avais pourtant bien lu la page de BeautifulSoup et la solution indiquée ici par wiztricks... Probablement une question de "yeux pas en face des trous",ou de "mauvaise réceptivité à l'anglais", ou de "confusion entre charsets, encodage, et pourquoi ça ne marche pas alors que tout mon Ubuntu est en UTF-8"... Allez savoir... :-)


    A tyrtamos : si tu as l'occasion de terminer ton tuto... Je suis sûr que tu feras des heureux :-)

    Merci encore à vous deux et... peut-être rendez-vous bientôt pour d'autres questions de novice ;-)

  5. #5
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 696
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 696
    Par défaut
    Salut

    sys.setdefaultencoding() disparait de sys au démarrage car ceci est dans site.py
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        # Remove sys.setdefaultencoding() so that users cannot change the
        # encoding after initialization.  The test for presence is needed when
        # this module is run as a script, because this code is executed twice.
        if hasattr(sys, "setdefaultencoding"):
            del sys.setdefaultencoding
    Ce qui n'empêche pas de mettre:
    import sys
    sys.setdefaultencoding("utf-8")
    dans un sitecustomize.py qui sera exécuté par site.py, s'il existe, avant que "del sys.setdefaultencoding" n'ait été exécuté.

    Puisque c'est "site.py" qui positionne l'encoding de Python... on peut répondre à:
    Probablement une question de "yeux pas en face des trous",ou de "mauvaise réceptivité à l'anglais", ou de "confusion entre charsets, encodage, et pourquoi ça ne marche pas alors que tout mon Ubuntu est en UTF-8"... Allez savoir... :-)
    Il (Python) fait ce qu'il y "par défaut" dans site.py - chez moi c'est 'ascii'...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def setencoding():
        """Set the string encoding used by the Unicode implementation.  The
        default is 'ascii', but if you're willing to experiment, you can
        change this."""
        encoding = "ascii" # Default value set by _PyUnicode_Init()
        if 0:
            # Enable to support locale aware default string encodings.
            import locale
            loc = locale.getdefaultlocale()
            if loc[1]:
                encoding = loc[1]
        if 0:
            # Enable to switch off string to Unicode coercion and implicit
            # Unicode to string conversion.
            encoding = "undefined"
        if encoding != "ascii":
            # On Non-Unicode builds this will raise an AttributeError...
            sys.setdefaultencoding(encoding) # Needs Python Unicode build !
    Et voilà
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre émérite Avatar de stigma
    Homme Profil pro
    Créateur jeux vidéo
    Inscrit en
    Octobre 2003
    Messages
    1 126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 74
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Créateur jeux vidéo
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 126
    Par défaut
    Depuis le temps que je cherchais une solution, ça marche enfin !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    import codecs
    import sys
    streamWriter = codecs.lookup('utf-8')[-1]
    sys.stdout = streamWriter(sys.stdout)
    Merci

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 7
    Dernier message: 27/04/2014, 23h43
  2. UnicodeEncodeError: 'ascii' codec: problème classique mais…
    Par gvdmoort dans le forum Général Python
    Réponses: 3
    Dernier message: 03/04/2014, 21h10
  3. Réponses: 10
    Dernier message: 26/01/2013, 16h25
  4. UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9'
    Par slaima15 dans le forum Général Python
    Réponses: 4
    Dernier message: 06/06/2011, 23h18
  5. 'ascii' codec can't decode byte
    Par Bibicmoi dans le forum Général Python
    Réponses: 5
    Dernier message: 28/08/2007, 23h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo