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