Enrichir avec Python le contenu d'un fichier Excel avec des données d’un Webservice

Enrichir avec Python le contenu d'un fichier Excel avec des données d’un Webservice

Lundi, 2 Décembre 2024 Python Webservice

Python est un langage formidable qui permet très facilement de créer de petits outils pour interroger des API ou lire des données. Je vous propose de réaliser un petit outil pour valoriser une liste de vieux 45 tours grâce à un fichier Excel et l’API de Discogs.

L’idée est simple : créer un fichier Excel avec le nom des artistes et les titres puis interroger la marketplace de Discogs pour trouver leur prix le plus bas.

Les pre-requis, pour faire ceci, sont :

  1. Python
  2. Excel pour créer un fichier initial
  3. Un compte Discogs et une clef API
  4. La lib Spire.Xls pour lire et écrire dans un fichier Excel

Voici notre fichier Excel initial :

Artiste, titre et un tableau vide

J’ai trouvé tout ce dont j’avais besoin pour lire et écrire un fichier Excel en Python avec Spire.Xls là : https://medium.com/@alice.yang_10652/read-data-from-excel-files-in-python-a-comprehensive-guide-bbf91d38d7c5. Attention, Spire.Xls n’est pas une lib gratuite (et est même très chère pour un usage perso). Elle fonctionne toutefois en mode évaluation avec un onglet d’alerte.

En ce qui concerne l’API Discogs, nous allons directement l’interroger (sans utiliser le client Python), via l’url de recherche de l’api : https://api.discogs.com/database/search . La documentation de l'API est très bien faite même si il m'a fallu un peu de temps pour en maitriser les subtilités !

Nous allons effectuer une recherche sur le format (Vinyl+Single), le pays (tous mes vinyls proviennent de France) et une recherche texte simple utilisant l'artiste et le titre. Pour améliorer les résultats, les espaces et les caractères spéciaux seront remplacés par des « + ». Les "lettres seules" seront aussi supprimées. 

Dans le petit script ci dessous, c'est le rôle de la fonction "prepare_texte"

Une fois le 45 tours trouvé avec la recherche de Discogs, nous irons chercher les infos relatives au « master » du premier résultat. Le master est une entité « fictive » crée par Discogs pour regrouper ensemble toutes les éditions d’un même contenu. L’url API du master est présente dans le résultat de la recherche !

from spire.xls import *
from spire.xls.common import *
import requests 
import re
import time

DISCOGS_TOKEN = [Coller ici votre clef API]

def recherche_Discogs(IN_artiste, IN_titre):

    # valeurs de retours
    ret_45t_infos = {'prix' : '', 'annee' : '', 'master_id' : '', 'master_uri' : '', 'genre' : '', 'style' : '', 'master_api_url' : ''}   
    # preparation des parametres : 
    params_request = {}
    params_request["token"] = DISCOGS_TOKEN 
    params_request["format"] = 'Vinyl+Single' 
    params_request["country"] = 'France' 
    params_request["q"] = prepare_texte(IN_artiste) + '+' + prepare_texte(IN_titre) 

    # on est pret : on interoge discogs !!!
    tmp_response = requests.get("https://api.discogs.com/database/search", params = params_request)
    # si le webservice retourne une erreur 429, c'est qu'on a fait trop d'interogarteion en peu de temps ... on attend un peu et on recommence :)
    while(str(tmp_response) == "<Response [429]>"): 
            time.sleep(10)
            tmp_response = requests.get("https://api.discogs.com/database/search", params = params_request) 

    infos = tmp_response.json() # recuepre la reponse JSON du webservice
    resultats = infos.get("results") # recupere la branche des resultats 

    if len(resultats)<1: return ret_45t_infos
    # on a plein de resultats mais on va se focaliser sur le 1er et recuperer l'url vers le "master" dans la reponse :
    if('master_url' in resultats[0]): ret_45t_infos['master_api_url'] = str(resultats[0]['master_url'])  	

    # maintenant, on va regarder dans le "master", le prix du disque !!!
    tmp_response_master = requests.get(ret_45t_infos['master_api_url'])
    # si le webservice retourne une erreur 429, c'est qu'on a fait trop d'interogarteion en peu de temps .... on attend un peu et on recommence :)
    while(str(tmp_response_master) == "<Response [429]>"): 
            time.sleep(10)
            tmp_response_master = requests.get(ret_45t_infos['master_api_url'])
    master_infos = tmp_response_master.json() # recuepre la reponse JSON du webservice 
    if('lowest_price' in master_infos): ret_45t_infos['prix'] = str(master_infos['lowest_price']) 
    if('year' in master_infos): ret_45t_infos['annee'] = str(master_infos['year'])
    if('id' in master_infos): ret_45t_infos['master_id'] = str(master_infos['id'])

    if('uri' in master_infos): ret_45t_infos['master_uri'] = str(master_infos['uri']) 
    if('genres' in master_infos): ret_45t_infos['genre'] = str('/'.join(list(master_infos['genres'])))
    if('styles' in master_infos): ret_45t_infos['style'] = str('/'.join(list(master_infos['styles'])))
    
    return ret_45t_infos
            
