让JSON对象接受字节或让urlopen输出字符串

Pet*_*mit 176 python encoding json urlopen python-3.x

使用Python 3,我从URL请求json文档.

response = urllib.request.urlopen(request)
Run Code Online (Sandbox Code Playgroud)

response对象是一个类似文件的对象readreadline方法.通常,可以使用以文本模式打开的文件创建JSON对象.

obj = json.load(fp)
Run Code Online (Sandbox Code Playgroud)

我想做的是:

obj = json.load(response)
Run Code Online (Sandbox Code Playgroud)

但是,这不起作用,因为urlopen以二进制模式返回文件对象.

当然,解决方法是:

str_response = response.read().decode('utf-8')
obj = json.loads(str_response)
Run Code Online (Sandbox Code Playgroud)

但这感觉很糟糕......

有没有更好的方法可以将字节文件对象转换为字符串文件对象?或者我错过任何参数urlopenjson.load给出编码?

jbg*_*jbg 99

Python拯救的精彩标准库......

import codecs

reader = codecs.getreader("utf-8")
obj = json.load(reader(response))
Run Code Online (Sandbox Code Playgroud)

适用于py2和py3.

文档:Python 2,Python3

  • 在`python 3.4.3`中尝试这个答案时我得到了这个错误,不知道为什么?错误是`TypeError:JSON对象必须是str,而不是'StreamReader' (11认同)
  • @AronYsidoro您是否可以使用`json.loads()`而不是`json.load()`? (9认同)
  • 对于奖励积分,请使用响应中指定的编码,而不是假设utf-8:`response.headers.get_content_charset()`.如果没有编码则返回`None`,并且在python2上不存在. (6认同)
  • 当我在python 3.5中使用时,错误是"AttributeError:'bytes'对象没有属性'read'" (6认同)
  • @PhilFrost那很光滑.在实践中,要小心这一点; JSON始终是UTF-8,UTF-16或UTF-32定义(并且是压倒性的可能是UTF-8),所以如果另一编码是由Web服务器返回,这也可能是Web服务器软件的配置错误,而不是真正的非标准JSON. (5认同)

Hum*_*art 79

HTTP发送字节.如果有问题的资源是文本,则通常通过Content-Type HTTP标头或其他机制(RFC,HTML meta http-equiv,...)指定字符编码.

urllib 应该知道如何将字节编码为字符串,但它太天真 - 它是一个可怕的动力不足和非Pythonic库.

Dive Into Python 3提供了有关情况的概述.

你的"解决方案"很好 - 虽然感觉不对,但这是正确的做法.

  • 这可能是"正确"的方式,但如果有一件事我可以撤消Python 3,那将是这个字节/字符串废话.您会认为内置库函数至少知道如何处理其他内置库函数.我们使用python的部分原因是简单直观的语法.这种变化打破了整个地方. (5认同)
  • 查看["请求"库](http://docs.python-requests.org/) - 它会自动处理这类事情. (3认同)
  • 这不是内置库函数需要"知道如何"处理其他函数的情况.JSON被定义为对象的UTF-8表示,因此它不能神奇地解码它不知道编码的字节.我同意`urlopen`应该能够解码字节本身,因为它知道编码.无论如何,我已经发布了Python标准库解决方案作为答案 - 您可以使用`codecs`模块对字节进行流式解码. (2认同)

Ser*_*rgO 66

我认为问题是最好的答案:)

import json
from urllib.request import urlopen

response = urlopen("site.com/api/foo/bar").read().decode('utf8')
obj = json.loads(response)
Run Code Online (Sandbox Code Playgroud)


Luk*_*ger 18

对于任何试图使用该requests库解决此问题的人:

import json
import requests

r = requests.get('http://localhost/index.json')
r.raise_for_status()
# works for Python2 and Python3
json.loads(r.content.decode('utf-8'))
Run Code Online (Sandbox Code Playgroud)

  • 这个功能内置于`requests`:你可以简单地做`r.json()` (12认同)

Sar*_*pta 13

这个适用于我,我使用'请求'库json()检查文件中的人类请求

import requests

url = 'here goes your url'

obj = requests.get(url).json() 
Run Code Online (Sandbox Code Playgroud)


Pau*_*est 6

我使用Python 3.4.3和3.5.2以及Django 1.11.3遇到了类似的问题.但是,当我升级到Python 3.6.1时,问题就消失了.

你可以在这里阅读更多相关信息:https: //docs.python.org/3/whatsnew/3.6.html#json

如果您没有绑定特定版本的Python,请考虑升级到3.6或更高版本.


Eug*_*ash 5

从Python 3.6开始,您可以直接使用json.loads()它来反序列化bytes对象(编码必须是UTF-8、UTF-16或UTF-32)。因此,仅使用标准库中的模块,您可以执行以下操作:

import json
from urllib import request

response = request.urlopen(url).read()
data = json.loads(response)
Run Code Online (Sandbox Code Playgroud)