web-dev-qa-db-ja.com

PythonでエラーなしにUnicodeをASCIIに変換

私のコードはWebページを削り取ってから、それをUnicodeに変換します。

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

しかし、私は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がどこかでUnicodeに対して何らかの不正な形式の試みを含むことを意味すると思います。 エラーになるのではなく、問題の原因となっているコードバイトをすべて削除できますか?

163
themirror

2018年更新:

2018年2月の時点で、gzipのような圧縮を使用することは 非常にポピュラーになりました (Google、YouTube、Yahooのような大きなサイトを含むすべてのWebサイトの約73%がそれを使用します、Wikipedia、Reddit、Stack Overflow、およびStack Exchange Networkサイト).
元の答えのようにgzipされた応答で単純なデコードを実行すると、次のようなエラーが発生します。

UnicodeDecodeError: 'utf8'コーデックは位置1のバイト0x8bをデコードできません:予期しないコードバイト

Gzpipされたレスポンスをデコードするためには、次のモジュールを追加する必要があります(Python 3)。

import gzip
import io

注:Python 2ではStringIOの代わりにioを使用します

それからあなたはこのように内容を解析することができます:

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

このコードは応答を読み取り、バイトをバッファーに入れます。 gzipモジュールはGZipFile関数を使ってバッファを読み込みます。その後、gzipされたファイルは再びバイトに読み込まれ、最後に普通に読めるテキストにデコードされます。

2010年からの元の答え:

linkに使用されている実際の値を取得できますか?

さらに、すでにエンコードされたバイト文字列を.encode()しようとしているときに、通常この問題に遭遇します。だからあなたは最初にそれをデコードしようとするかもしれません

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

例として:

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

失敗する

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

ながら:

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

エラーなしで成功します。 "windows-1252"はの例として使ったものです。私はこれを chardet から得ました、そしてそれはそれが正しいという0.5の自信を持っていました!あなたはそれを.urlopen().read()から返されたバイト文字列のエンコーディングに変更しなければなりません。

もう1つ問題があるのは、.encode()文字列メソッドが変更された文字列を返し、元の場所を変更しないことです。 htmlはhtml.encodeからのエンコードされた文字列ではないため、self.response.out.write(html)を持つのは無駄です(もしそれがあなたが当初目指していたものであれば)。

Ignacioが示唆したように、read()から返された文字列の実際のエンコーディングについては、ソースのWebページをチェックしてください。これは、Metaタグのいずれか、またはレスポンスのContentTypeヘッダーにあります。それを.decode()のパラメータとして使います。

ただし、ヘッダーやメタキャラクタセットの宣言が実際の内容と一致することを他の開発者が確認するのに十分な責任を負うとは想定しないでください。 (これはPITAです、ええ、私は知っておくべきです、私は前のもののうちの1つでした)。

99
Vin-G
>>> u'aあä'.encode('ascii', 'ignore')
'a'

編集:

レスポンス内の適切なmetaタグ内またはContent-Typeヘッダー内の文字セットを使用して、戻ってきた文字列をデコードしてからエンコードします。

メソッドencode()は他の値を "ignore"として受け入れます。例えば、 'replace'、 'xmlcharrefreplace'、 'backslashreplace'などです。 https://docs.python.org/3/library/stdtypes.html#str.encode を参照してください。

200

Ignacio Vazquez-Abramsの答えの延長として

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

文字からアクセントを取り除き、基本フォームを印刷することが望ましい場合があります。これはで達成することができます

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

他の文字(句読点など)をそれらに最も近い文字に変換することもできます。たとえば、RIGHT SINGLE QUOTATION MARK Unicode文字はエンコード時にASCIIのアポストロフィに変換されません。

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

これを達成するためのより効率的な方法がありますが。詳細については、この質問を参照してください Pythonの "このUnicodeに最適なASCII"データベースはどこにありますか?

113
Peter Gibson

unidecodeを使用します - これは、変な文字を即座にASCIIに変換し、さらに中国語を音声アスキーに変換します。

$ pip install unidecode

その後:

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

私はすべてのプロジェクトを通してこのヘルパー関数を使います。 Unicodeを変換できない場合は無視します。これはDjangoライブラリと結びついていますが、少し調査すればそれを回避することができます。

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

これを使用した後、Unicodeエラーが発生しなくなりました。

24
Gattster

cmd.exeやHTML出力のような壊れたコンソールにはいつでも使えます:

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

純粋なASCIIで印刷可能にしながら、これはすべての非ASCII文字を保存します。

WARNINGエラーを回避するために本番コードでこれを使用すると、おそらくコードに問題がある可能性があります。これに対する唯一の有効なユースケースは、非Unicodeコンソールへの印刷、またはHTMLコンテキストでのHTMLエンティティへの簡単な変換です。

そして最後に、あなたがウィンドウズ上にいてcmd.exeを使っているなら、utf-8出力を有効にするためにchcp 65001をタイプすることができます(Lucida Consoleフォントで動作します)。 myUnicodeString.encode('utf8')を追加する必要があるかもしれません。

9
ccpizza

あなたが書いたのは ""私はHTMLがどこかのunicodeで間違った形式の試みを含んでいることを意味しています。 ""

HTMLには、整形式であろうとなかろうと、いかなる種類の "Unicodeでの試み"が含まれることは想定されていません。それは必然的に何らかのエンコーディングでエンコードされたUnicode文字を含んでいなければなりません。それは通常最初に提供されます... "charset"を探してください。

あなたは、文字セットがUTF-8であると仮定しているようです…何を根拠に?エラーメッセージに表示されている "\ xA0"バイトは、シングルバイトの文字セットがある可能性があることを示しています。 cp1252.

HTMLの先頭で宣言から理解できない場合は、 chardet を使用して、可能性のあるエンコーディングが何かを調べてください。

あなたの質問に "regex"というタグを付けたのはなぜですか?

質問全体を質問以外に置き換えた後にを更新します。

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
John Machin

文字列lineがある場合は、文字列に.encode([encoding], [errors='strict'])メソッドを使用してエンコーディングタイプを変換できます。

line = 'my big string'

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

PythonでのASCIIとUnicodeの処理の詳細については、これが本当に便利なサイトです。 https://docs.python.org/2/howto/unicode.html

4
Jama22

私は答えがそこにあるが、少しずつでしかないと思う、それはそのような問題を素早く修正することを難しくする。

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

例を見てみましょう。次の形式のデータを含むファイルがあるとします(ASCII文字と非ASCII文字を含む)。

1/10/17、21:36 - 土地:ようこそ��

そして私達はASCII文字だけを無視して保存したいです。

このコードはします:

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

そしてタイプ(rline)はあなたに与えるでしょう

>type(rline) 
<type 'str'>
3
Somum
unicodestring = '\xa0'

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

私のために働く

1
HimalayanCoder