我在处理从不同网页(在不同的网站上)获取的文本中的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)

我怀疑这是因为某些页面(或者更具体地说,来自某些网站的页面)可能是编码的,而其他页面可能是未编码的。所有的网站都设在英国,并提供英国消费的数据,因此不存在与内化或处理非英语文本相关的问题。

有没有人对如何解决这个问题有什么想法,以便我能够始终如一地解决这个问题?


当前回答

这里是一些其他所谓的“逃避”答案的翻版。尽管这里有抗议声,但在某些情况下,扔掉麻烦的字符/字符串是一个很好的解决方案。

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字符所在的位置,请在调用中指定替换而不是忽略,以对错误处理程序进行编码。

建议:您可能希望将此函数命名为Ascii?这是一个偏好问题。。。

最后,这里有一个更强大的PY2/3版本,它使用了六个,我选择使用替换,并添加了一些字符交换,以用作为ascii集合一部分的简单垂直引号来替换花哨的unicode引号和撇号,这些引号或撇号向左或向右卷曲。您可以自己扩展此类掉期:

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

其他回答

更新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格式。

更多信息可以在PEP538中阅读——强制传统的C语言环境转换为基于UTF-8的语言环境。

唉,这至少在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'))

其中在编码中忽略错误。

我只使用了以下内容:

import unicodedata
message = unicodedata.normalize("NFKD", message)

检查文档中的内容:

unicodedata.normalize(form,unistr)返回Unicode字符串unistr。表单的有效值为“NFC”、“NFKC”、,“NFD”和“NFKD”。Unicode标准定义了Unicode的各种规范化形式字符串,基于规范等价的定义和兼容性等效。在Unicode中以各种方式表达。例如,字符U+00C7(LATIN大写字母C和CEDILLA)也可以表示为序列U+0043(拉丁大写字母C)U+0327(组合塞迪拉)。对于每个字符,有两种范式:范式C和正规形式D(NFD)也称为规范形式并将每个字符转换为其分解形式。范式C(NFC)首先应用正则分解,然后再次编写预先组合的字符。除了这两种形式外,还有两种额外的正规形式基于兼容性等效性。在Unicode中,某些字符是支持通常与其他字符统一的字符。对于例如,U+2160(罗马数字一)实际上与U+0049是一样的(拉丁大写字母I)。但是,Unicode支持与现有字符集(例如gb2312)兼容。范式KD(NFKD)将应用相容性分解,即用它们的等价物替换所有兼容字符。这个范式KC(NFKC)首先应用相容性分解,然后是规范组成。即使两个unicode字符串被规范化人类读者,如果一个有组合字符而另一个没有,它们可能不相等。

帮我解决,简单易行。

这里有简单的助手函数。

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

下面的解决方案对我有效,刚刚补充

u“字符串”

(表示字符串为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)