Skip to content

Commit 95d9b1d

Browse files
Edouard SilvestreEdouard Silvestre
authored andcommitted
[FIX] db format
1 parent 716f986 commit 95d9b1d

6 files changed

Lines changed: 10 additions & 53 deletions

File tree

scrap/medium_scraping.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from typing import List, Dict, Optional
44
import time
55

6-
# Constantes de l'outil de veille
76
SOURCE_SITE = "medium"
87

98
RSS_FEEDS = [
@@ -17,7 +16,6 @@ def normalize_medium_entry(entry: feedparser.FeedParserDict) -> Dict:
1716
"""Normalise une entrée RSS Medium dans le format unifié."""
1817
entry_id = entry.get('link', '')
1918

20-
# Conversion de la date
2119
published_date = datetime.utcnow().isoformat()
2220
if getattr(entry, "published_parsed", None):
2321
published_date = datetime.fromtimestamp(time.mktime(entry.published_parsed)).isoformat()
@@ -54,7 +52,7 @@ def scrape_medium(max_articles_per_feed: int = 10) -> List[Dict]:
5452

5553
except Exception as e:
5654
print(f"❌ Error fetching {feed_url}: {e}")
57-
time.sleep(1) # Respecter une pause entre les appels RSS
55+
time.sleep(1)
5856

5957
return all_items
6058

scrap/scrap_arxiv.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ def normalize_arxiv_result(paper: arxiv.Result) -> Dict:
1111

1212
authors = ", ".join([a.name for a in paper.authors])
1313

14-
# Utiliser le lien de l'abstract comme ID/URL
15-
link = paper.entry_id # C'est généralement l'URL de l'abstract dans la librairie
14+
link = paper.entry_id
1615

1716
keywords_list = [paper.primary_category]
1817
if paper.categories:
@@ -21,7 +20,7 @@ def normalize_arxiv_result(paper: arxiv.Result) -> Dict:
2120
return {
2221
"id": link,
2322
"source_site": SOURCE_SITE,
24-
"title": paper.title.replace('\n', ' '), # Enlever les sauts de ligne dans le titre
23+
"title": paper.title.replace('\n', ' '),
2524
"description": paper.summary.replace('\n', ' '),
2625
"author_info": authors,
2726
"keywords": ", ".join(keywords_list),

scrap/scrap_le_monde.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from datetime import datetime
44
from typing import List, Dict
55

6-
# Constantes de l'outil de veille
76
SOURCE_SITE = "le_monde"
87

98
FEEDS = [
@@ -16,14 +15,12 @@ def normalize_lemonde_entry(entry: feedparser.FeedParserDict, feed_url: str) ->
1615
"""Normalise une entrée RSS Le Monde dans le format unifié."""
1716
entry_id = getattr(entry, "id", None) or getattr(entry, "link", None)
1817

19-
# Déterminer la date
2018
published_date = datetime.utcnow().isoformat()
2119
if getattr(entry, "published_parsed", None):
2220
published_date = datetime.fromtimestamp(time.mktime(entry.published_parsed)).isoformat()
2321
elif getattr(entry, "updated_parsed", None):
2422
published_date = datetime.fromtimestamp(time.mktime(entry.updated_parsed)).isoformat()
2523

26-
# Extraire la catégorie du flux si possible
2724
category = "actualité générale"
2825
if "international" in feed_url:
2926
category = "international"
@@ -38,7 +35,7 @@ def normalize_lemonde_entry(entry: feedparser.FeedParserDict, feed_url: str) ->
3835
"title": getattr(entry, "title", ""),
3936
"description": getattr(entry, "summary", ""),
4037
"author_info": getattr(entry, "author", "Le Monde"),
41-
"keywords": category, # Utiliser la catégorie du flux comme mot-clé principal
38+
"keywords": category,
4239
"content_url": getattr(entry, "link", ""),
4340
"published_date": published_date,
4441
"item_type": "article",
@@ -61,7 +58,7 @@ def scrape_lemonde(feeds: List[str] = FEEDS) -> List[Dict]:
6158

6259
except Exception as e:
6360
print(f"[ERREUR] du fetch du feed {feed_url}: {e}")
64-
time.sleep(1) # Petite pause entre les flux
61+
time.sleep(1)
6562

6663
return all_items
6764

scrap/scrape_github.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
from datetime import datetime, UTC
44
from typing import List, Dict
55

6-
# Constantes de l'outil de veille
76
SOURCE_SITE = "github"
87
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
98

10-
# ... (THEMES, HEADERS, RateLimitError, sanitize_text, normalize_github_repo, build_query_for_theme restent inchangés) ...
119

1210
THEMES = [
1311
"large-language-model", "llm", "transformer", "text-generation", "retrieval-augmented-generation",
@@ -69,24 +67,17 @@ def search_github_repos(query: str, per_page: int = 20) -> List[Dict]:
6967

7068
if resp.status_code == 403:
7169
retry_after = resp.headers.get("Retry-After")
72-
# Lève l'erreur pour la gestion du break dans scrape_github
7370
raise RateLimitError(retry_after=int(retry_after) if retry_after and retry_after.isdigit() else None)
74-
75-
# 🎯 CORRECTION CLÉ DANS CE BLOC :
76-
# Utiliser 'resp.raise_for_status()' si vous souhaitez détecter les 4xx/5xx généraux,
77-
# mais pour la robustesse, nous allons d'abord vérifier le statut et analyser le JSON.
78-
71+
7972
if resp.status_code != 200:
80-
# Pour toutes les autres erreurs non 403, nous loguons et retournons vide.
8173
print(f"[WARN] HTTP Status {resp.status_code} for query: {query}")
8274
return []
8375

84-
# Si le statut est 200, nous essayons d'analyser le JSON
8576
data = resp.json()
8677
return data.get("items", [])
8778

8879
except RateLimitError:
89-
raise # Relance RateLimitError
80+
raise
9081
except requests.exceptions.RequestException as e:
9182
print(f"[ERREUR CONNEXION/HTTP] GitHub Search: {e}")
9283
return []
@@ -99,7 +90,7 @@ def scrape_github(themes: List[str] = THEMES, limit_per_theme: int = 20) -> List
9990
"""Scrape GitHub pour les thèmes donnés et retourne les éléments unifiés."""
10091

10192
all_items = []
102-
stop_scraping = False # Drapeau de contrôle
93+
stop_scraping = False
10394

10495
try:
10596
for theme in themes:
@@ -112,7 +103,6 @@ def scrape_github(themes: List[str] = THEMES, limit_per_theme: int = 20) -> List
112103
try:
113104
items = search_github_repos(q, limit_per_theme)
114105

115-
# SÉCURITÉ SUPPLÉMENTAIRE :
116106
if not isinstance(items, list):
117107
print(f"[FATAL WARN] search_github_repos a retourné {type(items)} au lieu de list. Arrêt.")
118108
stop_scraping = True
@@ -122,11 +112,9 @@ def scrape_github(themes: List[str] = THEMES, limit_per_theme: int = 20) -> List
122112
all_items.extend(normalized_items)
123113

124114
except RateLimitError:
125-
# Gère spécifiquement l'erreur de Rate Limit
126115
print(f"[RATE LIMIT] Limite atteinte. Arrêt de la veille GitHub pour cette itération.")
127116
stop_scraping = True
128117
except Exception as e:
129-
# Gère toutes les autres exceptions de niveau thème (très peu probables maintenant)
130118
print(f"[ERREUR THÈME] '{theme}': {e}")
131119
continue
132120

scrap/scrape_hf.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import requests
2-
from datetime import datetime, UTC # Importation de UTC
2+
from datetime import datetime, UTC
33
from typing import List, Dict
44

5-
# Constantes de l'outil de veille
65
SOURCE_SITE = "huggingface"
76

87
def build_url(item: Dict, item_type: str) -> str:
@@ -11,32 +10,26 @@ def build_url(item: Dict, item_type: str) -> str:
1110
item_id = item.get("id")
1211
if item_type == "model":
1312
return f"{base}/{item.get('modelId')}"
14-
# Correction de la liste pour inclure tous les types pertinents
1513
elif item_type in ("dataset", "space", "collection", "paper"):
1614
return f"{base}/{item_id}"
1715
return base
1816

1917
def normalize_huggingface_item(item: Dict, item_type: str) -> Dict:
2018
"""Normalise un élément Hugging Face dans le format unifié."""
21-
# Déterminer le nom et l'ID
2219
item_name = item.get("name") or item.get("modelId") or item.get("id")
2320
item_id = item.get("id") or item.get("modelId") or item.get("name")
2421

25-
# Déterminer l'auteur
2622
author = item.get("author") or item.get("organization", "")
2723

28-
# Déterminer la description/le résumé (souvent pas disponible dans la liste, on utilise le 'name' ou 'id' par défaut)
2924
description = item.get("description", item_name)
3025

31-
# Déterminer les mots-clés
3226
keywords_list = []
3327
if item.get("tags"):
3428
keywords_list.extend(item.get("tags"))
3529
if item.get("pipeline_tag"):
3630
tag = item.get("pipeline_tag")
3731
keywords_list.append(tag if isinstance(tag, str) else ", ".join(tag))
3832

39-
# Déterminer la date - Utilisation de datetime.now(UTC)
4033
last_modified = item.get("lastModified") or item.get("last_modified") or datetime.now(UTC).isoformat()
4134

4235
return {
@@ -56,18 +49,15 @@ def fetch_huggingface_api(endpoint: str, item_type: str, limit: int = 20) -> Lis
5649
url = f"https://huggingface.co/api/{endpoint}?sort=lastModified&direction=-1&limit={limit}"
5750

5851
try:
59-
# Aucune en-tête d'authentification envoyée
6052
r = requests.get(url, timeout=20)
6153

6254
if r.status_code == 404:
6355
return []
6456

65-
# Gère les autres erreurs (4xx/5xx) si elles surviennent
6657
r.raise_for_status()
6758

6859
items = r.json()
6960

70-
# Normalisation des données
7161
normalized_items = [normalize_huggingface_item(item, item_type) for item in items]
7262
return normalized_items
7363

@@ -78,7 +68,6 @@ def fetch_huggingface_api(endpoint: str, item_type: str, limit: int = 20) -> Lis
7868
def scrape_huggingface(limit_per_type: int = 20) -> List[Dict]:
7969
"""Scrape le Hugging Face Hub, ignorant l'endpoint 'organizations'."""
8070

81-
# 🛑 L'entrée "organizations" a été retirée pour éviter l'erreur 401
8271
fetchers = [
8372
("models", "model"),
8473
("datasets", "dataset"),

scrap/unified_scrapper_pipeline.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import sqlite3
2-
# Importation de UTC pour la gestion moderne du temps
32
from datetime import datetime, UTC
43
from typing import List, Dict
54
import time
65
import os
7-
8-
# 💡 Assurez-vous d'importer vos fonctions de scraping normalisées
9-
# J'utilise les noms de modules que vous avez fournis
106
from scrape_hf import scrape_huggingface
117
from scrape_github import scrape_github
128
from medium_scraping import scrape_medium
@@ -41,10 +37,8 @@ def setup_database():
4137
def save_unified_item(item: Dict, conn: sqlite3.Connection):
4238
"""Insère un élément unifié dans la base de données."""
4339
cur = conn.cursor()
44-
# ✅ CORRECTION 1: Utilisation de datetime.now(UTC) pour éviter la dépréciation
4540
now = datetime.now(UTC).isoformat()
4641

47-
# Utilisation d'INSERT OR IGNORE pour gérer le dédoublonnage par l'ID
4842
cur.execute("""
4943
INSERT OR IGNORE INTO unified_data
5044
(id, source_site, title, description, author_info, keywords, content_url, published_date, item_type, created_at)
@@ -86,32 +80,26 @@ def run_scrapers_and_save():
8680
try:
8781
items = scraper_func(limit) if limit is not None else scraper_func()
8882

89-
# ✅ CORRECTION 2: Gestion robuste des types de retour non-itérables (comme int ou None)
9083

91-
# Si 'items' est None, ou non-itérable (int), nous le traitons.
9284
if items is None:
9385
print(f" ❌ **ALERTE: Le scraper {name} a retourné None. Skipping.**")
9486
continue
9587

96-
# Tenter de vérifier l'itérabilité pour attraper l'erreur 'int' object is not iterable
9788
try:
98-
# Si l'objet n'est pas itérable (ex: int 403), cette ligne lève une TypeError
9989
iter(items)
10090

10191
except TypeError:
10292
print(f" ❌ **ERREUR FATALE (Non-Itérable)**: Le scraper {name} a retourné un type non itérable ({type(items)}). Skipping.")
10393
continue
10494

105-
# À ce stade, 'items' est garanti d'être itérable, mais nous vérifions si c'est une liste
10695
if not isinstance(items, list):
10796
print(f" ⚠️ WARNING: Le scraper {name} a retourné un objet itérable ({type(items)}) mais pas une liste. Conversion en liste.")
108-
items = list(items) # Convertir en liste au cas où ce serait un tuple/set
97+
items = list(items)
10998

11099
print(f" -> {len(items)} éléments récupérés.")
111100

112101
count_saved = 0
113102
for item in items:
114-
# La fonction save_unified_item gère le dédoublonnage (INSERT OR IGNORE)
115103
save_unified_item(item, conn)
116104
count_saved += 1
117105

@@ -138,12 +126,10 @@ def check_results():
138126
print("La base de données est vide.")
139127
return
140128

141-
# Afficher les noms de colonnes
142129
column_names = [description[0] for description in cur.description]
143130
print(f"Colonnes: {column_names}")
144131
print("-" * 120)
145132

146-
# Afficher les données
147133
for row in rows:
148134
print(row)
149135

0 commit comments

Comments
 (0)