File size: 7,544 Bytes
997d370
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# extract_codex.py
import requests
from bs4 import BeautifulSoup
import json
import time
from datetime import datetime
import os

# URLs des sections du Codex Alimentarius
URLS = {
    "guidelines": "https://www.fao.org/fao-who-codexalimentarius/codex-texts/guidelines/fr/",
    "standards": "https://www.fao.org/fao-who-codexalimentarius/codex-texts/list-standards/fr/",
    "codes_of_practice": "https://www.fao.org/fao-who-codexalimentarius/codex-texts/codes-of-practice/fr/",
    "miscellaneous": "https://www.fao.org/fao-who-codexalimentarius/codex-texts/miscellaneous/fr/"
}

# Mapping des catégories pour les noms de fichiers ou l'identification
CATEGORY_MAP = {
    "guidelines": "Directives (CXG)",
    "standards": "Normes (CXS)",
    "codes_of_practice": "Codes de Pratique (CXC)",
    "miscellaneous": "Divers (CXM)"
}

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36'
}

def extract_table_data(url, category_key):
    """
    Extrait les données d'un tableau spécifique d'une page Codex.
    """
    print(f"Extraction depuis: {url}")
    try:
        response = requests.get(url, headers=HEADERS, timeout=15) # Ajout d'un timeout
        response.raise_for_status()
        soup = BeautifulSoup(response.content, 'html.parser')

        data_lines = []
        # Trouver tous les tableaux
        tables = soup.find_all('table')

        target_table = None
        # Heuristique : trouver le tableau avec le plus de lignes significatives
        max_rows = 0
        for table in tables:
            rows = table.find_all('tr')
            # Compter les lignes avec des cellules de données (td), pas seulement des en-têtes (th)
            data_rows_count = sum(1 for row in rows if row.find('td'))
            if data_rows_count > max_rows:
                max_rows = data_rows_count
                target_table = table

        if not target_table:
            print(f"  Avertissement: Aucun tableau principal trouvé sur {url}")
            return []

        # Extraire les lignes du tableau trouvé
        rows = target_table.find_all('tr')
        # Identifier les en-têtes si nécessaire (optionnel ici car la structure est connue)
        # Nous supposons que la première ligne est l'en-tête et les suivantes sont les données
        # Parcourir les lignes de données
        for row in rows:
            cells = row.find_all(['td', 'th']) # Inclure th au cas où l'en-tête serait dans le tbody
            if len(cells) >= 4: # S'assurer qu'il y a suffisamment de colonnes
                # Extraire le texte de chaque cellule et le nettoyer
                # La structure semble être: Code | Titre | Comité | Année | Status
                code_raw = cells[0].get_text(strip=True) if cells[0] else ""
                title_raw = cells[1].get_text(strip=True) if cells[1] else ""
                committee_raw = cells[2].get_text(strip=True) if cells[2] else ""
                year_raw = cells[3].get_text(strip=True) if cells[3] else ""
                # status_raw = cells[4].get_text(strip=True) if len(cells) > 4 else "" # Optionnel

                # Nettoyer les données extraites
                # Parfois, le code contient aussi le titre, essayons de les séparer si c'est le cas
                # Mais souvent, ils sont dans des cellules séparées. Vérifions simplement.
                code = code_raw
                title = title_raw
                committee = committee_raw
                year = year_raw

                # Vérifier si les données de base sont présentes
                if code and title and committee and year:
                    # Déterminer le préfixe pour la catégorie (CXG, CXS, etc.)
                    prefix_map = {
                        "guidelines": "CXG",
                        "standards": "CXS",
                        "codes_of_practice": "CXC",
                        "miscellaneous": "CXM"
                    }
                    expected_prefix = prefix_map.get(category_key, "CX")
                    # Vérifier si le code commence par le bon préfixe (optionnel mais bon filtre)
                    # Parfois les pages peuvent avoir des données supplémentaires, donc on filtre
                    if code.startswith(expected_prefix) or category_key == "miscellaneous": # Divers peut avoir différents codes
                         data_lines.append({
                             "code": code,
                             "title": title,
                             "committee": committee,
                             "year": year,
                             # "status": status_raw # Ajouter si pertinent
                         })
                    # else:
                    #     print(f"  Debug: Code '{code}' ne commence pas par '{expected_prefix}', ignoré sur {url}.") # Pour debug
                # else:
                #     print(f"  Debug: Ligne ignorée en raison de données manquantes: {code_raw}, {title_raw}, {committee_raw}, {year_raw} sur {url}") # Pour debug

        print(f"  {len(data_lines)} documents extraits de {category_key}.")
        return data_lines

    except requests.exceptions.Timeout:
        print(f"  Erreur: Timeout lors de la requête HTTP pour {url}")
        return []
    except requests.exceptions.RequestException as e:
        print(f"  Erreur lors de la requête HTTP pour {url}: {e}")
        return []
    except Exception as e:
        print(f"  Erreur inattendue lors du parsing de {url}: {e}")
        import traceback
        traceback.print_exc() # Affiche la pile d'appel pour le débogage
        return []


def run_extraction(output_dir="data"):
    """
    Fonction principale pour orchestrer l'extraction et sauvegarder les données.
    """
    print("Démarrage de l'extraction des données du Codex Alimentarius...")
    
    # Créer le dossier de sortie s'il n'existe pas
    os.makedirs(output_dir, exist_ok=True)

    all_data = {}
    total_documents = 0

    for key, url in URLS.items():
        print(f"\n--- Extraction de la catégorie: {CATEGORY_MAP[key]} ---")
        documents = extract_table_data(url, key)
        all_data[key] = documents
        total_documents += len(documents)
        # Pause courte pour être gentil avec le serveur
        time.sleep(1)

    # Sauvegarder les données dans un fichier JSON structuré
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    output_filename = f"codex_data_{timestamp}.json"
    output_path = os.path.join(output_dir, output_filename)
    
    final_output = {
        "extraction_date": datetime.now().isoformat(),
        "total_documents": total_documents,
        "categories": all_data
    }

    try:
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(final_output, f, ensure_ascii=False, indent=4)
        print(f"\n✅ Extraction terminée avec succès!")
        print(f"   {total_documents} documents ont été extraits et sauvegardés dans '{output_path}'.")
        return output_path # Retourner le chemin du fichier créé
    except Exception as e:
        print(f"\n❌ Erreur lors de la sauvegarde du fichier JSON: {e}")
        return None

    # Optionnel: Afficher un résumé
    # print("\n--- Résumé de l'extraction ---")
    # for key, docs in all_data.items():
    #     print(f"  - {CATEGORY_MAP[key]}: {len(docs)} documents")

if __name__ == "__main__":
    # Si le script est exécuté directement
    run_extraction()