python-3.x - 从 Python 2 移植到 Python 3 : 'utf-8 codec can' t decode byte'

标签 python-3.x urllib stringio

嘿,我试图将那个小片段从 2 移植到 Python 3。

python 2:

def _download_database(self, url):
  try:
    with closing(urllib.urlopen(url)) as u:
      return StringIO(u.read())
  except IOError:
    self.__show_exception(sys.exc_info())
  return None

python 3:

def _download_database(self, url):
  try:
    with closing(urllib.request.urlopen(url)) as u:
      response = u.read().decode('utf-8')
      return StringIO(response)
  except IOError:
    self.__show_exception(sys.exc_info())
  return None

但我还是得到了

utf-8 codec can't decode byte 0x8f in position 12: invalid start byte

我需要使用 StringIO,因为它是一个压缩文件,我想用那个函数解析它:

   def _parse_zip(self, raw_zip):
  try:
     zip = zipfile.ZipFile(raw_zip)

     filelist = map(lambda x: x.filename, zip.filelist)
     db_file  = 'IpToCountry.csv' if 'IpToCountry.csv' in filelist else filelist[0]

     with closing(StringIO(zip.read(db_file))) as raw_database:
        return_val = self.___parse_database(raw_database)

     if return_val:
        self._load_data()

  except:
     self.__show_exception(sys.exc_info())
     return_val = False

  return return_val

raw_zip 是 download_database 函数的返回值

最佳答案

utf-8 无法解码任意二进制数据。

utf-8 是一种字符编码,可用于将文本(例如,在 Python 3 中表示为 str 类型——Unicode 代码点序列)编码为 bytestring(bytes type -- 字节序列([0, 255] 区间内的小整数)并将其解码回来。

utf-8 不是唯一的字符编码。存在与 utf-8 不兼容的字符编码。即使 .decode('utf-8') 没有引发异常;这并不意味着结果是正确的——你可能会得到 mojibake如果您使用错误的字符编码来解码文本。参见 A good way to get the charset/encoding of an HTTP response in Python .

您的输入是一个 zip 文件——二进制数据不是文本,因此您不应尝试将其解码为文本。

Python 3 可帮助您查找与混合二进制数据和文本相关的错误。 要将代码从 Python 2 移植到 Python 3,您应该了解文本 (Unicode) 与二进制数据(字节)的区别。

Python 2 上的

str 是一个字节串,可用于二进制数据和(编码的)文本。除非存在 from __future__ import unicode_literals'' 文字在 Python 2 中创建字节串。u'' 创建 unicode 实例。在 Python 3 上,str 类型是 Unicode。 bytes 是指 Python 3 和 Python 2.7 上的字节序列(bytes 是 Python 2 上 str 的别名)。 b'' 在 Python 2/3 上创建 bytes 实例。

urllib.request.urlopen(url) 返回一个类似文件的对象(二进制文件),您可以按原样传递它在某些情况下 例如,to decode remote gzipped content on-the-fly :

#!/usr/bin/env python3
import xml.etree.ElementTree as etree
from gzip import GzipFile
from urllib.request import urlopen, Request

with urlopen(Request("http://smarkets.s3.amazonaws.com/oddsfeed.xml",
                     headers={"Accept-Encoding": "gzip"})) as response, \
     GzipFile(fileobj=response) as xml_file:
    for elem in getelements(xml_file, 'interesting_tag'):
        process(elem)

ZipFile() 需要一个可使用 seek() 的文件,因此您不能直接传递 urlopen()。您必须先下载内容。您可以使用 io.BytesIO() 来包装它:

#!/usr/bin/env python3
import io
import zipfile
from urllib.request import urlopen

url = "http://www.pythonchallenge.com/pc/def/channel.zip"
with urlopen(url) as r, zipfile.ZipFile(io.BytesIO(r.read())) as archive:
    print({member.filename: archive.read(member) for member in archive.infolist()})

StringIO() 是文本文件。它在 Python 3 中存储 Unicode。

关于python-3.x - 从 Python 2 移植到 Python 3 : 'utf-8 codec can' t decode byte',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34290759/

相关文章:

Python 3 urllib urlparse URL解析

python - 是否可以倒带 python StringIO 内存文件?

python - 在 Python 中使用 BytesIO 和 markdown 代替 open/read 有什么优势吗?

linux - Pyinstaller libmkl_intel_thread.so : undefined symbol: omp_get_num_procs

python - 在 Mac 上使用 Python 3.5.0 + Sublime 3.0 运行代码

python - 通过输入字符串引用Python函数

python - 如何在python中读取url然后打印网站上的每个URL?

python - 使用 urlretrieve 后关闭 ftp 连接

python-3.x - 权限错误 : [WinError 5] Access is denied

python3打印到字符串