MMOON commited on
Commit
a2dcdda
·
verified ·
1 Parent(s): df42b73

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +114 -83
src/streamlit_app.py CHANGED
@@ -1,131 +1,162 @@
1
- # codex_simple_extractor.py
2
  import requests
3
  from bs4 import BeautifulSoup
4
  import re
5
  import time
 
 
6
 
7
- # URL de la page à scraper
8
- url = "https://www.fao.org/fao-who-codexalimentarius/codex-texts/codes-of-practice/fr/"
9
-
10
  # Entête pour simuler un navigateur
11
- headers = {
12
- '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'
13
  }
 
 
 
 
 
14
 
15
- print(f"Tentative d'extraction depuis: {url}")
 
 
 
 
 
 
 
16
 
17
- try:
18
- # 1. Récupérer la page web
19
- response = requests.get(url, headers=headers, timeout=30)
20
- response.raise_for_status() # Lève une exception si le statut est une erreur (4xx, 5xx)
21
- print(f"Page récupérée avec succès. Statut: {response.status_code}")
 
 
 
22
 
23
- # 2. Analyser le contenu HTML
24
- soup = BeautifulSoup(response.content, 'html.parser')
25
- print("Analyse HTML terminée.")
 
26
 
27
- # 3. Trouver tous les tableaux
28
- tables = soup.find_all('table')
29
- print(f"Nombre de tableaux trouvés sur la page: {len(tables)}")
 
30
 
31
- documents = []
32
- seen_codes = set() # Pour éviter les doublons
 
33
 
34
- if tables:
35
- print("Analyse des tableaux pour trouver les documents...")
36
- # Parcourir chaque tableau
37
- for i, table in enumerate(tables):
38
- # print(f" Analyse du tableau {i+1}...")
 
 
39
  rows = table.find_all('tr')
 
 
40
  for row in rows:
41
  cells = row.find_all(['td', 'th']) # Inclure th au cas où
42
- # Un document valide a généralement au moins 4 cellules
 
43
  if len(cells) >= 4:
44
- # Extraire le texte de chaque cellule
45
  cell_texts = [cell.get_text(strip=True) for cell in cells]
46
 
47
- # Essayer de trouver un code Codex CXC dans la première cellule
 
48
  code_candidate = cell_texts[0] if cell_texts else ""
49
- # Pattern pour CXC suivi d'un numéro (ex: CXC 80-2020, CXC 43R-1995)
50
- code_match = re.match(r'^(CXC)\s*([\w\-R]*\d+(?:-\d+)?)$', code_candidate)
 
51
 
52
  if code_match:
53
  prefix = code_match.group(1)
54
  number_part = code_match.group(2)
55
  full_code = f"{prefix} {number_part}"
56
 
 
57
  if full_code not in seen_codes:
58
  seen_codes.add(full_code)
 
59
 
60
- # Extraire les autres informations
61
  title = cell_texts[1] if len(cell_texts) > 1 else "Titre non trouvé"
 
 
62
  committee = cell_texts[2] if len(cell_texts) > 2 else "COMITE"
 
 
63
  year_str = cell_texts[3] if len(cell_texts) > 3 else ""
64
  try:
65
  year = int(year_str) if year_str.isdigit() else 0
66
  except ValueError:
67
- year = 0
68
 
 
69
  documents.append({
70
  'code': full_code,
71
  'title': title,
72
  'committee': committee,
73
  'year': year
74
  })
75
- print(f"Extraction terminée. Documents trouvés via analyse de tableau: {len(documents)}")
76
- else:
77
- print("Aucun tableau trouvé. Tentative d'extraction via le texte brut...")
78
- # Méthode de secours: Parser le texte brut
79
- text_content = soup.get_text()
80
- # Pattern pour extraire les documents dans le texte brut (format | CODE | Titre | Comité | Année |)
81
- pattern = r'\|\s*(CXC)\s*([\w\-R]*\d+(?:-\d+)?)\s*\|\s*([^|]+?)\s*\|\s*([A-Z0-9]{2,15})\s*\|\s*(\d{4})'
82
- matches = re.findall(pattern, text_content, re.DOTALL)
83
 
84
- for match in matches:
85
- prefix, number_part, title, committee, year_str = match
86
- full_code = f"{prefix} {number_part}"
87
- title = title.strip()
88
- committee = committee.strip()
89
- try:
90
- year = int(year_str.strip())
91
- except ValueError:
92
- year = 0
 
 
 
 
93
 
