Filtrage JSON avec Python

Fermé
Lynow - Modifié le 29 juin 2022 à 11:02
mamiemando Messages postés 33410 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 2 décembre 2024 - 30 juin 2022 à 12:36
Bonjour,

J'essaye, malgré mon peu d'expériences en python, de créer un script simple permettant de faire des appels API sur outils de collecte de données, ne trouvant pas la fonctionnalité que je souhaite dans la documentation.

Les données reçues sont au format JSON, je reçois en fait plusieurs "Observables" correspondants à différentes dates, descriptions, etc.

L'idée est de pouvoir récupérer l'id de chaque Observables, ensuite de faire un second appel API pour ajouter un "tag" approprié. Cet ajout de tag se fait facilement avec l'API.

Mon problème est la lecture du JSON, voici un début de code :

import requests
from requests.structures import CaseInsensitiveDict
import json
import re

url = "https://xxx.xxx.xxx.xxx/attributes/restSearch"

headers = CaseInsensitiveDict()
headers["Authorization"]="xxxxx"
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/json"
data = '{"event_timestamp":"5d"}'

requete = requests.post(url, headers=headers, data=data, verify=False)
reponse = requete.json()

# exemple
obj = '{response: {"Attribute": [{"category": "Network activity"}]}}'

dict = json.loads(obj)



En fait, je reçois un format tel que la variable "obj", avec beaucoup plus de valeurs. J'ai lu qu'il fallait convertir le JSON en Python, cependant avec loads je reçois des erreurs de string car en effet le JSON reçu n'a pas guillemets (donc lorsque je teste loads avec "reponse")

Est-ce qu'il y a une meilleure méthode que celle-ci ?

L'idée ensuite est de parcourir le dictionnaire :


for key in dict:
      
       # et ensuite de récupérer la valeur id pour chaque clés différentes, je ne sais pas si c'est clair
       id = reponse["response"]["Attribute"][0]["event_id"]

3 réponses

mamiemando Messages postés 33410 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 2 décembre 2024 7 808
29 juin 2022 à 11:21
Bonjour,

On ne peut pas reproduire ton problème avec le code partagé (l'URL de l'API est offusquée). S'il s'agit d'une API publique, tu peux l'indiquer et on pourra tester ton script.

Autrement, il faudrait joindre un exemple de json que tu obtiens. S'il est bien formé tu devrais pouvoir utiliser
json.loads
.

Bonne chance
0
Bonjour,

En effet je ne peux pas donner accès à l'AIP, voici donc un exemple de réponse que je peux avoir après le code
reponse = requete.json()
:

python script2.py 

/usr/lib/python2.7/dist-packages/urllib3/connectionpool.py:849: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)

{u'response': {u'Attribute':[{u'category': u'Network activity', u'comment': u'', u'first_seen': None, u'uuid': u'fabdc862-xxxxxx-xxxxxx', u'event_id': u'1484', u'timestamp': u'1656322152', u'to_ids': False, u'deleted': False, u'object_id': u'0', u'Event': {u'info': u'Event test 11', u'orgc_id': u'29', u'uuid': u'8e6b9b8a-xxxxxx-xxxxxxx', u'org_id': u'2', u'distribution': u'1', u'id': u'1484'}, u'sharing_group_id': u'0', u'Tag': [{u'colour': u'#856c13', u'numerical_value': None, u'local': False, u'id': u'171', u'name': u'Phishing'}], u'value': u'192.168.1.3', u'disable_correlation': False, u'distribution': u'0', u'object_relation': None, u'type': u'ip-src', u'id': u'278236', u'last_seen': None}]}}

0
mamiemando Messages postés 33410 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 2 décembre 2024 7 808 > Lynow
Modifié le 29 juin 2022 à 11:54
La valeur retournée dans
reponse
est déjà un objet python, et donc il n'y a pas besoin de faire de
json.loads
.

Exemple :

reponse = {u'response': {u'Attribute':[{u'category': u'Network activity', u'comment': u'', u'first_seen': None, u'uuid': u'fabdc862-xxxxxx-xxxxxx', u'event_id': u'1484', u'timestamp': u'1656322152', u'to_ids': False, u'deleted': False, u'object_id': u'0', u'Event': {u'info': u'Event test 11', u'orgc_id': u'29', u'uuid': u'8e6b9b8a-xxxxxx-xxxxxxx', u'org_id': u'2', u'distribution': u'1', u'id': u'1484'}, u'sharing_group_id': u'0', u'Tag': [{u'colour': u'#856c13', u'numerical_value': None, u'local': False, u'id': u'171', u'name': u'Phishing'}], u'value': u'192.168.1.3', u'disable_correlation': False, u'distribution': u'0', u'object_relation': None, u'type': u'ip-src', u'id': u'278236', u'last_seen': None}]}}
obj = reponse["response"]["Attribute"][0]
print(obj["id"]) # 278236


Bonne chance
0
Lynow > mamiemando Messages postés 33410 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 2 décembre 2024
Modifié le 29 juin 2022 à 15:20
D'accord, je comprends mieux le problème.

Cela a l'air de fonctionner. Ensuite pour parcourir le json j'ai pensé à ceci :

nombre_attribut = (len(reponse["response"]["Attribute"]))

for i in range(nombre_attribut):
      print(reponse["response"]["Attribute"][i])


