题 UnicodeEncodeError:'ascii'编解码器无法对位置20中的字符u'\ xa0'进行编码:序数不在范围内(128)


我在处理从不同网页(在不同网站上)获取的文本中的unicode字符时遇到问题。我正在使用BeautifulSoup。

问题是错误并不总是可重现的;它有时适用于某些页面,有时它通过抛出一个barf 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()

以下是运行上述代码段时在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)

我怀疑这是因为某些页面(或更具体地说,来自某些站点的页面)可能被编码,而其他页面可能是未编码的。所有这些网站都位于英国,并提供供英国消费的数据 - 因此,没有与内部化或处理用英语以外的任何文字处理的文本相关的问题。

有没有人有任何想法如何解决这个问题,以便我可以一致地解决这个问题?


939
2018-03-30 12:06


起源


如果您以用户而非开发人员的身份获得这些错误,请检查 serverfault.com/questions/54591/... 和 askubuntu.com/questions/599808/... - That Brazilian Guy
我将添加这一点不要使用 onlinegdb.com/online_python_interpreter 对于这个东西。正在使用该解释器来试用,并且没有正确配置Unicode!一直打印的格式为'B'\ nnn''...当我想要的只是一个guillemet!尝试在VM上使用chr()按预期立即工作 - JGFMK


答案:


你需要阅读Python Unicode HOWTO。这个错误是 第一个例子

基本上,停止使用 str 从unicode转换为编码的文本/字节。

相反,正确使用 .encode() 编码字符串:

p.agent_info = u' '.join((agent_contact, agent_telno)).encode('utf-8').strip()

或完全以unicode工作。


1054
2018-03-30 12:21



同意!我被教导的一个好的经验法则是使用“unicode sandwich”的想法。您的脚本接受来自外部世界的字节,但所有处理都应该以unicode完成。只有当您准备好输出数据时,才能将其重新分成字节! - Andbdrew
如果其他人对此感到困惑,我发现了一个奇怪的事情:我的终端使用utf-8,当我 print 我的utf-8字符串很好用。但是,当我将程序输出传递给一个文件时,它会抛出一个 UnicodeEncodeError。实际上,当输出被重定向(到文件或管道)时,我发现了 sys.stdout.encoding 是 None!接下来 .encode('utf-8') 解决了这个问题。 - drevicko
@drevicko:用 PYTHONIOENCODING=utf-8 相反,即打印Unicode字符串并让环境设置预期的编码。 - jfs
这是一个糟糕而混乱的建议。人们使用str的原因是因为对象不是字符串,所以没有 .encode() 打电话的方法。 - Cerin
@Praxiteles不,不是。它只是从原始代码复制,如连接和变量名称。这是他的用例特有的东西。 - agf


这是一个经典的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)

噢,这不会有任何好处!要修复错误,请使用.encode显式编码字节并告诉python要使用的编解码器:

a.encode('utf-8')
 => 'bats\xc3\xa0'
print a.encode('utf-8')
 => batsà

Voil \ u00E0!

问题是,当你调用str()时,python使用默认的字符编码来尝试编码你给它的字节,在你的情况下有时代表unicode字符。要解决这个问题,你必须告诉python如何使用.encode('whatever_unicode')来处理你给它的字符串。大多数时候,使用utf-8应该没问题。

有关此主题的精彩论述,请参阅Ned Batchelder的PyCon演讲: http://nedbatchelder.com/text/unipain.html


369
2018-03-30 12:25



个人注意:当尝试键入“.encode”时,不要意外键入“.unicode”,然后想知道为什么没有任何效果。 - Skip Huffman
好建议。但是当你使用str(x)来打印可能是或可能不是字符串的对象时,你会做什么呢?如果x是数字,日期时间,布尔值或普通字符串,则str(x)起作用。突然间,如果它是一个unicode它停止工作。有没有办法获得相同的行为或者我们现在需要添加IF检查以测试对象是否为使用.encode的字符串,否则为str()? - Dirk R


我找到了优雅的工作来删除符号并继续将字符串保持为字符串如下:

yourstring = yourstring.encode('ascii', 'ignore').decode('ascii')

重要的是要注意使用ignore选项是 危险 因为它静默地从使用它的代码中删除任何unicode(和国际化)支持,如此处所示(转换unicode):

>>> u'City: Malmö'.encode('ascii', 'ignore').decode('ascii')
'City: Malm'

167
2017-08-20 10:13



你让我今天一整天都感觉很好!对于utf-8,它足以做到: yourstring = yourstring.encode('utf-8', 'ignore').decode('utf-8') - luca76
对我来说,这确实有效,但我的情况不同,我正在保存文件名,名称中有“/”,路径不存在所以我必须使用.replace(“/”,“”)并因此保存我的剧本。而忽略ascii也适用于'utf-8'的情况。 - harrypotter0


好吧,我尝试了一切,但它没有帮助,谷歌搜索后我想到以下,它有所帮助。 python 2.7正在使用中。

# encoding=utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')

109
2017-09-02 13:10



不要这样做。 stackoverflow.com/questions/3828723/...,虽然你有这样的答案 stackoverflow.com/a/31137935/2141635 当您搜索错误时,在结果顶部附近,我可以看到为什么它看起来像个好主意。 - Padraic Cunningham
我尝试了几乎所有这个主题的建议,但实际上没有一个适合我。最后我尝试了这个。它真的是唯一一个简单而有效的工具。如果有人说“不要这样做,那就来一个简单的解决方案。否则使用这个。因为这是一个很好的工作副本和过去的解决方案。 - Richard
此解决方案适用于file.write()以及print()。 - Jacob Quisenberry
怎么能在python3中完成?很高兴知道。 - Kanerva Peter
不要这样做!如果你这样做,你可以避免 堆 Python2和unicode的神秘知识!惊恐的事件! - Prof. Falken