94
- if full_code not in seen_codes:
95
- seen_codes.add(full_code)
96
- documents.append({
97
- 'code': full_code,
98
- 'title': title,
99
- 'committee': committee,
100
- 'year': year
101
- })
102
- print(f"Extraction terminée. Documents trouvés via analyse de texte brut: {len(documents)}")
 
 
 
103
 
 
 
 
 
 
 
 
 
104
 
105
- # 4. Afficher les résultats
106
- if documents:
107
- print("\n--- Documents Extraits (5 premiers) ---")
108
- # Trier par année décroissante
109
- documents.sort(key=lambda x: x['year'], reverse=True)
110
- for doc in documents[:5]:
111
- print(f" - {doc['code']} | {doc['title'][:50]}... | {doc['committee']} | {doc['year']}")
112
- print(f"\n--- Nombre Total de Documents Extraits: {len(documents)} ---")
113
-
114
- # Optionnel: Sauvegarder dans un fichier
115
- # with open("codes_of_practice_simple.txt", "w", encoding='utf-8') as f:
116
- # for doc in documents:
117
- # f.write(f"{doc['code']} | {doc['title']} | {doc['committee']} | {doc['year']}\n")
118
- # print("Résultats sauvegardés dans 'codes_of_practice_simple.txt'")
119
-
120
- else:
121
- print("\nAucun document n'a pu être extrait.")
122
- # Afficher un échantillon du texte pour débogage
123
- print("\n--- Échantillon du texte de la page (1000 premiers caractères) ---")
124
- print(soup.get_text()[:1000])
125
- print("--- Fin de l'échantillon ---")
126
 
 
127
 
128
- except requests.exceptions.RequestException as e:
129
- print(f"Erreur lors de la requête HTTP : {e}")
130
- except Exception as e:
131
- print(f"Une erreur inattendue s'est produite : {e}")
 
 
1
+ # codex_cxc_extractor_final.py
2
  import requests
3
  from bs4 import BeautifulSoup
4
  import re
5
  import time
6
+ import csv
7
+ from datetime import datetime
8
 
9
+ # --- Configuration ---
10
+ # URL de la page à scraper pour les Codes de Pratique
11
+ URL = "https://www.fao.org/fao-who-codexalimentarius/codex-texts/codes-of-practice/fr/"
12
  # Entête pour simuler un navigateur
13
+ HEADERS = {
14
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
15
  }
16
+ # Timeout pour la requête (en secondes)
17
+ TIMEOUT = 30
18
+ # Nom du fichier de sortie
19
+ OUTPUT_FILENAME = f"codex_cxc_documents_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
20
+ # --- Fin Configuration ---
21
 
22
+ def extract_cxc_documents():
23
+ """
24
+ Fonction principale pour extraire les Codes de Pratique (CXC).
25
+ """
26
+ print(f"--- Démarrage de l'extraction depuis : {URL} ---")
27
+
28
+ documents = []
29
+ seen_codes = set() # Pour éviter les doublons
30
 
31
+ try:
32
+ # 1. Récupérer la page web
33
+ print("1. Connexion au site Codex...")
34
+ start_time = time.time()
35
+ response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
36
+ response.raise_for_status() # Lève une exception pour les codes d'erreur 4xx/5xx
37
+ end_time = time.time()
38
+ print(f" -> Page récupérée avec succès (Statut: {response.status_code}) en {end_time - start_time:.2f} secondes.")
39
 
40
+ # 2. Analyser le contenu HTML
41
+ print("2. Analyse du contenu HTML...")
42
+ soup = BeautifulSoup(response.content, 'html.parser')
43
+ print(" -> Analyse HTML terminée.")
44
 
45
+ # 3. Trouver les tableaux
46
+ print("3. Recherche des tableaux dans la page...")
47
+ tables = soup.find_all('table')
48
+ print(f" -> Nombre de tableaux trouvés : {len(tables)}")
49
 
50
+ if not tables:
51
+ print(" -> Aucun tableau trouvé. Arrêt de l'extraction.")
52
+ return documents # Retourne une liste vide
53
 
54
+ # 4. Parcourir les tableaux pour trouver les documents
55
+ print("4. Analyse des tableaux pour extraire les documents CXC...")
56
+ documents_found_in_tables = 0
57
+
58
+ # Parcourir chaque tableau trouvé
59
+ for table_index, table in enumerate(tables):
60
+ # print(f" -> Analyse du tableau {table_index + 1}...")
61
  rows = table.find_all('tr')
62
+
63
+ # Parcourir chaque ligne du tableau
64
  for row in rows:
65
  cells = row.find_all(['td', 'th']) # Inclure th au cas où
66
+
67
+ # Vérifier s'il y a suffisamment de cellules (au moins 4: code, titre, comité, année)
68
  if len(cells) >= 4:
69
+ # Extraire le texte brut de chaque cellule
70
  cell_texts = [cell.get_text(strip=True) for cell in cells]
71
 
72
+ # --- Extraction des données ---
73
+ # 1. Code (de la première cellule)
74
  code_candidate = cell_texts[0] if cell_texts else ""
75
+ # Pattern regex pour identifier les codes CXC (ex: CXC 80-2020, CXC 43R-1995)
76
+ # Amélioration: Gère mieux les tirets et 'R'
77
+ code_match = re.match(r'^(CXC)\s+([\w\-R]*\d+(?:-\d+)?[R]?)$', code_candidate)
78
 
79
  if code_match:
80
  prefix = code_match.group(1)
81
  number_part = code_match.group(2)
82
  full_code = f"{prefix} {number_part}"
83
 
84
+ # Éviter les doublons
85
  if full_code not in seen_codes:
86
  seen_codes.add(full_code)
87
+ documents_found_in_tables += 1
88
 
89
+ # 2. Titre (de la deuxième cellule)
90
  title = cell_texts[1] if len(cell_texts) > 1 else "Titre non trouvé"
91
+
92
+ # 3. Comité (de la troisième cellule)
93
  committee = cell_texts[2] if len(cell_texts) > 2 else "COMITE"
94
+
95
+ # 4. Année (de la quatrième cellule)
96
  year_str = cell_texts[3] if len(cell_texts) > 3 else ""
97
  try:
98
  year = int(year_str) if year_str.isdigit() else 0
99
  except ValueError:
100
+ year = 0 # Valeur par défaut si l'année n'est pas valide
101
 
102
+ # Ajouter le document à la liste
103
  documents.append({
104
  'code': full_code,
105
  'title': title,
106
  'committee': committee,
107
  'year': year
108
  })
109
+ # print(f" Document trouvé : {full_code}")
 
 
 
 
 
 
 
110
 
111
+ print(f" -> Extraction terminée. Documents trouvés via analyse de tableau : {documents_found_in_tables}")
112
+
113
+ # 5. Afficher les résultats
114
+ if documents:
115
+ print(f"\n--- Résumé de l'extraction ---")
116
+ print(f"Nombre total de documents CXC extraits : {len(documents)}")
117
+
118
+ # Trier par année (décroissante) puis par code
119
+ documents.sort(key=lambda x: (-x['year'], x['code']))
120
+
121
+ print("\n--- 10 premiers documents extraits ---")
122
+ for i, doc in enumerate(documents[:10]):
123
+ print(f" {i+1}. {doc['code']} | {doc['title'][:60]}... | {doc['committee']} | {doc['year']}")
124
 
125
+ # 6. Sauvegarder dans un fichier CSV
126
+ print(f"\n--- Sauvegarde des données ---")
127
+ try:
128
+ with open(OUTPUT_FILENAME, 'w', newline='', encoding='utf-8') as csvfile:
129
+ fieldnames = ['code', 'title', 'committee', 'year']
130
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=';') # ';' pour compatibilité Excel FR
131
+ writer.writeheader()
132
+ for doc in documents:
133
+ writer.writerow(doc)
134
+ print(f" -> Données sauvegardées dans '{OUTPUT_FILENAME}'")
135
+ except Exception as e:
136
+ print(f" -> Erreur lors de la sauvegarde du fichier CSV : {e}")
137
 
138
+ else:
139
+ print("\n--- Aucun document CXC n'a pu être extrait via l'analyse de tableau. ---")
140
+
141
+ # Option de secours : Afficher un échantillon du texte brut
142
+ print("\n--- Diagnostic : Échantillon du texte brut de la page ---")
143
+ text_sample = soup.get_text()
144
+ print(text_sample[:2000]) # Afficher les 2000 premiers caractères
145
+ print("--- Fin de l'échantillon ---")
146
 
147
+ except requests.exceptions.Timeout:
148
+ print(f"Erreur : La requête a expiré après {TIMEOUT} secondes.")
149
+ except requests.exceptions.RequestException as e:
150
+ print(f"Erreur lors de la requête HTTP : {e}")
151
+ except Exception as e:
152
+ print(f"Une erreur inattendue s'est produite : {e}")
153
+ import traceback
154
+ traceback.print_exc() # Affiche la pile d'appel pour le débogage
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
+ return documents
157
 
158
+ # --- Point d'entrée du script ---
159
+ if __name__ == "__main__":
160
+ extracted_docs = extract_cxc_documents()
161
+ # Le script se termine ici. Les résultats sont affichés et sauvegardés.
162
+ print("\n--- Script terminé ---")