Convertir Unicode en ASCII sans erreurs en Python

mon code efface une page web, puis la convertit en Unicode.

html = urllib.urlopen(link).read()
html.encode("utf8","ignore")
self.response.out.write(html)

mais je reçois un UnicodeDecodeError :


Traceback (most recent call last):
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/webapp/__init__.py", line 507, in __call__
    handler.get(*groups)
  File "/Users/greg/clounce/main.py", line 55, in get
    html.encode("utf8","ignore")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2818: ordinal not in range(128)

je suppose que cela signifie que le HTML contient une tentative mal formée à Unicode quelque part. puis-je simplement laisser tomber les octets de code qui causent le problème au lieu d'obtenir une erreur?

155
demandé sur Martin Thoma 2010-03-02 20:52:42

11 réponses

2018 Mise à jour:

depuis février 2018, en utilisant des compressions comme gzip est devenu très populaire (environ 73% de tous les sites l'utilisent, y compris les grands sites comme Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow et Stack Exchange Network sites).

Si vous faites un décodage simple comme dans la réponse d'origine avec une réponse gzippée, vous obtiendrez une erreur comme ou similaire à ceci:

UnicodeDecodeError: 'utf8' codec ne peut pas décoder les octets 0x8b en position 1: inattendu octet de code

pour décoder une réponse gzpippée, vous devez ajouter les modules suivants (en Python 3):

import gzip
import io

Note: en Python 2 vous utiliseriez StringIO au lieu de io

"

, Alors vous pouvez analyser le contenu comme ceci:

response = urlopen("https://example.com/gzipped-ressource")
buffer = io.BytesIO(response.read()) # Use StringIO.StringIO(response.read()) in Python 2
gzipped_file = gzip.GzipFile(fileobj=buffer)
decoded = gzipped_file.read()
content = decoded.decode("utf-8") # Replace utf-8 with the source encoding of your requested resource

ce code lit la réponse et place les octets dans un tampon. Le module gzip lit alors le tampon en utilisant la fonction GZipFile . Après cela, le fichier gzippé peut être lu en octets à nouveau et décodé en texte normalement lisible à la fin.

réponse originale de 2010:

Pouvons-nous obtenir la valeur réelle utilisée pour link ?

en outre, nous avons habituellement rencontrer ce problème lorsque nous essayons de .encode() déjà codé chaîne d'octets. Donc vous pourriez essayer de le décoder d'abord comme dans

html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")

comme exemple:

html = '\xa0'
encoded_str = html.encode("utf8")

échoue avec

UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)

alors que:

html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")

succède sans erreur. Notez que "windows-1252" est quelque chose que j'ai utilisé comme un exemple . J'ai eu ça de chardet et il avait 0,5 confiance qu'il est juste! (Eh bien, comme donné avec une chaîne de 1 caractère-longueur, ce que vous attendez) vous devriez changer cela à l'encodage de la chaîne byte retourné de .urlopen().read() à ce qui s'applique au contenu que vous avez récupéré.

un autre problème que je vois, c'est que la méthode .encode() renvoie la chaîne modifiée et ne modifie pas la source en place. Donc c'est un peu inutile d'avoir self.response.out.write(html) comme html n'est pas la chaîne codée du html.encoder (si c'est ce que vous vouliez à l'origine).

comme Ignacio l'a suggéré, vérifiez sur la page Web source l'encodage réel de la chaîne retournée de read() . C'est soit dans L'une des balises Meta ou dans L'en-tête ContentType de la réponse. Utilisez ce paramètre comme paramètre pour .decode() .

notez cependant qu'il ne faut pas croire que les autres développeurs sont assez responsable pour s'assurer l'en-tête et/ou méta-jeu de caractères déclarations correspondre au contenu réel. (Qui est une PITA, Ouais, je devrais savoir, je était l'un de ceux avant).

93
répondu Vin-G 2018-07-24 17:25:50
>>> u'aあä'.encode('ascii', 'ignore')
'a'

EDIT:

décodez la chaîne que vous récupérez, en utilisant soit le jeu de caractères dans la balise appropriée meta dans la réponse ou dans l'en-tête Content-Type , puis encodez.

La méthode encode() accepte d'autres valeurs comme "ignorer". Par exemple: "remplacer", "xmlcharrefplace", "backslashreplace". Voir https://docs.python.org/3/library/stdtypes.html#str.encode

189
répondu Ignacio Vazquez-Abrams 2017-07-28 08:31:13

comme extension de la réponse D'Ignacio Vazquez-Abrams

>>> u'aあä'.encode('ascii', 'ignore')
'a'

il est parfois souhaitable de supprimer les accents des caractères et d'Imprimer la forme de base. Cela peut être accompli avec

>>> import unicodedata
>>> unicodedata.normalize('NFKD', u'aあä').encode('ascii', 'ignore')
'aa'

vous pouvez également vouloir traduire d'autres caractères (tels que la ponctuation) à leurs équivalents les plus proches, par exemple le caractère unicode droit de guillemet ne se convertit pas à une APOSTROPHE ascii lors de l'encodage.

>>> print u'\u2019'
’
>>> unicodedata.name(u'\u2019')
'RIGHT SINGLE QUOTATION MARK'
>>> u'\u2019'.encode('ascii', 'ignore')
''
# Note we get an empty string back
>>> u'\u2019'.replace(u'\u2019', u'\'').encode('ascii', 'ignore')
"'"

bien qu'il existe des moyens plus efficaces pour y parvenir. Voir cette question pour plus de détails Où est la base de données "best ASCII for this Unicode" de Python?

106
répondu Peter Gibson 2017-05-23 12:10:11

