私は(異なるサイト上の)異なるWebページから取得したテキストのUnicode文字を扱うのに問題があります。 BeautifulSoupを使っています。
問題は、エラーが常に再現可能ではないことです。それは時々いくつかのページで動作します、そして時々、それはUnicodeEncodeError
を投げることによって邪魔します。私は考えられるすべてのことを試してみましたが、Unicode関連のエラーを発生させずに一貫して動作するものは何も見つかりませんでした。
問題を引き起こしているコードのセクションの1つを以下に示します。
agent_telno = agent.find('div', 'agent_contact_number')
agent_telno = '' if agent_telno is None else agent_telno.contents[0]
p.agent_info = str(agent_contact + ' ' + agent_telno).strip()
これは上記のスニペットが実行されたときにSOME文字列で生成されたスタックトレースです。
Traceback (most recent call last):
File "foobar.py", line 792, in <module>
p.agent_info = str(agent_contact + ' ' + agent_telno).strip()
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 20: ordinal not in range(128)
これは、一部のページ(またはより具体的には、一部のサイトのページ)がエンコードされている可能性があるのに対し、他のページはエンコードされていない可能性があるためです。すべてのサイトはイギリスを拠点としており、イギリスでの消費を目的としたデータを提供しています。そのため、内部化や英語以外の文字で書かれたテキストの処理に関する問題はありません。
私がこの問題を確実に解決することができるようにこれを解決する方法について何か考えがありますか?
Python Unicode HOWTO を読む必要があります。このエラーは 一番最初の例 です。
基本的に、str
を使ってUnicodeからエンコードされたtext/bytesに変換するのをやめる。
代わりに、文字列をエンコードするために .encode()
を正しく使用してください。
p.agent_info = u' '.join((agent_contact, agent_telno)).encode('utf-8').strip()
または完全にUnicodeで動作します。
これは古典的なPythonのユニコードの痛みのポイントです。次の点を考慮してください。
a = u'bats\u00E0'
print a
=> batsà
これですべて問題ありませんが、str(a)を呼び出した場合はどうなりますか。
str(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)
ああディップ、それは誰にも良いことをするつもりはない!エラーを修正するには、バイトを.encodeで明示的にエンコードし、pythonに使用するコーデックを指定します。
a.encode('utf-8')
=> 'bats\xc3\xa0'
print a.encode('utf-8')
=> batsà
失礼します。
問題は、あなたがstr()を呼ぶとき、あなたがそれに与えたバイトを試みそしてエンコードするためにpythonがデフォルトの文字エンコーディングを使うことです。あなたの場合には、それは時々ユニコード文字の表現です。この問題を解決するには、.encode( 'whatever_unicode')を使用して、与えた文字列をどのように処理するかをpythonに指示する必要があります。ほとんどの場合、あなたはutf-8を使って問題ないはずです。
このトピックに関する優れた解説については、Ned BatchelderのPyCon talkを参照してください。 http://nedbatchelder.com/text/unipain.html
私はシンボルを削除し、次のように文字列として文字列を保持し続けるために私のための優雅な回避策を見つけました:
yourstring = yourstring.encode('ascii', 'ignore').decode('ascii')
無視するオプションの使用は dangerous であることに注意することが重要です。なぜなら、ここで見られるように(convert unicode)、それを使用するコードからUnicode(および国際化)サポートを黙って削除するからです。
>>> u'City: Malmö'.encode('ascii', 'ignore').decode('ascii')
'City: Malm'
まあ私はすべてを試したが、周りをグーグルした後、それは助けにはならなかった、私は以下を考え出し、それは助けた。 python 2.7が使われています。
# encoding=utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
印刷でさえ失敗する微妙な問題はあなたの環境変数を間違って設定することです。ここでLC_ALLは "C"に設定されています。 Debianでは、彼らはそれを設定することを勧めません: ロケールに関するDebian Wiki
$ echo $LANG
en_US.utf8
$ echo $LC_ALL
C
$ python -c "print (u'voil\u00e0')"
Traceback (most recent call last):
File "<string>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)
$ export LC_ALL='en_US.utf8'
$ python -c "print (u'voil\u00e0')"
voilà
$ unset LC_ALL
$ python -c "print (u'voil\u00e0')"
voilà
私は実際に私のほとんどの場合、それらの文字を削除するだけの方がはるかに簡単であることがわかりました。
s = mystring.decode('ascii', 'ignore')
私にとって、うまくいったのは次のとおりです。
BeautifulSoup(html_text,from_encoding="utf-8")
これが誰かに役立つことを願っています。
これを解決してみてください
# encoding=utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
問題は、あなたがUnicode文字を印刷しようとしていることですが、あなたの端末はそれをサポートしていません。
language-pack-en
パッケージをインストールしてみてください。
Sudo apt-get install language-pack-en
サポートされているすべてのパッケージ(Pythonを含む)の英語翻訳データの更新を提供します。必要に応じて別の言語パッケージをインストールします(印刷しようとしている文字によって異なります)。
Linuxディストリビューションによっては、デフォルトの英語ロケールが正しく設定されていることを確認するために必要です(したがって、Unicode文字はShell/terminalで処理できます)。手動で設定するよりも、インストールする方が簡単なことがあります。
それからコードを書くとき、あなたがあなたのコードで正しいエンコーディングを使うことを確認してください。
例えば:
open(foo, encoding='utf-8')
それでも問題が解決しない場合は、次のようにシステム設定をもう一度確認してください。
あなたのロケールファイル(/etc/default/locale
)。
LANG="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
または
LC_ALL=C.UTF-8
LANG=C.UTF-8
シェルのLANG
/ LC_CTYPE
の値。
以下の方法で、シェルがサポートしているロケールを確認してください。
locale -a | grep "UTF-8"
新しいVMで問題と解決策を実証する。
VMを初期化して準備します(例: vagrant
を使用)。
vagrant init ubuntu/trusty64; vagrant up; vagrant ssh
利用可能なUbuntuボックス を見てください。。
Unicode文字(™
のような商標記号など)を印刷する:
$ python -c 'print(u"\u2122");'
Traceback (most recent call last):
File "<string>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2122' in position 0: ordinal not in range(128)
今language-pack-en
をインストールします。
$ Sudo apt-get -y install language-pack-en
The following extra packages will be installed:
language-pack-en-base
Generating locales...
en_GB.UTF-8... /usr/sbin/locale-gen: done
Generation complete.
今問題は解決されるべきです:
$ python -c 'print(u"\u2122");'
™
それ以外の場合は、次のコマンドを試してください。
$ LC_ALL=C.UTF-8 python -c 'print(u"\u2122");'
™
これが、他のいわゆる「コップアウト」の答えのまとめです。抗議行動がここで表明されているにもかかわらず、単に面倒な文字や文字列を捨てるのが良い解決策である状況があります。
def safeStr(obj):
try: return str(obj)
except UnicodeEncodeError:
return obj.encode('ascii', 'ignore').decode('ascii')
except: return ""
それをテストする:
if __== '__main__':
print safeStr( 1 )
print safeStr( "test" )
print u'98\xb0'
print safeStr( u'98\xb0' )
結果:
1
test
98°
98
提案:代わりにこの関数にtoAscii
という名前を付けたいと思うかもしれませんか?それは好みの問題です。
これはPython 2用に書かれました。 Python 3の場合、bytes(obj,"ascii")
ではなくstr(obj)
を使用することをお勧めします。まだテストしていませんが、ある時点で回答を修正します。
シェルで:
次のコマンドでサポートされているUTF-8ロケールを見つけます。
locale -a | grep "UTF-8"
スクリプトを実行する前にエクスポートしてください。
export LC_ALL=$(locale -a | grep UTF-8)
手動で
export LC_ALL=C.UTF-8
特殊文字を印刷してテストします。 ™
:
python -c 'print(u"\u2122");'
上記のUbuntuでテスト済み。
私はいつも以下のコードをpythonファイルの最初の2行に入れます。
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
単純なヘルパー関数が見つかりました ここ 。
def safe_unicode(obj, *args):
""" return the unicode representation of obj """
try:
return unicode(obj, *args)
except UnicodeDecodeError:
# obj is byte string
ascii_text = str(obj).encode('string_escape')
return unicode(ascii_text)
def safe_str(obj):
""" return the byte string representation of obj """
try:
return str(obj)
except UnicodeEncodeError:
# obj is unicode
return unicode(obj).encode('unicode_escape')
単に変数encodeに追加する( 'utf-8')
agent_contact.encode('utf-8')
以下の解決策は私のために働いた、ちょうど追加
"文字列"
私の文字列の前に(Unicodeとして文字列を表します)。
result_html = result.to_html(col_space=1, index=False, justify={'right'})
text = u"""
<html>
<body>
<p>
Hello all, <br>
<br>
Here's weekly summary report. Let me know if you have any questions. <br>
<br>
Data Summary <br>
<br>
<br>
{0}
</p>
<p>Thanks,</p>
<p>Data Team</p>
</body></html>
""".format(result_html)
私はちょうど以下を使いました:
import unicodedata
message = unicodedata.normalize("NFKD", message)
それについてドキュメントが何を言っているかチェックしてください。
unicodedata.normalize(form、unistr)Unicode文字列unistrのための正規形formを返します。 formの有効値は、「NFC」、「NFKC」、「NFD」、および「NFKD」です。
Unicode規格では、正規の等価性と互換性の等価性の定義に基づいて、Unicode文字列のさまざまな正規化形式が定義されています。 Unicodeでは、いくつかの文字をさまざまな方法で表現できます。たとえば、文字U + 00C7(ローマ字大文字CとCEDILLA)は、シーケンスU + 0043(ラテン大文字C)U + 0327(COMBINING CEDILLA)と表現することもできます。
各文字には、正規形Cと正規形Dの2つの正規形があります。正規形D(NFD)は、標準分解とも呼ばれ、各文字をその分解形に変換します。正規形C(NFC)は、最初に正規分解を適用してから、事前に結合された文字をもう一度構成します。
これら2つの形式に加えて、互換性の同等性に基づく2つの追加の標準形式があります。 Unicodeでは、通常他の文字と統合される特定の文字がサポートされています。たとえば、U + 2160(ROMAN NUMERAL ONE)は、実際にはU + 0049(ラテン大文字L)と同じものです。ただし、既存の文字セットとの互換性のためにUnicodeでサポートされています(例:gb2312)。
正規形KD(NFKD)は互換性分解を適用するでしょう、すなわちすべての互換性文字をそれらの同等物で置き換えます。正規形KC(NFKC)は最初に互換性分解を適用し、続いて標準的な構成を適用します。
2つのUnicode文字列が正規化されていて、読者には同じに見えても、一方に結合文字があり、もう一方にそうでない場合、それらは等しく比較されない場合があります。
私のためにそれを解決します。シンプルで簡単.
ターミナルを開いて以下のコマンドを実行してください。
export LC_ALL="en_US.UTF-8"
ローカライズされたフィクスチャを使ってDjangoでmanage.py migrate
を実行すると、このエラーが発生しました。
私達のソースは# -*- coding: utf-8 -*-
宣言を含んでいました、MySQLはutf8のために正しく構成されました、そして、Ubuntuは適切な言語パックと/etc/default/locale
の値を持っていました。
問題は、Djangoコンテナ(我々はdockerを使用しています)にLANG
env varがないということです。
マイグレーションを再実行する前にLANG
をen_US.UTF-8
に設定し、コンテナーを再始動することで問題を解決しました。
私はちょうどこの問題を抱えていた、そしてグーグルはここに私を導いた、それでここに一般的な解決策を追加するために、これは私のために働いたものである:
# 'value' contains the problematic data
unic = u''
unic += value
value = unic
Nedの発表 を読んだ後、私はこの考えを持っていました。
しかし、なぜこれが機能するのかを完全に理解しているとは言いません。だから誰かがこの答えを編集したり説明のためにコメントを入れたりすることができれば私はそれをいただければ幸いです。
これは少なくともPython 3で動作します…
Python 3
時々エラーは環境変数にあり、そうエンコードすることにあります
import os
import locale
os.environ["PYTHONIOENCODING"] = "utf-8"
myLocale=locale.setlocale(category=locale.LC_ALL, locale="en_GB.UTF-8")
...
print(myText.encode('utf-8', errors='ignore'))
エラーはエンコード時に無視されます。
ここでの多くの回答(@agfや@Andbdrewなど)は、OPの質問の最も直接的な側面に既に対処しています。
しかし、私はほとんど無視されている微妙で重要な側面があり、Pythonでエンコーディングの意味を理解しようとしている間に私のように終わったすべての人にとって非常に重要であると思います:Python 2 vs Python 3文字表現の管理は大きく異なります。バージョンを意識せずにPythonのエンコーディングについて読んでいる人々と関係があるので、大きな混乱が生じているように感じます。
OPの問題の根本原因を理解することに興味がある人は、まず Spolskyの 文字表現とUnicodeの概要を読んでから Batchelder Python 2およびPython 3.のUnicode.
変数をstr(variable)に変換しないようにしてください。時々、それは問題を引き起こすかもしれません。
避けるべき簡単なヒント:
try:
data=str(data)
except:
data = str(data)
上記の例はEncode erroも解決します。
packet_data = "This is data"
のようなものがある場合は、packet_data
を初期化した直後の次の行でこれを行います。
unic = u''
packet_data = unic
Python 3.0以降にアップデートしてください。 Pythonエディタで次のことを試してください。
locale-gen en_US.UTF-8
export LANG=en_US.UTF-8 LANGUAGE=en_US.en
LC_ALL=en_US.UTF-8
これはシステムのデフォルトロケールエンコーディングをUTF-8フォーマットに設定します。
もっと読むことができます ここPEP 538 - レガシーCロケールをUTF-8ベースのロケールに強制変換する 。
これはpython 2.7に役立ちます
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
これはsys.setdefaultencoding()を再び有効にするのを助けます
私はUnicode文字をstdout
に出力しようとしていましたが、printではなくsys.stdout.write
を使っていました。
BeautifulSoup自身のドキュメンテーションから 、私はコーデックライブラリでこれを解決しました:
import sys
import codecs
def main(fIn, fOut):
soup = BeautifulSoup(fIn)
# Do processing, with data including non-ASCII characters
fOut.write(unicode(soup))
if __== '__main__':
with (sys.stdin) as fIn: # Don't think we need codecs.getreader here
with codecs.getwriter('utf-8')(sys.stdout) as fOut:
main(fIn, fOut)