Automatisation de l'extraction de données des modèles IRI2012 et NRLMSISE avec Python

L'extraction de données scientifiques à partir des interfaces web de la NASA nécessite la simulation de requêtes HTTP POST. Les modèles IRI2012 (ionosphère) et NRLMSISE00 (thermosphère) exposent des formulaires permettnat de générer des profils atmosphériques en fonction de paramètres spatio-temporels spécifiques tels que la date, l'heure, la latitude, la longitude et l'altitude.

Pour automtaiser la récupération de ces données sur de multiples plages de valeurs, il est nécessaire d'itérer sur les paramètres cibles et de soumettre les formulaires programmatiquement. L'approche technique repose sur la bibliothèque requests de Python. Afin d'optimiser les performances réseau et d'assurer la robustesse du script, l'implémentation utilise requests.Session pour le maintien des connexions TCP, le module pathlib pour une gestion sécurisée et multiplateforme des chemins de fichiers, ainsi qu'un mécanisme de reprise sur erreur (retry) borné.

Récupération des données du modèle IRI2012

Le premier script interagit avec l'interface du modèle IRI2012. Il est configuré pour extraire des paramètres tels que la densité électronique et les températures des particules (Ne, Tn, Ti, Te). Le code parcourt une plage définie pour un paramètre choisi (par exemple, la longitude) et sauvegarde la réponse brute du serveur dans un fichier texte.


import requests
import time
from pathlib import Path

def fetch_iri2012_data():
    target_dimension = 'longitude'
    api_endpoint = 'https://omniweb.gsfc.nasa.gov/cgi/vitmo/vitmo_model.cgi'
    
    # Configuration du chemin de sortie avec pathlib
    output_directory = Path(__file__).parent
    output_filepath = output_directory / f"iri2012_raw_{target_dimension}.txt"
    
    # Utilisation d'une session pour le pooling de connexions
    http_session = requests.Session()
    http_session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Referer': 'https://omniweb.gsfc.nasa.gov/vitmo/iri2012_vitmo.html',
        'Content-Type': 'application/x-www-form-urlencoded'
    })

    # Paramètres de base du formulaire POST
    form_payload = {
        'model': 'iri_2012',
        'year': '2023',
        'month': '12',
        'day': '01',
        'time_flag': '1',
        'hour': '8',
        'geo_flag': '0.',
        'latitude': '50.',
        'longitude': '40.',
        'height': '100.',
        'profile': '1',
        'start': '100.',
        'stop': '1000.',
        'step': '50.',
        'sun_n': '',
        'ion_n': '',
        'radio_f': '',
        'format': '0',
        'vars': ['17', '19', '20', '21'], # Ne, Tn, Ti, Te
        'linestyle': 'solid'
    }

    # Dictionnaire des plages de valeurs itérables
    dimension_ranges = {
        'hour': range(0, 24),
        'month': range(1, 13),
        'latitude': range(-90, 91, 10),
        'longitude': range(0, 360, 10)
    }

    with open(output_filepath, 'w', encoding='utf-8') as data_file:
        for current_value in dimension_ranges[target_dimension]:
            form_payload[target_dimension] = str(current_value)
            data_file.write(f"\n#======== {target_dimension}: {current_value} ========\n")
            
            max_retries = 3
            attempt = 0
            
            while attempt < max_retries:
                try:
                    print(f"Requête en cours pour {target_dimension}={current_value} (Tentative {attempt + 1})")
                    response = http_session.post(api_endpoint, data=form_payload)
                    response.raise_for_status()
                    data_file.write(response.text)
                    break # Succès, sortie de la boucle de retry
                except requests.exceptions.RequestException as network_error:
                    print(f"Erreur réseau détectée : {network_error}")
                    attempt += 1
                    time.sleep(2) # Pause avant la prochaine tentative
            
            data_file.write(f"\n#-------- Fin {target_dimension}: {current_value} --------\n")

if __name__ == "__main__":
    fetch_iri2012_data()

Récupération des données du modèle NRLMSISE00

Le second script s'adresse au modèle NRLMSISE00. La structure logique demeure identique, mais l'URL cible et les variables du formulaire (payload) sont modifiées pour extraire les concentrations de gaz neutres, notamment l'oxygène atomique (O), les molécules d'azote (N2) et d'oxygène (O2).


import requests
import time
from pathlib import Path

def fetch_nrlmsise_data():
    target_dimension = 'latitude'
    api_endpoint = 'https://ccmc.gsfc.nasa.gov/cgi-bin/modelweb/models/vitmo_model.cgi'
    
    output_directory = Path(__file__).parent
    output_filepath = output_directory / f"nrlmsise_raw_{target_dimension}.txt"
    
    http_session = requests.Session()
    http_session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
        'Referer': 'https://ccmc.gsfc.nasa.gov/modelweb/models/nrlmsise00.php',
        'Content-Type': 'application/x-www-form-urlencoded'
    })

    form_payload = {
        'model': 'nrlmsise',
        'year': '2023',
        'month': '12',
        'day': '01',
        'time_flag': '1',
        'hour': '12',
        'geo_flag': '0.',
        'latitude': '60',
        'longitude': '120',
        'height': '100.',
        'profile': '1',
        'start': '60.',
        'stop': '1000.',
        'step': '10.',
        'f10_7': '',
        'f10_7_3': '',
        'ap': '',
        'format': '0',
        'vars': ['08', '09', '10'], # O, N2, O2
        'linestyle': 'solid'
    }

    dimension_ranges = {
        'hour': range(0, 24),
        'month': range(1, 13),
        'latitude': range(-90, 91, 10),
        'longitude': range(0, 360, 10)
    }

    with open(output_filepath, 'w', encoding='utf-8') as data_file:
        for current_value in dimension_ranges[target_dimension]:
            form_payload[target_dimension] = str(current_value)
            data_file.write(f"\n#======== {target_dimension}: {current_value} ========\n")
            
            max_retries = 3
            attempt = 0
            
            while attempt < max_retries:
                try:
                    print(f"Requête en cours pour {target_dimension}={current_value} (Tentative {attempt + 1})")
                    response = http_session.post(api_endpoint, data=form_payload)
                    response.raise_for_status()
                    data_file.write(response.text)
                    break
                except requests.exceptions.RequestException as network_error:
                    print(f"Erreur réseau détectée : {network_error}")
                    attempt += 1
                    time.sleep(2)
            
            data_file.write(f"\n#-------- Fin {target_dimension}: {current_value} --------\n")

if __name__ == "__main__":
    fetch_nrlmsise_data()

Étiquettes: Python requests web-scraping data-extraction http-post

Publié le 30 juin à 02h45