让 JSON 对象接受字节或让 urlopen 输出字符串
Posted
技术标签:
【中文标题】让 JSON 对象接受字节或让 urlopen 输出字符串【英文标题】:Let JSON object accept bytes or let urlopen output strings 【发布时间】:2011-10-15 07:42:15 【问题描述】:使用 Python 3,我从 URL 请求一个 json 文档。
response = urllib.request.urlopen(request)
response
对象是具有read
和readline
方法的类文件对象。通常可以使用以文本模式打开的文件来创建 JSON 对象。
obj = json.load(fp)
我想做的是:
obj = json.load(response)
但这不起作用,因为 urlopen 以二进制模式返回文件对象。
解决方法当然是:
str_response = response.read().decode('utf-8')
obj = json.loads(str_response)
但这感觉很糟糕......
有没有更好的方法可以将字节文件对象转换为字符串文件对象?或者我是否缺少urlopen
或json.load
的任何参数来提供编码?
【问题讨论】:
我认为你有一个错字,“readall”应该是“read”? @BobYoplait 我同意。 【参考方案1】:从 Python 3.6 开始,您可以使用 json.loads()
直接反序列化 bytes
object(编码必须是 UTF-8、UTF-16 或 UTF-32)。因此,仅使用标准库中的模块,您可以:
import json
from urllib import request
response = request.urlopen(url).read()
data = json.loads(response)
【讨论】:
【参考方案2】:我使用下面的程序来使用json.loads()
import urllib.request
import json
endpoint = 'https://maps.googleapis.com/maps/api/directions/json?'
api_key = 'AIzaSyABbKiwfzv9vLBR_kCuhO7w13Kseu68lr0'
origin = input('where are you ?').replace(' ','+')
destination = input('where do u want to go').replace(' ','+')
nav_request = 'origin=&destination=&key='.format(origin,destination,api_key)
request = endpoint + nav_request
response = urllib.request.urlopen(request).read().decode('utf-8')
directions = json.loads(response)
print(directions)
【讨论】:
【参考方案3】:Python 出色的标准库来拯救...
import codecs
reader = codecs.getreader("utf-8")
obj = json.load(reader(response))
适用于 py2 和 py3。
文档:Python 2、Python3
【讨论】:
在python 3.4.3
中尝试此答案时出现此错误,不知道为什么?错误是TypeError: the JSON object must be str, not 'StreamReader'
@AronYsidoro 你有没有可能用json.loads()
代替json.load()
?
对于奖励积分,使用响应中指定的编码,而不是假设 utf-8:response.headers.get_content_charset()
。如果没有编码,则返回None
,并且在python2上不存在。
@PhilFrost 这很漂亮。在实践中,小心这一点可能是值得的;根据定义,JSON 始终是 UTF-8、UTF-16 或 UTF-32(绝大多数可能是 UTF-8),因此如果 Web 服务器返回另一种编码,则可能是 Web 服务器软件配置错误,而不是真正非标准的 JSON。
当我在python 3.5中使用时,错误是“AttributeError: 'bytes' object has no attribute 'read'”【参考方案4】:
这会将字节数据流式传输到 json 中。
import io
obj = json.load(io.TextIOWrapper(response))
io.TextIOWrapper 优先于编解码器的模块阅读器。 https://www.python.org/dev/peps/pep-0400/
【讨论】:
`*** AttributeError:“响应”对象没有属性“可读”`` *** AttributeError: 'bytes' 对象没有属性 '可读' 你使用的是 urllib 还是 requests?这是针对 urllib 的。如果你有一个字节对象,只需使用json.loads(bytes_obj.decode())
。【参考方案5】:
您的解决方法实际上只是救了我。我在使用 Falcon 框架处理请求时遇到了很多问题。这对我有用。 req 是请求表单 curl pr httpie
json.loads(req.stream.read().decode('utf-8'))
【讨论】:
【参考方案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 或更高版本。
【讨论】:
【参考方案7】:这个对我有用,我使用带有json()
的“请求”库查看requests for humans 中的文档
import requests
url = 'here goes your url'
obj = requests.get(url).json()
【讨论】:
这是最好的方法。真正可读,任何做这种事情的人都应该有请求。【参考方案8】:如果您在使用烧瓶微框架时遇到此问题,那么您可以这样做:
data = json.loads(response.get_data(as_text=True))
From the docs: "如果 as_text 设置为 True,则返回值将是解码后的 unicode 字符串"
【讨论】:
我来到这个页面是因为我遇到了 Flask 单元测试的问题 - 感谢您发布单行调用。【参考方案9】:对于其他尝试使用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'))
【讨论】:
这个功能是内置在requests
中的:你可以简单地做r.json()
澄清一下,如果你使用@jbg的方法,你不需要做json.loads
。您所要做的就是r.json()
,并且您已经将 JSON 对象加载到 dict 中。
*** UnicodeEncodeError: 'ascii' codec can't encode characters in position 264-265: ordinal not in range(128)
【参考方案10】:
HTTP 发送字节。如果所讨论的资源是文本,则通常通过 Content-Type HTTP 标头或其他机制(RFC、HTML meta http-equiv
、...)指定字符编码。
urllib
应该知道如何将字节编码为字符串,但这太天真了——它是一个功能严重不足且非 Pythonic 的库。
Dive Into Python 3 提供有关情况的概述。
你的“变通办法”很好——虽然感觉不对,但这是正确的做法。
【讨论】:
这可能是“正确”的方法,但如果我可以撤消关于 Python 3 的一件事,那就是这个字节/字符串废话。你会认为内置库函数至少知道如何处理其他内置库函数。我们使用 python 的部分原因是简单直观的语法。这一变化彻底打破了这一点。 查看the "requests" library——它会自动为您处理这类事情。 这不是内置库函数需要“知道如何”处理其他函数的情况。 JSON 被定义为对象的 UTF-8 表示,因此它不能神奇地解码它不知道编码的字节。我同意urlopen
应该能够自己解码字节,因为它知道编码。无论如何,我已经发布了 Python 标准库解决方案作为答案——您可以使用 codecs
模块对字节进行流式解码。
@ThatAintWorking:我不同意。虽然明确地必须管理字节和字符串之间的差异是一件令人头疼的事情,但让语言为您进行一些隐式转换是一件更大的痛苦。隐式字节 字符串转换是许多错误的根源,Python3 非常有助于指出这些缺陷。但我同意图书馆在这方面还有改进的余地。
@ThatAintWorking:不,如果您想要可以在英国或美国以外的其他地方使用的软件,字符串必须是 Unicode。几十年来,我们一直在 ASCII 委员会的短视世界观下受苦。 Python3 终于做对了。可能与起源于欧洲的 Python 有关...【参考方案11】:
刚刚发现了这个简单的方法,可以将 HttpResponse 内容制作成 json 格式
import json
request = RequestFactory() # ignore this, this just like your request object
response = MyView.as_view()(request) # got response as HttpResponse object
response.render() # call this so we could call response.content after
json_response = json.loads(response.content.decode('utf-8'))
print(json_response) # "your_json_key": "your json value"
希望对你有帮助
【讨论】:
【参考方案12】:我认为这个问题是最好的答案:)
import json
from urllib.request import urlopen
response = urlopen("site.com/api/foo/bar").read().decode('utf8')
obj = json.loads(response)
【讨论】:
以上是关于让 JSON 对象接受字节或让 urlopen 输出字符串的主要内容,如果未能解决你的问题,请参考以下文章
包含()资源(css,js)或让浏览器执行另一个请求会更好吗?
是否可以使用 NAT 网络让 FTP 在 VirtualBox 中工作或让 php 的 ftp 功能工作
如何让这个 php 脚本在 data.txt 中添加一行或让它写入不同的