def prepare_texte(IN_texte):
    # pour ameliorer al eprformance de la recherche, nous allons nous simpler le travail en supprimant tout ce qui n'est pas alphanumerique, ainsi que les caracteres seuls.
    OUT_texte = re.sub('[^0-9a-zA-Z]+', ' ', IN_texte) # remplace par des espaces tout ce qui n'est pas alpha numerique
    arr_texte = OUT_texte.split(" ") # decoupe les mots en tableau
    OUT_texte = '+'.join([mot for mot in arr_texte if len(mot) >= 2]) # supprime tous les caracetres seuls et concatene ce qui reste avec des +
    # Cette methode etant un peu "agressive", si le resultat est trop court (moins de 3 caracteres), on est moins brutal en gardant tout
    if len(OUT_texte)<3: OUT_texte = '+'.join(arr_texte)  

    return OUT_texte

# ouvre le classeur Excel contenant les artistes et titres des 45 tours
ClasseurXLS = Workbook()
ClasseurXLS.LoadFromFile("Classeur1.xlsx")

# recupere le premier onglet
ongletXLS1 = ClasseurXLS.Worksheets[0]

# balaye toute les valeurs de la colonne Artiste  
cellRangeCollection = ongletXLS1.Columns[0].Cells
for cellRange in cellRangeCollection:
    # la premiere ligne ne nous interese pas 
    if(cellRange.Row==1): continue
    # recupere l'artiste et le titre (la celle à droite de l'artiste)
    artiste = cellRange.Value
    # si la cellule est vide, on s'arrete !
    if(artiste.strip()==""): break
    titre = ongletXLS1[cellRange.Row,cellRange.Column + 1].Value   
    recherche_discogs = recherche_Discogs(artiste,titre)
    ongletXLS1[cellRange.Row,cellRange.Column + 2].Text = recherche_discogs['annee']
    ongletXLS1[cellRange.Row,cellRange.Column + 3].Text = recherche_discogs['genre']
    ongletXLS1[cellRange.Row,cellRange.Column + 4].Text = recherche_discogs['style'] 
    ongletXLS1[cellRange.Row,cellRange.Column + 5].Text = recherche_discogs['prix']  
    urlLink = ongletXLS1.HyperLinks.Add(ongletXLS1[cellRange.Row,cellRange.Column + 6])
    urlLink.Type = HyperLinkType.Url
    urlLink.TextToDisplay = recherche_discogs['master_id']
    urlLink.Address = recherche_discogs['master_uri']
 
 
#on enresitre le resultat dans un ficheir Excel !!!
ClasseurXLS.SaveToFile("Classeur_resultat.xlsx", ExcelVersion.Version2016)
# on a terminé !
ClasseurXLS.Dispose()
  
    

Une fois terminé, un nouveau fichier apparait avec les infos complétés !!!

Conclusion, c'est assez simple à mettre en œuvre et, si on exclut mes tests avec l'API Discogs, un couple d'heure m'a suffit pour réaliser ceci (sans connaitre l'API Spire.Xls). 

On peut par contre aussi voir que les prix remontés par l'API Discogs sont franchement farfelus ! Heureusement que je ne souhaite pas vendre mes précieux disques car à ces prix-là, ce serait un sacrilège !  

Pas de commentaire encore
Recherche