Unicode'u Python'da hata olmadan ASCII'ye dönüştürme


178

Kodum sadece bir web sayfasını kazıyarak daha sonra Unicode'a dönüştürür.

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

Ama şunu elde ederim 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)

HTML'nin Unicode'da bir yerde yanlış oluşturulmuş bir deneme içerdiği anlamına geliyor. Bir hata almak yerine soruna neden olan kod baytlarını bırakabilir miyim?


2
Önemli karakterler atılırsa bunun bir hata olduğunu düşünüyorum! (Ayrıca, soru nerede?)
Arafangion

Web sayfasında bir "boşluk yok" ile karşılaşmış gibi görünüyor? öncesinde bir c2bayt olmalı veya muhtemelen bir kod çözme hatası alırsınız: hexutf8.com/?q=C2A0
kavanoz

Yanıtlar:


105

2018 Güncellemesi:

Şubat 2018 itibariyle, gibi sıkıştırmalar kullanmak oldukça popülergzip hale geldi (Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow ve Stack Exchange Network siteleri gibi büyük siteler dahil olmak üzere tüm web sitelerinin yaklaşık% 73'ü bunu kullanıyor). Orijinal cevaptaki gibi basit bir kod çözme işlemi yaparsanız, buna benzer bir hata alırsınız:

UnicodeDecodeError: 'utf8' codec bileşeni, konum 1'deki bayt 0x8b kodunu çözemiyor: beklenmeyen kod baytı

Bir gzpipped yanıtının kodunu çözmek için aşağıdaki modülleri eklemeniz gerekir (Python 3'te):

import gzip
import io

Not: Python 2'de kullanmak StringIOyerineio

Ardından içeriği şu şekilde ayrıştırabilirsiniz:

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

Bu kod yanıtı okur ve baytları arabelleğe yerleştirir. gzipArdından modül kullanarak tampon okur GZipFileişlevi. Bundan sonra, gzip edilmiş dosya tekrar baytlara okunabilir ve sonunda normal olarak okunabilir metne çözülebilir.

2010'dan Orijinal Yanıt:

Kullanılan gerçek değeri alabilir miyiz link?

Ayrıca, .encode()zaten kodlanmış bir bayt dizesini denemeye çalışırken genellikle bu sorunla karşılaşırız . Yani önce olduğu gibi kodunu çözmeyi deneyebilirsiniz

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

Örnek olarak:

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

İle başarısız olur

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

Süre:

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

Hatasız başarılı olur. "Windows-1252" nin örnek olarak kullandığım bir şey olduğunu unutmayın . Bunu pazıdan aldım ve doğru olduğuna dair 0,5 güven vardı! (iyi, 1 karakter uzunluğunda bir dize ile verildiği gibi, ne bekliyorsunuz) Bunu, aldığınız .urlopen().read()içerik için geçerli olana döndürülen bayt dizesinin kodlamasına değiştirmelisiniz .

Başka bir sorun var .encode()dize yöntemi değiştirilmiş dize döndürür ve yerinde kaynak değiştirmez olduğunu görüyorum . Bu yüzden self.response.out.write(html)html, html.encode'dan kodlanmış dize olmadığı için işe yaramaz (eğer başlangıçta amaçladığınız buysa).

Ignacio'nun önerdiği gibi, döndürülen dizenin gerçek kodlaması için kaynak web sayfasını kontrol edin read(). Yanıtta Meta etiketlerinden birinde veya ContentType üstbilgisinde bulunur. Bunu parametre olarak kullanın .decode().

Ancak, diğer geliştiricilerin üstbilgi ve / veya meta karakter kümesi bildirimlerinin gerçek içerikle eşleştiğinden emin olmak için yeterince sorumlu olduğu varsayılmamalıdır. (Bu bir PITA, evet, bilmeliyim, daha önce onlardan biriydim).


1
encoded_str = decoded_str.encode("utf8")
Örneğinizde

1
Python 2.7.15'te denedim ve bu mesajı aldım raise IOError, 'Not a gzipped file'. Yaptığım hata nedir?
Hyun-geun Kim

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

metaYanıtta veya Content-Typebaşlıktaki uygun etiketteki karakter kümesini kullanarak geri aldığınız dizginin kodunu çözün ve sonra kodlayın.

Yöntem encode(encoding, errors), hatalar için özel işleyicileri kabul eder. Varsayılan değerlerin yanı sıra ignore:

>>> u'aあä'.encode('ascii', 'replace')
b'a??'
>>> u'aあä'.encode('ascii', 'xmlcharrefreplace')
b'a&#12354;&#228;'
>>> u'aあä'.encode('ascii', 'backslashreplace')
b'a\\u3042\\xe4'

Bkz. Https://docs.python.org/3/library/stdtypes.html#str.encode


119

Ignacio Vazquez-Abrams'ın cevabının bir uzantısı olarak

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

Bazen karakterlerden aksanları kaldırmak ve temel formu yazdırmak istenebilir. Bu ile yapılabilir

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

Ayrıca diğer karakterleri (noktalama işaretleri gibi) en yakın eşdeğerlerine çevirmek isteyebilirsiniz, örneğin SAĞ TEK TEKLİFİ İŞARETİ unicode karakteri kodlama sırasında bir ascii APOSTROPHE dönüştürülmez.

>>> 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')
"'"

Her ne kadar bunu başarmanın daha etkili yolları olsa da. Daha fazla ayrıntı için bu soruya bakın Python'un "Bu Unicode için en iyi ASCII" veritabanı nerede?


4
Her ikisi de sorulan sorunun ele alınmasında yardımcı olur ve sorulan sorunun altında yatan sorunların ele alınması için pratiktir. Bu, bu tür bir soru için örnek bir cevaptır.
shanusmagnus

96

Unidecode kullanın - garip karakterleri anında ascii'ye , hatta Çince'yi fonetik ascii'ye bile dönüştürür.

$ pip install unidecode

sonra:

>>> from unidecode import unidecode
>>> unidecode(u'北京')
'Bei Jing'
>>> unidecode(u'Škoda')
'Skoda'

3
halle-freakin-lujah - onun zamanı geldi benim için çalışan bir cevap buldum
Aurielle Perlmann

10
Eğlence değeri için yükseltildi. Bunun aksanlı tüm dillerde kelimeleri karıştırdığını unutmayın. Škoda Skoda değil. Skoda büyük olasılıkla yılanbalığı ve hoverkraftlarla kaba bir şey ifade ediyor.
Sylvain

1
Şimdiye kadar günlerdir internet izliyorum .... teşekkür ederim, çok teşekkür ederim
Stephen

23

Bu yardımcı işlevi tüm projelerim boyunca kullanıyorum. Unicode'u dönüştüremezse, yok sayar. Bu bir django kütüphanesine bağlanır, ancak küçük bir araştırma ile onu atlayabilirsiniz.

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')

Bunu kullandıktan sonra artık herhangi bir unicode hatası almıyorum.


10
Bu, sorunu teşhis etmek ve düzeltmek değil, sorunu SÜRÜMEKTİR. "Ayaklarımı kestikten sonra artık mısır ve bunyonlarla ilgili sorunum yok" demek gibi.
John Machin

10
Sorunu bastırdığını kabul ediyorum. Sorunun peşinde olduğu gibi görünüyor. Notuna bakın: "Hata almak yerine soruna neden olan kod baytlarını bırakabilir miyim?"
Gattster

3
bu sadece "some-string" .encode ('ascii', 'ignore') olarak adlandırmakla tamamen aynıdır
Joshua Burns

17
SO hakkında bir soru soran ve tüm bu preachy tepkilerini alan birinin ne kadar yorgun olduğumu size söyleyemem. "Arabam çalışmıyor." "Neden arabanı çalıştırmak istiyorsun? Bunun yerine yürümelisin." Yapma!
shanusmagnus

8
@JohnMachin Kimse umursamıyor. İnsanların RSS beslemelerine ne gibi engelli bok koyduğu umurumda değil, eğer ascii'de olmayan bir karakter ise kesilebilir. Onların sorunu. Ben sadece python aslında boğmak ve onunla başa çıkmak istiyorum, 'görmezden' belirttiğiniz her zaman bana hatalar vermek istiyorum. O boktan kim geldi ?!
user1244215

10

Gibi kırık konsollar cmd.exeve HTML çıktısı için her zaman kullanabilirsiniz:

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

Bu, ASCII olmayan tüm karakterleri korurken saf ASCII ve HTML'de yazdırılabilir hale getirir .

UYARI : Hatalardan kaçınmak için bunu üretim kodunda kullanırsanız, büyük olasılıkla kodunuzda bir sorun var . Bunun için tek geçerli kullanım durumu, unicode olmayan bir konsola yazdırmak veya HTML bağlamında HTML varlıklarına kolay dönüşümdür.

Ve son olarak, eğer pencere üzerindeyseniz ve cmd.exe kullanıyorsanız chcp 65001, utf-8 çıktısını etkinleştirmek için yazabilirsiniz (Lucida Console yazı tipiyle çalışır). Eklemeniz gerekebilir myUnicodeString.encode('utf8').


6

"" "HTML'nin bir yerde unicode'da yanlış oluşturulmuş bir deneme içerdiği anlamına geldiğini varsaydınız." ""

HTML'nin iyi biçimlendirilmiş veya biçimlendirilmemiş herhangi bir "unicode girişimi" içermesi beklenmez. Zorunlu olarak, bazı kodlamalarda kodlanmış Unicode karakterleri içermelidir, bunlar genellikle önden verilir ... "karakter kümesi" arayın.

Karakter setinin UTF-8 olduğunu varsayıyorsunuz ... hangi gerekçeyle? Hata iletinizde gösterilen "\ xA0" baytı, tek baytlık bir karakter kümeniz olabileceğini gösterir, örn. Cp1252.

HTML'nin başlangıcındaki bildirimden bir anlam alamıyorsanız , olası kodlamanın ne olduğunu bulmak için chardet'i kullanmayı deneyin .

Sorunuzu neden "normal ifade" ile etiketlediniz?

Sorunuzun tamamını soru olmayan biriyle değiştirdikten sonra güncelleyin :

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) ;-)

