'ascii' 编解码器无法编码字符:序号不在范围内(128)

6
我正在使用Selenium和BeautifulSoup爬取一些网页。我正在遍历一堆链接,获取信息,然后将其转储到JSON中:
for event in events:

    case = {'Artist': item['Artist'], 'Date': item['Date'], 'Time': item['Time'], 'Venue': item['Venue'],
        'Address': item['Address'], 'Coordinates': item['Coordinates']}
    item[event] = case

with open("testScrape.json", "w") as writeJSON:
json.dump(item, writeJSON, ensure_ascii=False)

当我访问这个链接时:https://www.bandsintown.com/e/100778334-jean-deaux-music-at-rickshaw-stop?came_from=257&utm_medium=web&utm_source=home&utm_campaign=event

代码出现错误,我收到以下错误信息:

 Traceback (most recent call last):
  File "/Users/s/PycharmProjects/hi/BandsintownWebScraper.py", line 126, in <module>
    json.dump(item, writeJSON, ensure_ascii=False)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 190, in dump
    fp.write(chunk)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe6' in position 7: ordinal not in range(128)

我尝试使用:

json.dump(item, writeJSON, ensure_ascii=False).decode('utf-8')

并且:

json.dump(item, writeJSON, ensure_ascii=False).encode('utf-8')

没有成功。我相信链接中的ï字符导致了失败。有人能简要介绍一下正在发生什么,编码/解码的含义以及如何解决这个问题吗?


这个错误可能意味着数据不是以UTF-8编码,而是其他编码方式,例如Latin1CP1250 - furas
Python将文本保存在Unicode中,这意味着每个字符甚至可以使用8个字节。为了发送或保存到文件中,它会被转换(编码)为utf-8latin1等,以使用更少的空间。编码后的字符可能使用1个字节,其他字符可能使用2个或更多字节 - 这比每个字符使用8个字节要节省空间。当您获取它时,您必须将其转换(解码)回Unicode,以便Python可以使用它。 - furas
你没有展示完整的错误(Traceback),但我认为问题不在这段代码中,而是在从网页获取数据的代码中。也许你需要对页面上的数据进行解码。或者你可能需要在 open(..., encode='utf-8') 中设置编码。 - furas
4个回答

18

在shell中运行python脚本之前,您可能需要设置PYTHONIOENCODING。例如,当我将python脚本输出重定向到日志文件时,我遇到了相同的错误:

$ your_python_script > output.log
'ascii' codec can't encode characters in position xxxxx-xxxxx: ordinal not in range(128)

在shell中将PYTHONIOENCODING更改为UTF8后,脚本执行时没有出现ASCII编解码错误:

$ export PYTHONIOENCODING=utf8

$ your_python_script > output.log

非常感谢!我疯了一样地尝试弄清楚为什么我的Python3不能打印基本的Unicode! - Eric C.

7

您的问题在于,在Python 2中,文件对象(由open()返回)只能写入str对象,而不能写入unicode对象。向json.dump()传递ensure_ascii=False会使其直接尝试将Unicode字符串作为unicode对象写入文件,这将导致失败。

json.dump(item, writeJSON, ensure_ascii=False).encode('utf-8')
这个尝试修复的方法行不通,因为json.dump()不会返回任何东西,而是直接将内容写入文件。(如果在item中没有Unicode文本,这将在json.dump()完成后崩溃-- json.dump() 返回None,无法调用.encode())。
有三种方法可以修复这个问题:
  1. Use Python 3. The unification of str and unicode in Python 3 makes your existing code work as-is; no code changes are necessary.

  2. Remove ensure_ascii=False from your call to json.dump. Non-ASCII characters will be written to the file in escaped form -- for instance, ï will be written as \u00ef. This is a perfectly valid way of representing Unicode characters, and most JSON libraries will handle it just fine.

  3. Wrap the file object in a UTF-8 StreamWriter:

    import codecs
    with codecs.getwriter("utf8")(open("testScrape.json", "w")) as writeJSON:
        json.dump(item, writeJSON, ensure_ascii=False)
    

1
一种同时实现修复1和3的方法是使用io.open而不是Python 2内置的open函数。 io.open与Python 3的open相同,因此默认支持Unicode。 - Blckknght
@Blckknght,你应该把它作为答案,它确实帮了我 :) - OfirD

2

当我在 Gitlab 中运行流水线时,遇到了同样的问题(在 Github Action、Circle CI 或其他流水线中从未见过此错误)。

最终通过以下方式解决了该问题。

before_script:
    - apt-get clean && apt-get update && apt-get install -y locales
    - echo "en_US UTF-8" > /etc/locale.gen
    - locale-gen en_US.UTF-8
    - export LANG=en_US.UTF-8
    - export LANGUAGE=en_US:en
    - export LC_ALL=en_US.UTF-8

0

pip install unidecode

from unidecode import unidecode

for col in ['column1', 'column2']:
    df[col] = df[col].apply(unidecode)

如果这是一个Pandas对象,那么只需将列名放在[]内作为列表传递即可。 因为我今天遇到了同样的问题,并解决了它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接