我在处理从不同网页(在不同站点上)获取的文本中的 unicode 字符时遇到问题。我正在使用 BeautifulSoup。
问题是错误并不总是可重现的。它有时适用于某些页面,有时它会通过抛出 UnicodeEncodeError
来呕吐。我已经尝试了几乎所有我能想到的东西,但是我还没有找到任何可以始终如一地工作而不会引发某种与 Unicode 相关的错误的东西。
导致问题的代码部分之一如下所示:
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()
这是运行上面的代码片段时在某些字符串上产生的堆栈跟踪:
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)
我怀疑这是因为某些页面(或更具体地说,来自某些站点的页面)可能已编码,而其他页面可能未编码。所有网站都位于英国,并提供用于英国消费的数据 - 因此不存在与内部化或处理以英语以外的任何文本编写的文本相关的问题。
有没有人对如何解决这个问题有任何想法,以便我可以始终如一地解决这个问题?
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'))
。
$ export PYTHONIOENCODING=utf8
您需要阅读 Python Unicode HOWTO。此错误是 very first example。
基本上,停止使用 str
将 unicode 转换为编码文本/字节。
相反,正确使用 .encode()
对字符串进行编码:
p.agent_info = u' '.join((agent_contact, agent_telno)).encode('utf-8').strip()
或完全使用 unicode。
这是一个经典的python unicode 痛点!考虑以下:
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)
哦,dip,这对任何人都没有好处!要修复错误,请使用 .encode 显式编码字节并告诉 python 使用什么编解码器:
a.encode('utf-8')
=> 'bats\xc3\xa0'
print a.encode('utf-8')
=> batsà
瞧\u00E0!
问题是,当您调用 str() 时,python 使用默认字符编码来尝试对您给它的字节进行编码,在您的情况下,这些字节有时是 unicode 字符的表示。要解决这个问题,你必须告诉 python 如何使用 .encode('whatever_unicode') 来处理你给它的字符串。大多数时候,使用 utf-8 应该没问题。
有关该主题的精彩论述,请参阅此处的 Ned Batchelder 的 PyCon 演讲:http://nedbatchelder.com/text/unipain.html
None
值提出相同的问题。
我找到了优雅的解决方法来删除符号并继续将字符串保留为字符串,如下所示:
yourstring = yourstring.encode('ascii', 'ignore').decode('ascii')
请务必注意,使用忽略选项很危险,因为它会默默地从使用它的代码中删除任何 unicode(和国际化)支持,如下所示(convert unicode):
>>> u'City: Malmö'.encode('ascii', 'ignore').decode('ascii')
'City: Malm'
yourstring = yourstring.encode('utf-8', 'ignore').decode('utf-8')
os.path.join()
,当您开始进行跨平台编程时,这是一个非常好的习惯。 :)
好吧,我尝试了所有方法,但没有帮助,在谷歌搜索后我发现了以下内容并且它有所帮助。 python 2.7 正在使用中。
# encoding=utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
if sys.version_info.major < 3:
甚至导致打印失败的一个微妙问题是您的环境变量设置错误,例如。这里 LC_ALL 设置为“C”。在 Debian 中,他们不鼓励设置它:Debian wiki on Locale
$ 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à
env|grep -E '(LC|LANG)'
。
Ctrl-O
) 中使用 mc
,但我也忘记了我在 bash 中添加了以下别名:alias mc="LANG=en_EN.UTF-8 mc"
。因此,当我尝试运行内部依赖于 ru_RU.UTF-8
的编写不佳的脚本时,它们就会死掉。在我发现真正的问题之前,从这个线程中尝试了很多东西。 :)
问题是您正在尝试打印 unicode 字符,但您的终端不支持它。
您可以尝试安装 language-pack-en
软件包来解决此问题:
sudo apt-get install language-pack-en
它为所有受支持的包(包括 Python)提供英文翻译数据更新。如有必要,安装不同的语言包(取决于您要打印的字符)。
在某些 Linux 发行版上,需要它以确保正确设置默认的英语语言环境(因此 unicode 字符可以由 shell/终端处理)。有时安装它比手动配置更容易。
然后在编写代码时,请确保在代码中使用正确的编码。
例如:
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
shell 中 LANG/LC_CTYPE 的值。
通过以下方式检查您的 shell 支持的语言环境: locale -a | grep "UTF-8"
演示新 VM 中的问题和解决方案。
初始化和配置虚拟机(例如使用 vagrant): vagrant init ubuntu/trusty64;流浪起来; vagrant ssh 请参阅:可用的 Ubuntu 框。打印 unicode 字符(例如商标符号 like ™):$ python -c 'print(u"\u2122");'回溯(最后一次调用):文件“
language-pack-en
与 Python 或这个问题有什么关系? AFAIK,它可以为消息提供语言翻译,但与编码无关
/etc/locale.gen
以确保在使用之前构建他们的语言环境?
/etc/default/locale
中注释掉 LANG
(因为 /etc/locale.gen
不存在)并运行 locale-gen
,但它没有帮助。我不确定 language-pack-en
究竟做了什么,因为我没有找到太多文档,列出它的内容也没有多大帮助。
LANG
/ LC_CTYPE
/ LC_ALL
(例如,{4 })。
在外壳中:
通过以下命令查找支持的 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 中测试。
实际上,我发现在大多数情况下,仅删除这些字符要简单得多:
s = mystring.decode('ascii', 'ignore')
对我来说,有效的是:
BeautifulSoup(html_text,from_encoding="utf-8")
希望这可以帮助某人。
这是对其他一些所谓的“逃避”答案的重新散列。尽管这里表达了抗议,但在某些情况下,简单地丢弃麻烦的字符/字符串是一个很好的解决方案。
def safeStr(obj):
try: return str(obj)
except UnicodeEncodeError:
return obj.encode('ascii', 'ignore').decode('ascii')
except: return ""
测试它:
if __name__ == '__main__':
print safeStr( 1 )
print safeStr( "test" )
print u'98\xb0'
print safeStr( u'98\xb0' )
结果:
1
test
98°
98
更新:我的原始答案是为 Python 2 编写的。对于 Python 3:
def safeStr(obj):
try: return str(obj).encode('ascii', 'ignore').decode('ascii')
except: return ""
注意:如果您希望在“不安全”Unicode 字符所在的位置留下一个 ?
指示符,请在调用中指定 replace
而不是 ignore
以对错误处理程序进行编码。
建议:您可能想将此函数命名为 toAscii
?这是一个偏好问题...
最后,这是一个使用 six
的更强大的 PY2/3 版本,我选择使用 replace
,并加入了一些字符交换,以用简单的垂直引号替换花哨的 unicode 引号和撇号,它们向左或向右卷曲。的 ascii 集。您可以自己扩展此类交换:
from six import PY2, iteritems
CHAR_SWAP = { u'\u201c': u'"'
, u'\u201D': u'"'
, u'\u2018': u"'"
, u'\u2019': u"'"
}
def toAscii( text ) :
try:
for k,v in iteritems( CHAR_SWAP ):
text = text.replace(k,v)
except: pass
try: return str( text ) if PY2 else bytes( text, 'replace' ).decode('ascii')
except UnicodeEncodeError:
return text.encode('ascii', 'replace').decode('ascii')
except: return ""
if __name__ == '__main__':
print( toAscii( u'testin\u2019' ) )
在脚本开头添加以下行(或作为第二行):
# -*- coding: utf-8 -*-
这就是python源代码编码的定义。 PEP 263 中的更多信息。
我总是将下面的代码放在 python 文件的前两行:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
唉,这至少在 Python 3 中有效......
蟒蛇 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'))
其中错误在编码中被忽略。
这个对我有用:
export LC_CTYPE="en_US.UTF-8"
找到了简单的辅助函数 here。
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')
backslashreplace
错误处理程序:u'\xa0'.encode('ascii', 'backslashreplace')
。尽管您应该避免这种表示并将您的环境配置为接受非 ascii 字符,但现在是 2016 年!
只需添加到变量 encode('utf-8')
agent_contact.encode('utf-8')
请打开终端并触发以下命令:
export LC_ALL="en_US.UTF-8"
我刚刚使用了以下内容:
import unicodedata
message = unicodedata.normalize("NFKD", message)
检查有关它的文档说明:
unicodedata.normalize(form, unistr) 返回 Unicode 字符串 unistr 的范式形式。 form 的有效值为“NFC”、“NFKC”、“NFD”和“NFKD”。 Unicode 标准基于规范等价和兼容性等价的定义,定义了 Unicode 字符串的各种规范化形式。在 Unicode 中,几个字符可以用不同的方式表示。例如,字符 U+00C7 (LATIN CAPITAL LETTER C WITH CEDILLA) 也可以表示为序列 U+0043 (LATIN CAPITAL LETTER C) U+0327 (COMBINING CEDILLA)。对于每个字符,有两种范式:范式 C 和范式 D。范式 D (NFD) 也称为规范分解,将每个字符转换为其分解形式。范式 C (NFC) 首先应用规范分解,然后再次组合预先组合的字符。除了这两种形式之外,还有另外两种基于兼容性等价的范式。在 Unicode 中,支持某些通常会与其他字符统一的字符。例如,U+2160(罗马数字一)与 U+0049(拉丁文大写字母 I)实际上是一回事。但是,Unicode 支持它以与现有字符集(例如 gb2312)兼容。范式 KD (NFKD) 将应用兼容性分解,即将所有兼容性字符替换为其等效字符。范式 KC (NFKC) 首先应用兼容性分解,然后是规范组合。即使两个 unicode 字符串被规范化并且对人类读者来说看起来相同,如果一个具有组合字符而另一个没有,它们可能比较不相等。
为我解决。简单易行。
迟到的答案,但此错误与您的终端编码不支持某些字符有关。
我在 python3
上使用以下方法修复了它:
import sys
import io
sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
print("é, à, ...")
下面的解决方案对我有用,刚刚添加
“字符串”
(将字符串表示为 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)
在一般情况下,将此不支持的编码 字符串(比如说data_that_causes_this_error
)写入某个文件(例如results.txt
),这是可行的
f = open("results.txt", "w")
f.write(data_that_causes_this_error.encode('utf-8'))
f.close()
如果它是打印语句的问题,很多时候它只是终端打印的问题。这对我有帮助:export PYTHONIOENCODING=UTF-8
我刚遇到这个问题,谷歌把我带到了这里,所以只是在这里添加一般解决方案,这对我有用:
# 'value' contains the problematic data
unic = u''
unic += value
value = unic
阅读Ned's presentation后,我有了这个想法。
不过,我并没有声称完全理解为什么会这样。因此,如果有人可以编辑此答案或发表评论进行解释,我将不胜感激。
type
的价值是多少?在此之前和之后?我认为这行得通的原因是,通过执行与 unic = unic + value
相同的 unic += value
,您正在添加一个字符串和一个 unicode,然后 python 假定结果 unic
的 unicode 即更精确的类型(想想何时您这样做 a = float(1) + int(1)
,a
变为浮点数)然后 value = unic
将 value
指向恰好是 unicode 的新 unic
对象。
我们在使用本地化装置在 Django 中运行 manage.py migrate
时遇到了这个错误。
我们的源代码包含 # -*- coding: utf-8 -*-
声明,MySQL 已为 utf8 正确配置,并且 Ubuntu 在 /etc/default/locale
中具有适当的语言包和值。
问题只是 Django 容器(我们使用 docker)缺少 LANG
环境变量。
将 LANG
设置为 en_US.UTF-8
并在重新运行迁移之前重新启动容器可以解决问题。
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 格式。
可以阅读更多内容here at PEP 538 -- Coercing the legacy C locale to a UTF-8 based locale。
推荐的解决方案对我不起作用,我可以忍受转储所有非 ascii 字符,所以
s = s.encode('ascii',errors='ignore')
这给我留下了一些不会引发错误的东西。
这里的许多答案(例如@agf 和@Andbdrew)已经解决了 OP 问题最直接的方面。
但是,我认为有一个微妙但重要的方面在很大程度上被忽略了,这对于像我一样最终来到这里并试图理解 Python 中的编码的每个人来说都非常重要:Python 2 与 Python 3 字符表示管理完全不同.我觉得那里的一大块混乱与人们阅读 Python 中的编码而不了解版本有关。
我建议任何有兴趣了解 OP 问题的根本原因的人首先阅读 Spolsky's 字符表示和 Unicode 简介,然后转到 Batchelder 关于 Python 2 和 Python 3 中的 Unicode。
尽量避免将变量转换为 str(variable)。有时,它可能会导致问题。
避免的简单提示:
try:
data=str(data)
except:
data = data #Don't convert to String
上面的例子也将解决编码错误。
如果您有类似 packet_data = "This is data"
的内容,则在初始化 packet_data
之后的下一行执行此操作:
unic = u''
packet_data = unic
我在尝试将 Unicode 字符输出到 stdout
时遇到了这个问题,但使用的是 sys.stdout.write
,而不是打印(这样我也可以支持输出到不同的文件)。
From BeautifulSoup's own documentation,我使用编解码器库解决了这个问题:
import sys
import codecs
def main(fIn, fOut):
soup = BeautifulSoup(fIn)
# Do processing, with data including non-ASCII characters
fOut.write(unicode(soup))
if __name__ == '__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)
print
我的 utf-8 字符串时,它工作得很好。但是,当我将程序输出通过管道传输到文件时,它会抛出UnicodeEncodeError
。事实上,当输出被重定向(到文件或管道)时,我发现sys.stdout.encoding
是None
!加入.encode('utf-8')
解决了这个问题。PYTHONIOENCODING=utf-8
代替,即打印 Unicode 字符串并让环境设置预期的编码。.encode()
方法可以调用。