Spaces:
Sleeping
Sleeping
| # 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() | |