4

Bir dizeniz linevarsa, .encode([encoding], [errors='strict'])kodlama türlerini dönüştürmek için dizeler yöntemini kullanabilirsiniz .

line = 'my big string'

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

Python'da ASCII ve unicode kullanma hakkında daha fazla bilgi için, bu gerçekten yararlı bir sitedir: https://docs.python.org/2/howto/unicode.html


1
Dizede ü gibi ascii olmayan bir karakteriniz olduğunda bu çalışmaz.
sajid

4

Cevabın orada olduğunu düşünüyorum, ancak sadece bitler ve parçalar halinde, bu da sorunu hızlı bir şekilde çözmeyi zorlaştırıyor

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

Bir örnek verelim, aşağıdaki formda bazı veriler içeren bir dosyam olduğunu varsayalım (ascii ve ascii olmayan karakterler içeren)

1/10/17, 21:36 - Arazi: Hoş geldiniz ��

ve sadece ascii karakterleri görmezden gelmek ve korumak istiyoruz.

Bu kod şunları yapacaktır:

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

ve tip (rline) size verecek

>type(rline) 
<type 'str'>

Bu aynı zamanda (standart dışı) "genişletilmiş ascii" vakaları için de geçerlidir
Oliver Zendel

1
unicodestring = '\xa0'

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

Benim için çalışıyor


-5

Görünüşe göre python 2.x kullanıyorsunuz. Python 2.x varsayılan olarak ascii'dir ve Unicode hakkında bir şey bilmez. Dolayısıyla istisna.

Sadece shebang sonra aşağıdaki satırı yapıştırın, işe yarayacak

# -*- coding: utf-8 -*-

codingYorum sihirli derde deva değildir. Hatanın neden oluşturulduğunu bilmeniz gerekir, bu sadece Python kaynağınızda kötü karakterler olduğunda işleri düzeltir. Bu soru için durum böyle görünmüyor.
Mark Ransom
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.