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?
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).
>>> 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
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?
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.
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')
.
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) ;-)
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
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'>
unicodestring = '\xa0'
decoded_str = unicodestring.decode("windows-1252")
encoded_str = decoded_str.encode('ascii', 'ignore')
Fonctionne pour moi
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 -*-