Je récupère le nombre d'attributs du JSON pour ensuite faire une boucle for afin d'ajouter chaque ids dans une nouvelle liste (je n'ai pas encore écrit cette nouvelle liste).
0
mamiemando Messages postés 33410 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 2 décembre 2024 7 808
Modifié le 29 juin 2022 à 15:32
Bonjour,

Je ne vois pas trop l'intérêt de transformer ton dictionnaire sous forme de liste, mais pourquoi pas :-)

En tout cas, l'opérateur
[]
n'a pas le même sens selon que tu l'appliques à une liste ou un dictionnaire :
  • pour une liste, il faut passer un entier (entre 0 et la longueur de la liste - 1) qui indique quel élément on prend ;
  • pour un dictionnaire, il faut passer la clé (un type hashable, dans ton cas de sont des chaînes de caractères) qui indique quel valeur on récupère.
  • sans variable à sa gauche, il permet de créer une liste à la volée (e.g.,
    [1, 2, 3]
    )


Le message d'erreur
TypeError: list indices must be integers, not str
signifie que tu as passé une chaîne de caractère (au lieu d'un index) à un opérateur
[]
appliqué à une liste.

Maintenant regardons ce que tu as écrit :
["response"]["Attribute"][i-1]
signifie :
  • crée une liste contenant la chaîne
    "response"
    ;
  • récupère dans cette liste l'élément identifié par la clé
    "Attribute"
    (ça n'a pas de sens, puisqu'il faudrait passé un index, ici forcément
    0
    puisque ta liste n'a qu'un élément)
  • dans l'objet retourné (que ton code espère de type liste), récupère l'élément
    i - 1
    .


Il y beaucoup de choses qui ne vont pas :
  • tout d'abord, si je reprends mes notations, tu veux probablement plutôt écrire
    reponse["response"]["Attribute"][i-1]
    (et dans ce cas, reponse étant un dictionnaire, la suite à plus de sens)
  • ensuite tu veux récupérer l'élément
    i-1
    , or dans ta boucle
    for i in range(nombre_attribut)
    , la première valeur prise par
    i
    sera
    -1
    (pour rappel, le premier élément d'une liste est à l'index
    0
    ). Le programme marcherait car en python, l'élément
    -k
    signifie prend le k-ième élement en partant de la fin, donc ça ferait ce qu'il faut, mais ce n'est sans doute pas ce que tu voulais écrire.


Il serait donc plus naturel d'écrire :

liste = reponse["response"]["Attribute"]
for i in range(len(liste)):
    print(list[i])


... ou plus simplement :

liste = reponse["response"]["Attribute"]
for element in liste:
    print(element)


... ou encore plus simple :

liste = reponse["response"]["Attribute"]
print(liste)


... ou pour plus de lisibilité :

from pprint import pprint

liste = reponse["response"]["Attribute"]
pprint(liste)


Bonne chance
0
Merci pour ces explications, j'avais corrigé mon erreur plus haut en éditant mon message, mais cela devient d'autant plus clair avec ton message.

J'ai modifié mon script et m'occupe maintenant uniquement des évènements et non pas des attributs (pour une raison interne à l'outil).

A savoir que maintenant je considère que "reponse" contient plusieurs évènements.

Voici mon code actuel :

import requests
from requests.structures import CaseInsensitiveDict
import json
import re

#################################

### APPEL API ###

url = "https://xxxx.xxxx.xxxx.xxxx/events/index"
headers = CaseInsensitiveDict()
headers["Authorization"]="xxxxxxxxxxxxxxxxxxxxxxx"
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/json"
data = '{"event_timestamp":"20d"}'
requete = requests.post(url, headers=headers, data=data, verify=False)

###

### Recuperation au format json et en tant qu'object python ###

reponse = requete.json()

###

### Recuperation des IDs events ###

nombre_attribut = (len(reponse))
liste=[]

for i in range(nombre_attribut):
      obj = reponse[i]
      liste.append(obj["id"])

nombre_id = len(liste)

###

### Ajout des tags aux events concernes ###

for j in range(nombre_id):

     url = "https://xxxx.xxxx.xxxx.xxxx/events/addTag"
     headers = CaseInsensitiveDict()
     headers["Authorization"]="xxxxxxxxxxxxxxxxxxxxxxxxx"
     headers["Accept"] = "application/json"
     headers["Content-Type"] = "application/json"
     data = '{"id":liste[j],"tag":"Test"}'

     print(liste[j])

     requete = requests.post(url, headers=headers, data=data, verify=False)



D'après mon print ligne 50, j'ai bien tous les ids qui sont dans la liste. Pourtant, aucun tag n'est ajouté après vérification sur l'outil web.

J'ai donc remplacé cette ligne :
   data = '{"id":liste[j],"tag":"Test"}'
en mettant par exemple :
data = '{"id":1489,"tag":"Test"}'
, ou encore :
 data = '{"id":1490,"tag":"Test"}'


Et cela fonctionne ! Je ne comprends pas
0
Lynow > Lynow
29 juin 2022 à 17:22
Ok, le problème est ligne 48, il ne comprends pas le
list[j]
, en effet il est dans une chaîne de caractère, comment lui indiquer que ce n'en est pas une ?
0
Lynow > Lynow
29 juin 2022 à 17:55
J'ai répondu à ma question en mettant :
data = {"id":liste[j],"tag":"Test"}


Et pour mettre dans le bon format :

data = jason.dumps(data)
0
mamiemando Messages postés 33410 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 2 décembre 2024 7 808
Modifié le 30 juin 2022 à 12:36
Bonjour

#7

Si tu écris :

data = '{"id":liste[j],"tag":"Test"}' 


... alors
data
est une chaîne (ce n'est même pas du JSON à cause de liste[j]) et donc tu perds les bénéfices apportés par une structure python (en l'occurrence un dictionnaire). Donc il faudrait plutôt écrire

data = {"id":liste[j],"tag":"Test"}


... et ensuite rien ne t'empêche de faire :

print(json.dumps(data))


#9

À mon avis tu devrais distinguer dans le nommage data (la structure de donnée python) et la chaîne de caractère JSON qui lui correspond (tu peux l'appeler
s_json
par exemple).

Bonne chance
0