Use unidecode - il convertit même des caractères bizarres en ascii instantanément, et même convertit chinois en ASCII phonétique.

$ pip install unidecode

puis:

>>> from unidecode import unidecode
>>> unidecode(u'北京')
'Bei Jing'
>>> unidecode(u'Škoda')
'Skoda'
76
répondu Nimo 2016-08-27 18:26:09

j'utilise cette fonction d'aide dans tous mes projets. S'il ne peut pas convertir l'unicode, il l'ignore. C'est relié à une bibliothèque django, mais avec un peu de recherche, vous pourriez La contourner.

from django.utils import encoding

def convert_unicode_to_string(x):
    """
    >>> convert_unicode_to_string(u'ni\xf1era')
    'niera'
    """
    return encoding.smart_str(x, encoding='ascii', errors='ignore')

Je ne reçois plus d'erreurs unicode après avoir utilisé ceci.

24
répondu Gattster 2010-03-03 00:02:07

pour les consoles cassées comme cmd.exe et sortie HTML, vous pouvez toujours utiliser:

my_unicode_string.encode('ascii','xmlcharrefreplace')

cela préservera tous les caractères non-ascii tout en les rendant imprimables en ASCII pur et en HTML.

AVERTISSEMENT : Si vous utilisez cette option dans le code de production pour éviter les erreurs, alors il est probablement quelque chose de mal dans votre code . Le seul cas d'utilisation Valable est l'impression à une console non unicode ou à une conversion facile en entités HTML dans un contexte HTML.

et enfin, si vous êtes sur windows et utilisez cmd.exe alors vous pouvez taper chcp 65001 pour activer la sortie utf-8 (fonctionne avec la police de la Console Lucida). Vous pourriez avoir besoin d'ajouter myUnicodeString.encode('utf8') .

8
répondu ccpizza 2016-07-22 14:20:11

vous avez écrit "" je suppose que cela signifie que le HTML contient une tentative mal formée à unicode quelque part."""

on ne s'attend pas à ce que le HTML contienne une sorte de" tentative d'unicode", bien formée ou non. Il doit nécessairement contenir des caractères Unicode encodés dans un encodage, qui est généralement fourni à l'avance ... rechercher pour "jeu de caractères".

vous semblez supposer que le jeu de caractères est UTF-8 ... sur quels motifs? Le byte "\xA0" qui est affiché dans votre message d'erreur indique que vous pouvez avoir un jeu de caractères à un seul octet, par exemple cp1252.

si vous ne pouvez pas obtenir tout le sens de la déclaration au début du HTML, essayez d'utiliser chardet pour trouver ce qu'est l'encodage probable.

Pourquoi avez-vous étiqueté votre question avec "regex"?

Update après avoir remplacé toute votre question par une non-question:

html = urllib.urlopen(link).read()
# html refers to a str object. To get unicode, you need to find out
# how it is encoded, and decode it.

html.encode("utf8","ignore")
# problem 1: will fail because html is a str object;
# encode works on unicode objects so Python tries to decode it using 
# 'ascii' and fails
# problem 2: even if it worked, the result will be ignored; it doesn't 
# update html in situ, it returns a function result.
# problem 3: "ignore" with UTF-n: any valid unicode object 
# should be encodable in UTF-n; error implies end of the world,
# don't try to ignore it. Don't just whack in "ignore" willy-nilly,
# put it in only with a comment explaining your very cogent reasons for doing so.
# "ignore" with most other encodings: error implies that you are mistaken
# in your choice of encoding -- same advice as for UTF-n :-)
# "ignore" with decode latin1 aka iso-8859-1: error implies end of the world.
# Irrespective of error or not, you are probably mistaken
# (needing e.g. cp1252 or even cp850 instead) ;-)
5
répondu John Machin 2010-03-03 03:46:55

si vous avez une chaîne de caractères line , vous pouvez utiliser la méthode .encode([encoding], [errors='strict']) pour les chaînes de caractères afin de convertir les types d'encodage.

line = 'my big string'

line.encode('ascii', 'ignore')

pour plus d'informations sur la manipulation D'ASCII et d'unicode en Python, c'est un site très utile: https://docs.python.org/2/howto/unicode.html

4
répondu Jama22 2015-03-10 07:34:48

je pense que la réponse est là, mais seulement dans des morceaux, ce qui rend difficile de résoudre rapidement le problème comme

UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2818: ordinal not in range(128)

prenons un exemple, supposons que j'ai un fichier qui contient des données sous la forme suivante (contenant des caractères ascii et non-ascii )

1/10/17, 21 : 36-Land: Welcome ï ¿½

et nous voulons ignorer et préserver uniquement les caractères ascii.

ce code fera:

import unicodedata
fp  = open(<FILENAME>)
for line in fp:
    rline = line.strip()
    rline = unicode(rline, "utf-8")
    rline = unicodedata.normalize('NFKD', rline).encode('ascii','ignore')
    if len(rline) != 0:
        print rline

et le type (rline) vous donnera

>type(rline) 
<type 'str'>
3
répondu Somum 2017-01-15 14:41:03
unicodestring = '\xa0'

decoded_str = unicodestring.decode("windows-1252")
encoded_str = decoded_str.encode('ascii', 'ignore')

Fonctionne pour moi

1
répondu HimalayanCoder 2014-09-05 08:00:29

ressemble à python 2.x. Python 2.x par défaut à ascii et il ne connaît pas Unicode. Donc l'exception.

il suffit de coller la ligne ci-dessous après shebang, il fonctionnera

# -*- coding: utf-8 -*-
-4
répondu Haroon Rashedu 2016-05-03 20:04:28