Décoder du json avec le résultat de l'API

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonsoir,

je sollicite votre aide car je ne comprend pas se que je fais faux, malgré se que j'ai pu trouver dans la doc ou des forums.

Voici donc un code de base qui ne fonctionne pas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import urllib.request
import json

r = urllib.request.urlopen('https://zestedesavoir.com/api/membres/')
r_json = json.loads(r) #Egalement essayer avec 'loads' et en mettant 'r.read()' ...

#erreur ... Traceback (most recent call last):
#  File "<pyshell#21>", line 1, in <module>
#    r_json = json.loads(r) #Egalement essayer avec 'loads' et en mettant 'r.read()' ...
#  File "C:\Program Files (x86)\Python 3\lib\json\__init__.py", line 312, in loads
#    s.__class__.__name__))
#TypeError: the JSON object must be str, not 'HTTPResponse'

#erreur avec json.load:
#Traceback (most recent call last):
#  File "<pyshell#23>", line 1, in <module>
#    r_json = json.load(r)
#  File "C:\Program Files (x86)\Python 3\lib\json\__init__.py", line 268, in load
#    parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
#  File "C:\Program Files (x86)\Python 3\lib\json\__init__.py", line 312, in loads
#    s.__class__.__name__))
#TypeError: the JSON object must be str, not 'bytes'

L'erreur m'indique qu'il veut un str, mais même en faisant json.loads(str(r)) ou des variantes ça ne fonctionne pas …

(Et comme on me la conseiller, j'utiliserai la lib requests, mais je n'ai pas encore eu le temps de l'installer) Merci, WinXaito

+0 -0
Staff

Cette réponse a aidé l'auteur du sujet

Salut.

C'est toute la subtilité de Python 3. Un bytes (chaîne d'octets) n'est pas un str (chaîne unicode). ;)

1
2
3
4
5
>>> import urllib.request
>>> import json
>>> req = urllib.request.urlopen('https://zestedesavoir.com/api/membres/')
>>> req
<http.client.HTTPResponse object at 0x7f3e9bab85f8>

Jusqu'ici, tout va bien, pour lire le contenu du résultat, il faut faire appel à la méthode read de HTTPResponse. Sauf que ce contenu, vu que c'est le résultat d'une requête HTTP, tu ne peux pas savoir a priori si c'est du texte, du HTML, du binaire ou quoi que ce soit, donc il te sera forcément envoyé comme une donnée brute : un objet bytes.

Bon ici en l'occurrence c'est bien du json :

1
2
3
4
5
>>> req.getheader('Content-Type')
'application/json'
>>> data = req.read()
>>> type(data)
<class 'bytes'>

Du coup, comme json.loads n'accepte que des chaînes unicode, il faut décoder cette chaîne d'octet pour la convertir en un str.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> data_str = data.decode()
>>> type(data_str)
<class 'str'>
>>> data_json = json.loads(data_str)
>>> data_json
{'results': [...], 
 'previous': None, 
 'count': 2386, 
 'next': 'http://zestedesavoir.com/api/membres/?page=2'
}

Et voilà le travail. J'ai éludé le résultat pour éviter de pourrir la balise code. :)

Note que la méthode bytes.decode() accepte normalement un argment pour spécifier l'encodage. Ici, on s'est servi de l'encodage par défaut de Python 3 (utf-8).

Édité par nohar

I was a llama before it was cool

+0 -0
Staff

Tu devrais t'intéresser a la lib requests. Elle fait pas partit de la lib standard, mais c'est tout comme (meme pip l'utilise). Elle est typiquement faite pour ce genre de choses. Décoder une requête en JSON elle le fait pour toi en se chargeant des soucis d'encodage. Des que tu veux faire des requêtes web, c'est beaucoup plus facile.

Sinon j'ai déjà commencé une mini lib python pour l'api, je l'ai posté sur le sujet lié aux stats, hésite pas a l'utiliser ou l'améliorer

+0 -0
Staff

Pour compléter un peu ma précédente réponse, voila pourquoi requests est vraiment à conseiller pour ce genre de chose :

1
2
3
4
>>> import requests
>>> r = requests.get('https://zestedesavoir.com/api/membres/')
>>> r.json()
{'count': 2388, 'previous': None, 'next': 'http://zestedesavoir.com/api/membres/?page=2', 'results': ...}

C'est une lib tres bien faite, ultra bien documenté et vraiment efficace. A la pycon 2015 il a encore été question de l'inclure en standard (ce qui est déjà presque le cas puisque pip l'utilise), ce ne sera probablement pas le cas pour qu'ils puissent avoir des mises a jours de sécurité plus rapidement que le cycle de dev de python et parce qu'elle est tellement utilisé que les gens ne veulent pas devoir mettre a jour leur version de python pour profiter des nouvelles versions).

Sinon comme je l'ai dis, j'ai un début de lib client ici avec un exemple d'utilisation ici. Il y a largement à améliorer mais ce serait peut être bien qu'on conjugue les efforts pour faire une lib propre en python.

Édité par Kje

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte