0

我正在使用 Python 2.7.3 和 BeuatofulSoup 从网站的表格中获取数据,然后使用codecs将内容写入文件。我收集的变量之一,偶尔会出现乱码。例如,如果网站表看起来像这样

 Year    Name   City             State
 2000    John   D’Iberville    MS
 2001    Steve  Arlington        VA

因此,当我生成City变量时,我总是将其编码为utf-8

 Year = foo.text
 Name = foo1.text
 City = foo3.text.encode('utf-8').strip()
 State = foo4.text

 RowsData = ("{0},{1},{2},{3}").format(Year, Name, City, State)

所以我创建的逗号分隔字符串列表的内容被称为RowData并且RowHeaders看起来像这样

 RowHeaders = ['Year,Name,City,State']

 RowsData = ['2000, John, D\xc3\xa2\xe2\x82\xac\xe2\x84\xa2Iberville, MS', 
            '2001, Steve, Arlington, VA']

然后我尝试使用以下代码将其写入文件

 file1 = codecs.open(Outfile.csv,"wb","utf8")
 file1.write(RowHeaders + u'\n')
 line = "\n".join(RowsData)
 file1.write(line + u'\r\n')
 file1.close()

我收到以下错误

 Traceback (most recent call last):  
     File "HSRecruitsFBByPosition.py", line 141, in <module>
       file1.write(line + u'\r\n')

 UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 6879: ordinal not in range(128)

我可以使用 csv writer 包RowsData,它工作正常。由于我不想进入的原因,我需要使用编解码器来输出 csv 文件。我不知道发生了什么。谁能帮我解决这个问题?提前致谢。

4

2 回答 2

1

codecs.open() 为你编码。不要将编码数据交给它,因为这样 Python 会再次尝试为您解码数据,以便将其编码为 UTF-8。该隐式解码使用 ASCII 编解码器,但由于您的编码字节字符串中有非 ASCII 数据,因此失败:

>>> u'D’Iberville'.encode('utf8')
'D\xc3\xa2\xe2\x82\xac\xe2\x84\xa2Iberville'
>>> u'D’Iberville'.encode('utf8').encode('utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)

解决方案是 *not 手动编码:

Year = foo.text
Name = foo1.text
City = foo3.text.strip()
State = foo4.text

请注意,这codecs.open()不是文件流的最有效实现。在 Python 2.7 中,我会io.open()改用; 它提供了相同的功能,但实现得更健壮。该io模块是 Python 3 的默认 I/O 实现,但也可用于 Python 2 以实现前向兼容性。

但是,您似乎正在重新发明 CSV 处理;Python 有一个出色的csv模块,可以为您生成 CSV 文件。但是在 Python 2 中它不能处理 Unicode,所以你需要手动编码:

import csv

# ...

year = foo.text
name = foo1.text
city = foo3.text.strip()
state = foo4.text

row = [year, name, city, state]

with open(Outfile.csv, "wb") as outf:
    writer = csv.writer(outf)
    writer.writerow(['Year', 'Name', 'City', 'State'])
    writer.writerow([c.encode('utf8') for c in row])

最后但同样重要的是,如果您的 HTML 页面生成了文本,D’Iberville那么您就生成了Mojibake;您将 UTF-8 误解为 CP-1252 的地方:

>>> u'D’Iberville'.encode('cp1252').decode('utf8')
u'D\u2019Iberville'
>>> print u'D’Iberville'.encode('cp1252').decode('utf8')
D’Iberville

这通常是由于绕过 BeautifulSoup 的编码检测(传入字节字符串,而不是 Unicode)引起的。

您可以在事后尝试“修复”这些问题:

try:
    City = City.encode('cp1252').decode('utf8')
except UnicodeError:
    # Not a value that could be de-mojibaked, so probably
    # not a Mojibake in the first place.
    pass
于 2014-08-20T07:27:09.407 回答
0

'D\xc3\xa2\xe2\x82\xac\xe2\x84\xa2Iberville'是一个普通字符串,恰好有代表字符的转义位。

所以,要把它写出来,你需要先解码它。由于您没有进行解码,因此 Python 正在尝试 ASCII 并且失败了。

>>> s
'D\xc3\xa2\xe2\x82\xac\xe2\x84\xa2Iberville'
>>> type(s)
<type 'str'>
>>> type(s.decode('utf-8'))
<type 'unicode'>
>>> print(s.decode('utf-8'))
D’Iberville

以下是如何理解这个过程:

  1. 首先,要明白字符是为人类服务的,字节是为计算机服务的。计算机只是帮我们将字节转换为字符,以便我们能够理解数据。

  2. 因此,任何时候您需要为计算机的利益而存储某些内容时,您都需要将其从字符转换为字节,因为这是计算机所知道的。所有文件(甚至文本文件)都是字节。只要你打开它,这个字节数据就转换成字符,这样我们就可以理解它的内容了。对于“二进制”文件(如图像或 Word 文档),此过程有点不同。

  3. 如果我们正在编写“文本”内容,我们需要获取字形(字符)并将它们转换为字节,以便可以写入文件。这个过程称为解码。

  4. 当我们想“读取”一个文本文件时,即将字节转换为字形(字符或字母),我们需要对位进行编码——实际上,就是翻译它们。要知道什么字形对应于存储的位,我们使用一个查找表,这个表名(utf-8)就是你传入的。

于 2014-08-20T07:36:06.970 回答