【问题标题】:Why can't I decode this UTF-8 page?为什么我不能解码这个 UTF-8 页面?
【发布时间】:2017-12-10 16:09:41
【问题描述】:

大家好,

我是使用 python 从网络获取数据的新手。我想把这个页面的源代码放在一个字符串中: https://projects.fivethirtyeight.com/2018-nba-predictions/

以下代码适用于其他页面(例如https://www.basketball-reference.com/boxscores/201712090ATL.html):

import urllib.request
file = urllib.request.urlopen(webAddress)
data = file.read()
file.close()
dataString = data.decode(encoding='UTF-8')

而且我希望 dataString 是一个 HTML 字符串(请参阅下文了解我在这种特定情况下的期望)

<!DOCTYPE html><html lang="en"><head><meta property="article:modified_time" etc etc

相反,对于 538 网站,我收到此错误:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

我的研究表明问题在于我的文件实际上并未使用 UTF-8 编码,但页面的字符集和 beautiful-soup 的 UnicodeDammit() 都声称它是 UTF-8(第二个可能是因为第一个)。 chardet.detect() 不建议任何编码。我尝试在 decode() 的编码参数中将以下内容替换为“UTF-8”,但无济于事:

ISO-8859-1

拉丁-1

Windows-1252

也许值得一提的是字节数组数据看起来不像我期望的那样。这是来自工作 URL 的 data[:10]:

b'\n<!DOCTYPE'

这是来自 538 站点的数据[:10]:

b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

怎么了?

【问题讨论】:

  • wget抓取数据提供了一个gzip压缩的文件,它在未压缩时提供了一个常规的UTF-8 HTML页面;可能是服务器配置不当,提供了一个压缩页面而没有设置相关的标题。
  • (看file.headers['content-encoding'])
  • @Ryan:确实它似乎将gzip 设置为content-encoding,但curlwget 都没有对此做任何事情,这很奇怪,因为它们通常会透明地处理传输-级别压缩...这台服务器的行为一定有些奇怪。
  • @matteoitalia 使用 wget 确实向我展示了它是 gzip 压缩的。对于使用 python 的我来说,这是一个陌生的领域,但它已经取得了足够的进展,我有信心进一步探索。谢谢!!
  • @AndyPollino:进一步研究,似乎curl(没有--compressed)、wgeturllib(通常)不会自动处理压缩后的内容,因此它们不要设置相应的accept-encoding请求头,但是服务器无论如何都会提供gzip压缩的内容。看来你得自己处理了。 OTOH,伟大的 requests 库确实可以自己处理整个事情。

标签: python encoding utf-8 character-encoding


【解决方案1】:

服务器为您提供了 gzip 压缩的数据;这并不完全常见,因为默认情况下urllib 没有设置任何accept-encoding 值,因此服务器通常保守地不压缩数据。

不过,响应的content-encoding字段设置的,所以你有办法知道你的页面确实是gzip压缩的,你可以使用Python gzip模块解压它在进一步处理之前。

import urllib.request
import gzip
file = urllib.request.urlopen(webAddress)
data = file.read()
if file.headers['content-encoding'].lower() == 'gzip':
    data = gzip.decompress(data)
file.close()
dataString = data.decode(encoding='UTF-8')

OTOH,如果您有可能使用requests 模块,它会自行处理所有这些混乱,包括压缩(我是否提到除了gzip,您还可能得到deflate,哪个is the same but with different headers? ) 和(至少部分)编码。

import requests
webAddress = "https://projects.fivethirtyeight.com/2018-nba-predictions/"
r = requests.get(webAddress)
print(repr(r.text))

这将执行您的请求并正确打印出已解码的 Unicode 字符串。

【讨论】:

    【解决方案2】:

    您正在读取 gzipped 数据:http://www.forensicswiki.org/wiki/Gzip 您必须对其进行解压缩。

    【讨论】:

      猜你喜欢
      • 2018-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-29
      • 1970-01-01
      • 2019-10-24
      • 1970-01-01
      • 2011-03-01
      相关资源
      最近更新 更多