导致偶数打印失败的一个微妙问题是您的环境变量设置错误,例如。这里LC_ALL设置为“C”。在Debian中,他们不鼓励设置它: Locale上的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à

72
2017-12-02 17:58



得到了完全相同的问题,我以前没有检查过这么糟糕 报告。非常感谢。顺便说一下,你可以用前面的两个命令替换 env|grep -E '(LC|LANG)'。 - Dmitry Verhoturov


我实际上发现,在我的大多数情况下,剥离这些字符要简单得多:

s = mystring.decode('ascii', 'ignore')

27
2017-11-01 13:44



“完美”通常不是它的表现。它抛弃了你应该弄清楚如何正确处理的东西。 - tripleee
只是剥离“那些”(非英语)字符不是解决方案,因为python必须支持所有语言你不觉得吗? - alemol
Downvoted。这根本不是正确的解决方案。了解如何使用Unicode: joelonsoftware.com/articles/Unicode.html - Andrew Ferrier
看,提出这个特定答案最明智的方式就是这样:认识到ascii赋予某些语言和用户某种特权 - 这就是 逃生舱口 对于那些可能会在执行完全unicode支持之前将粗略的,第一次通过,脚本一起潜在地用于初步工作的用户,可能会被利用。 - lol
如果我正在编写一个只需要在内部公司应用程序中将英文文本打印到stdout的脚本,我只想让问题消失。无论什么有效。 - kagronick


对我来说,有用的是:

BeautifulSoup(html_text,from_encoding="utf-8")

希望这有助于某人。


24
2018-01-26 14:53





在脚本开头添加以下行(或作为第二行):

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

这是python源代码编码的定义。更多信息 PEP 263


16
2017-08-08 10:17





问题是你正在尝试打印一个unicode字符,但你的终端不支持它。

你可以尝试安装 language-pack-en 包修复:

sudo apt-get install language-pack-en

它为所有支持的软件包(包括Python)提供英文翻译数据更新。如有必要,请安装不同的语言包(取决于您尝试打​​印的字符)。

在某些Linux发行版中,为了确保正确设置默认的英语语言环境(因此可以通过shell /终端处理unicode字符),需要它。有时,安装它比手动配置更容易。

然后在编写代码时,请确保在代码中使用正确的编码。

例如:

open(foo, encoding='utf-8')

如果您仍然有问题,请仔细检查您的系统配置,例如:

  • 您的语言环境文件(/etc/default/locale),应具有例如

    LANG="en_US.UTF-8"
    LC_ALL="en_US.UTF-8"
    
  • 的价值 LANG/LC_CTYPE 在壳中。

  • 检查shell支持的语言环境:

    locale -a | grep "UTF-8"
    

在新VM中展示问题和解决方案。

  1. 初始化和配置VM(例如,使用 vagrant):

    vagrant init ubuntu/trusty64; vagrant up; vagrant ssh
    

    看到: 可用的Ubuntu盒子

  2. 打印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)
    
  3. 现在安装 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.
    
  4. 现在问题解决了:

    $ python -c 'print(u"\u2122");'
    
    

14
2017-08-13 12:07



有什么 language-pack-en 与Python或这个问题有关? AFAIK,它可以为消息提供语言翻译,但与编码无关 - Alastair McCormack
在某些Linux发行版中,为了确保正确设置默认的英语语言环境,特别是在终端上运行Python脚本时,需要它。它曾经对我有用。看到: 字符编码 - kenorb
喔好吧。你的意思是,如果你想使用非英语语言环境?我想用户也必须编辑 /etc/locale.gen 在使用它之前确保它们的区域设置是否已构建? - Alastair McCormack
@AlastairMcCormack注释掉了 LANG 从 /etc/default/locale (如 /etc/locale.gen 不存在)并跑了 locale-gen,但它没有帮助。我不确定是什么 language-pack-en 确切地说,因为我没有找到太多的文档和列出它的内容没有多大帮助。 - kenorb
桌面系统上不存在utf-8语言环境,即,您可能不需要安装任何东西,只需配置 LANG/ LC_CTYPE/ LC_ALL 相反(例如, LANG=C.UTF-8)。 - jfs


这是对其他一些所谓的“警察出局”答案的重复。尽管这里有抗议活动,但在某些情况下,简单地丢弃麻烦的角色/弦乐是一个很好的解决方案。

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

建议:您可能希望将此功能命名为 toAscii 代替?这是一个偏好问题。


10
2017-09-26 19:23





找到简单的辅助函数 这里

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

6
2017-12-31 07:57



要获取转义的字节串(使用ascii编码将任意Unicode字符串转换为字节),您可以使用 backslashreplace 错误处理程序 u'\xa0'.encode('ascii', 'backslashreplace')。虽然你应该避免这种表示,并将你的环境配置为接受非ascii字符 - 它是2016年! - jfs
新年快乐@ J.F.Sebastian。我对Python-Unicode问题感到沮丧,然后终于得到了这个有效的解决方案。我不知道这件事。无论如何,谢谢你的提示。 - Parag Tyagi -morpheus-