web-dev-qa-db-fra.com

Laissez l'objet JSON accepter les octets ou laissez les chaînes de sortie urlopen

Avec Python 3, je demande un document json à partir d'une URL.

response = urllib.request.urlopen(request)

L'objet response est un objet de type fichier avec les méthodes read et readline. Normalement, un objet JSON peut être créé avec un fichier ouvert en mode texte.

obj = json.load(fp)

Ce que j'aimerais faire, c'est:

obj = json.load(response)

Cependant, cela ne fonctionne pas car urlopen renvoie un objet de fichier en mode binaire.

Un travail autour est bien sûr:

str_response = response.read().decode('utf-8')
obj = json.loads(str_response)

mais ça fait mal ...

Existe-t-il un meilleur moyen de transformer un objet de fichier d'octets en un objet de fichier chaîne? Ou manque-t-il des paramètres pour que urlopen ou json.load donne un codage?

176
Peter Smit

HTTP envoie des octets. Si la ressource en question est du texte, le codage de caractères est normalement spécifié, soit par l'en-tête HTTP Content-Type, soit par un autre mécanisme (un RFC, HTML meta http-equiv, ...).

urllib devrait savoir comment coder les octets en chaîne, mais c'est trop naïf - c'est une bibliothèque horriblement sous-puissante et non pythonique.

Plongez dans Python donne un aperçu de la situation.

Votre "solution de rechange" va bien - même si vous vous sentez mal, c'est la bonne façon de le faire.

79
Humphrey Bogart

La merveilleuse bibliothèque standard de Python à la rescousse…

import codecs

reader = codecs.getreader("utf-8")
obj = json.load(reader(response))

Fonctionne à la fois avec py2 et py3.

Docs: Python 2 , Python

100
jbg

Je suis venu à l'opinion que la question est la meilleure réponse :)

import json
from urllib.request import urlopen

response = urlopen("site.com/api/foo/bar").read().decode('utf8')
obj = json.loads(response)
66
SergO

Pour tous ceux qui essaient de résoudre ce problème en utilisant la bibliothèque 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'))
18
Luke Yeager

Celui-ci fonctionne pour moi, j'ai utilisé la bibliothèque 'request' avec json() consultez le doc dans demandes pour des humains

import requests

url = 'here goes your url'

obj = requests.get(url).json() 
12
Sarthak Gupta

J'ai rencontré des problèmes similaires avec Python 3.4.3 & 3.5.2 et Django 1.11.3. Cependant, lors de la mise à niveau vers Python 3.6.1, les problèmes ont disparu.

Vous pouvez en savoir plus à ce sujet ici: https://docs.python.org/3/whatsnew/3.6.html#json

Si vous n'êtes pas lié à une version spécifique de Python, envisagez simplement de passer à la version 3.6 ou ultérieure.

5
PaulMest

Si vous rencontrez ce problème lorsque vous utilisez le microframework flask, vous pouvez simplement effectuer les opérations suivantes:

data = json.loads(response.get_data(as_text=True))

From the docs : "Si as_text est défini sur True, la valeur de retour sera une chaîne unicode décodée"

3
cs_stackX

Votre solution de contournement vient de me sauver. J'avais beaucoup de problèmes à traiter la demande en utilisant le framework Falcon. Cela a fonctionné pour moi. req étant le formulaire de demande curl pr httpie

json.loads(req.stream.read().decode('utf-8'))
2
thielyrics

Je viens de trouver cette méthode simple pour créer du contenu HttpResponse en 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"}

J'espère que ça vous aide

1

Cela va diffuser les données d'octet dans JSON.

import io

obj = json.load(io.TextIOWrapper(response))

io.TextIOWrapper est préférable au lecteur de module du codec. https://www.python.org/dev/peps/pep-0400/

1
Collin Anderson