avant codex
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
Voici des éléments structurés essentiels pour écrire un tutoriel de scraping Python à partir du site saint-dicton.com, en particulier pour la page d’un jour précis (par exemple https://www.saint-dicton.com/0222.html) :
|
||||
|
||||
📌 Structure observée du site Saint-Dicton
|
||||
|
||||
Un exemple de page date contient :
|
||||
|
||||
Liste des saints fêtés ce jour
|
||||
Exemples : “St-Sulpice Sévère”, “St-Valère”, etc.
|
||||
Ce bloc est présenté sous forme de texte HTML listé en paragraphes ou sections.
|
||||
|
||||
Phase de la lune
|
||||
Peut être présente mais n’est pas structurée pour le scraping des saints.
|
||||
|
||||
Dicton du jour
|
||||
Un bloc intitulé “## Dicton du jour” suivi d’un ou plusieurs dictons.
|
||||
|
||||
Prénoms à fêter
|
||||
Peut contenir une liste ou être vide.
|
||||
|
||||
🧠 Analyse de la pagination annuelle
|
||||
|
||||
Le site propose également une navigation jour par jour :
|
||||
|
||||
L’URL pour une date donnée prend la forme https://www.saint-dicton.com/MMDD.html où MM est le mois sur deux chiffres et DD le jour sur deux chiffres (par exemple 0222 pour 22 février).
|
||||
|
||||
Il existe une page par mois avec tous les saints par ordre alphabétique (ex. SaintsA.html).
|
||||
|
||||
Ceci permet de parcourir toutes les dates de l’année systématiquement.
|
||||
|
||||
📘 Sources externes utiles
|
||||
|
||||
Pour enrichir ton scraping :
|
||||
|
||||
Nominis propose une base de plusieurs milliers de saints et fêtes liturgiques (utile pour comparer).
|
||||
|
||||
Des sites comme Éphéméride ou Wiki des proverbes donnent des dictons et proverbes météo/agricoles si besoin de compléter la base. (source non spécifique, généraliste).
|
||||
|
||||
🧪 Extrait de page (exemple réel)
|
||||
|
||||
Dans la page 0222.html, on observe :
|
||||
|
||||
## Dicton du jour
|
||||
|
||||
S'il gèle à la Saint-Sulpice,
|
||||
Le printemps sera propice
|
||||
|
||||
Ici :
|
||||
|
||||
“Dicton du jour” est un titre h2
|
||||
|
||||
Le dicton est une ligne ou plusieurs lignes de texte juste après ce titre.
|
||||
|
||||
📌 Tutoriel ciblé pour écrire scrap_saint.md
|
||||
|
||||
Dans ce fichier .md, tu pourras expliquer :
|
||||
|
||||
Objectif du scraping
|
||||
Extraire pour chaque date :
|
||||
|
||||
date (MM-DD)
|
||||
|
||||
nom du saint (ou saints)
|
||||
|
||||
dictons du jour
|
||||
|
||||
éventuellement prénoms associés
|
||||
|
||||
Analyse HTML du site
|
||||
|
||||
Les pages individuelles des dates
|
||||
|
||||
Les sections à scraper
|
||||
|
||||
Comment repérer les titres HTML des dictons
|
||||
|
||||
Étapes du script Python
|
||||
|
||||
itérer sur toutes les dates de l’année
|
||||
|
||||
fetch des pages MMDD.html
|
||||
|
||||
parser l’HTML avec BeautifulSoup
|
||||
|
||||
extraire listes de saints
|
||||
|
||||
extraire bloc dictons
|
||||
|
||||
stocker dans une structure CSV ou JSON
|
||||
|
||||
Exemple de structure de sortie
|
||||
|
||||
{
|
||||
"date": "0222",
|
||||
"saints": ["St-Sulpice Sévère", "St-Valère"],
|
||||
"dictons": ["S'il gèle à la Saint-Sulpice, …"]
|
||||
}
|
||||
|
||||
Conseils pour éviter les pièges
|
||||
|
||||
certaines dates ont plusieurs dictons
|
||||
|
||||
structure HTML parfois variable
|
||||
|
||||
gérer les accents et encodage UTF-8
|
||||
|
||||
Si tu veux, je peux te générer directement le contenu complet de ce fichier scrap_saint.md avec un script Python détaillé (requests + BeautifulSoup) prêt à copier.
|
||||
Dis-moi si tu veux aussi inclure le scraping du contenu de la page alphabétique SaintsA.html pour récupérer la liste complète des noms de saints.
|
||||
|
||||
genere scrap.md
|
||||
|
||||
Voici un tutoriel complet scrap_saint.md pour t’aider à scraper les pages de saints et leurs dictons à partir du site https://www.saint-dicton.com/0222.html
|
||||
et des pages annuelles correspondantes. Ce fichier explique l’analyse de la structure, un exemple concret de script Python, et des conseils pour boucler sur toute l’année.
|
||||
|
||||
# scrap_saint.md
|
||||
|
||||
## Objectif
|
||||
|
||||
Ce tutoriel explique comment *scraper automatiquement* les informations suivantes pour chaque jour de l’année à partir du site **saint-dicton.com** :
|
||||
|
||||
- la **date** format MMDD (ex : *0222*)
|
||||
- le(s) **nom(s) du saint** fêté(s)
|
||||
- le ou les **dictons associés au jour**
|
||||
|
||||
L’objectif est de générer une base de données annuelle (CSV/JSON) des saints et des dictons, utilisable ensuite dans un projet Python par exemple.
|
||||
|
||||
---
|
||||
|
||||
## Analyse de la structure du site
|
||||
|
||||
Le site propose une **page par date** selon le format d’URL :
|
||||
|
||||
|
||||
https://www.saint-dicton.com/MMDD.html
|
||||
|
||||
|
||||
Exemples :
|
||||
- https://www.saint-dicton.com/0222.html → *Saint-Sulpice Sévère* + dicton du jour :contentReference[oaicite:0]{index=0}
|
||||
- https://www.saint-dicton.com/0208.html → *dicton de la Saint-Jean* :contentReference[oaicite:1]{index=1}
|
||||
|
||||
Chaque page contient typiquement :
|
||||
|
||||
1. **Fêtes du jour**
|
||||
Une section listant un ou plusieurs saints (ex : *St-Sulpice Sévère*, *St-Valère*) :contentReference[oaicite:2]{index=2}
|
||||
|
||||
2. **Bloc Dicton du jour**
|
||||
Titre suivi du texte du dicton (souvent une ou plusieurs lignes) :contentReference[oaicite:3]{index=3}
|
||||
|
||||
3. **(Optionnel) Prénoms à fêter**
|
||||
Liste de prénoms associés à la date :contentReference[oaicite:4]{index=4}
|
||||
|
||||
> Le site propose aussi une page d’accueil et des pages alphabétiques, mais pour extraire une *base annuelle*, le patron `MMDD.html` est utile pour itérer sur chaque jour de l’année. :contentReference[oaicite:5]{index=5}
|
||||
|
||||
---
|
||||
|
||||
## Pré-requis
|
||||
|
||||
Installer les dépendances Python nécessaires :
|
||||
|
||||
```bash
|
||||
pip install requests beautifulsoup4
|
||||
|
||||
Optionnel mais recommandé :
|
||||
|
||||
pip install lxml
|
||||
Exemple de script Python (scraper)
|
||||
|
||||
Ce script parcourt toutes les dates de l’année, récupère chaque page, analyse l’HTML et enregistre les données dans un fichier CSV.
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
import csv
|
||||
import time
|
||||
|
||||
BASE_URL = "https://www.saint-dicton.com/{:02d}{:02d}.html"
|
||||
|
||||
def scrape_day(month: int, day: int):
|
||||
url = BASE_URL.format(month, day)
|
||||
resp = requests.get(url)
|
||||
if resp.status_code != 200:
|
||||
return None
|
||||
|
||||
soup = BeautifulSoup(resp.text, "lxml")
|
||||
|
||||
# Extraire les noms de saints
|
||||
saints = []
|
||||
h1 = soup.find("h1")
|
||||
if h1:
|
||||
# souvent la liste des saints est en texte dans la partie principale
|
||||
for line in h1.text.split("\n"):
|
||||
if line.strip():
|
||||
saints.append(line.strip())
|
||||
|
||||
# trouver le div ou section contenant "Dicton du jour"
|
||||
dicton_data = ""
|
||||
target = soup.find(text="Dicton du jour")
|
||||
if target:
|
||||
parent = target.find_parent()
|
||||
if parent:
|
||||
# juste après ce bloc, prendre les lignes de dicton
|
||||
for p in parent.find_next_siblings():
|
||||
text = p.get_text(strip=True)
|
||||
if text:
|
||||
dicton_data += text + " "
|
||||
|
||||
return {
|
||||
"month": month,
|
||||
"day": day,
|
||||
"saints": saints,
|
||||
"dicton": dicton_data.strip()
|
||||
}
|
||||
|
||||
# Boucle sur toute l'année
|
||||
with open("saints_dictons.csv", "w", newline="", encoding="utf-8") as csvfile:
|
||||
fieldnames = ["month", "day", "saints", "dicton"]
|
||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";")
|
||||
writer.writeheader()
|
||||
|
||||
for m in range(1, 13):
|
||||
for d in range(1, 32):
|
||||
data = scrape_day(m, d)
|
||||
if data:
|
||||
writer.writerow({
|
||||
"month": data["month"],
|
||||
"day": data["day"],
|
||||
"saints": "|".join(data["saints"]),
|
||||
"dicton": data["dicton"]
|
||||
})
|
||||
print(f"Scraped {m:02d}-{d:02d}")
|
||||
time.sleep(1) # pause pour éviter surcharge
|
||||
Bonnes pratiques & conseils
|
||||
💡 Respect et performance
|
||||
|
||||
Respecte les règles d’utilisation du site. Le scraping est légitime sur du contenu public, mais il faut éviter une surcharge du serveur.
|
||||
|
||||
Met une pause (time.sleep(1)) entre les requêtes pour ne pas déclencher de blocage ou DDoS.
|
||||
|
||||
🧠 Inspection du HTML
|
||||
|
||||
Avant d’extraire le texte, inspecte la page dans un navigateur (clic droit → Inspecter) pour confirmer la structure des balises.
|
||||
|
||||
Le site n’utilise pas d’API JSON, donc BeautifulSoup est adapté.
|
||||
|
||||
⚠️ Variations possibles
|
||||
|
||||
La structure peut varier légèrement d’une date à l’autre (par ex., plusieurs saints listés en texte, parfois des paragraphes additionnels).
|
||||
|
||||
Adaptation des sélecteurs (find, find_all, classes ou IDs) peut être nécessaire selon les pages.
|
||||
|
||||
Sorties possibles
|
||||
|
||||
À partir du script ci-dessus, tu peux générer :
|
||||
|
||||
un CSV (comme ci-dessous)
|
||||
|
||||
un JSON structuré
|
||||
|
||||
un SQLite pour intégration dans ton application jardinage
|
||||
|
||||
Exemple d’enregistrement CSV :
|
||||
|
||||
month;day;saints;dicton
|
||||
2;22;St-Sulpice Sévère|St-Valère;"S'il gèle à la Saint-Sulpice, Le printemps sera propice"
|
||||
Conclusion
|
||||
|
||||
En parcourant les URL de MMDD.html et en analysant l’HTML, tu peux extraire automatiquement pour toute l’année la liste des saints et leurs dictons associés.
|
||||
Ce fichier scrap_saint.md te fournit une base de départ claire avec un script Python prêt à l’emploi.
|
||||
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
DATE_HEADING_RE = re.compile(
|
||||
r"^\s{0,3}(?:#{1,6}\s*)?((?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12][0-9]|3[01])|(?:0[1-9]|[12][0-9]|3[01])[-/](?:0[1-9]|1[0-2])|(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01]))\s*$"
|
||||
)
|
||||
INLINE_DATE_DIC_RE = re.compile(
|
||||
r"^\s*((?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12][0-9]|3[01])|(?:0[1-9]|[12][0-9]|3[01])[-/](?:0[1-9]|1[0-2])|(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01]))\s*[:\-]\s*(.+)$"
|
||||
)
|
||||
|
||||
|
||||
def _normalize_mmdd(token: str) -> str | None:
|
||||
token = token.strip()
|
||||
if re.fullmatch(r"(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])", token):
|
||||
month = token[:2]
|
||||
day = token[2:]
|
||||
elif re.fullmatch(r"(0[1-9]|1[0-2])[-/](0[1-9]|[12][0-9]|3[01])", token):
|
||||
month, day = re.split(r"[-/]", token)
|
||||
elif re.fullmatch(r"(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[0-2])", token):
|
||||
day, month = re.split(r"[-/]", token)
|
||||
else:
|
||||
return None
|
||||
return f"{month}-{day}"
|
||||
|
||||
|
||||
def _unique(values: list[str]) -> list[str]:
|
||||
out: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for value in values:
|
||||
v = value.strip()
|
||||
if not v:
|
||||
continue
|
||||
if v not in seen:
|
||||
seen.add(v)
|
||||
out.append(v)
|
||||
return out
|
||||
|
||||
|
||||
def _split_saints(text: str) -> list[str]:
|
||||
# Normalize separators and preserve saint labels.
|
||||
cleaned = text.strip().strip(".")
|
||||
cleaned = re.sub(r"^(saints?\s*[:\-]\s*)", "", cleaned, flags=re.I).strip()
|
||||
if not cleaned:
|
||||
return []
|
||||
parts = re.split(r"\s*(?:,|;|\||\set\s)\s*", cleaned, flags=re.I)
|
||||
return _unique(parts)
|
||||
|
||||
|
||||
def _split_dictons(text: str) -> list[str]:
|
||||
cleaned = text.strip()
|
||||
cleaned = re.sub(r"^(dictons?\s*[:\-]\s*)", "", cleaned, flags=re.I).strip()
|
||||
if not cleaned:
|
||||
return []
|
||||
# Keep sentences readable; split on explicit separators first.
|
||||
if "|" in cleaned or ";" in cleaned:
|
||||
parts = re.split(r"\s*(?:\||;)\s*", cleaned)
|
||||
else:
|
||||
parts = [cleaned]
|
||||
return _unique(parts)
|
||||
|
||||
|
||||
def load_saints(path: Path) -> dict[str, list[str]]:
|
||||
if not path.exists():
|
||||
return {}
|
||||
raw = json.loads(path.read_text(encoding="utf-8"))
|
||||
out: dict[str, list[str]] = {}
|
||||
for mmdd, saint_value in raw.items():
|
||||
key = _normalize_mmdd(mmdd)
|
||||
if key is None:
|
||||
continue
|
||||
if isinstance(saint_value, list):
|
||||
saints = [str(x).strip() for x in saint_value]
|
||||
else:
|
||||
saints = _split_saints(str(saint_value))
|
||||
out[key] = _unique(saints)
|
||||
return out
|
||||
|
||||
|
||||
def parse_dictons_text(path: Path) -> dict[str, list[str]]:
|
||||
if not path.exists():
|
||||
return {}
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
out: dict[str, list[str]] = {}
|
||||
current_date: str | None = None
|
||||
|
||||
for raw in lines:
|
||||
line = raw.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Date heading block
|
||||
m_head = DATE_HEADING_RE.match(line)
|
||||
if m_head:
|
||||
current_date = _normalize_mmdd(m_head.group(1))
|
||||
if current_date and current_date not in out:
|
||||
out[current_date] = []
|
||||
continue
|
||||
|
||||
# Inline date + dicton
|
||||
m_inline = INLINE_DATE_DIC_RE.match(line)
|
||||
if m_inline:
|
||||
mmdd = _normalize_mmdd(m_inline.group(1))
|
||||
if mmdd:
|
||||
out.setdefault(mmdd, []).extend(_split_dictons(m_inline.group(2)))
|
||||
current_date = mmdd
|
||||
continue
|
||||
|
||||
# Bullets or plain lines inside current date block
|
||||
if current_date:
|
||||
line = re.sub(r"^\s*[-*]\s*", "", line).strip()
|
||||
if not line:
|
||||
continue
|
||||
if re.match(r"^saints?\s*[:\-]", line, flags=re.I):
|
||||
# Saints line is ignored here; saints come from saints_json.
|
||||
continue
|
||||
out.setdefault(current_date, []).extend(_split_dictons(line))
|
||||
|
||||
return {k: _unique(v) for k, v in out.items()}
|
||||
|
||||
|
||||
def _as_iso(year: int, mmdd: str) -> str:
|
||||
month, day = mmdd.split("-")
|
||||
return date(year, int(month), int(day)).isoformat()
|
||||
|
||||
|
||||
def build_output(
|
||||
saints_by_date: dict[str, list[str]],
|
||||
dictons_by_date: dict[str, list[str]],
|
||||
year: int | None,
|
||||
) -> list[dict]:
|
||||
all_dates = sorted(set(saints_by_date) | set(dictons_by_date))
|
||||
rows: list[dict] = []
|
||||
for mmdd in all_dates:
|
||||
row = {
|
||||
"date": _as_iso(year, mmdd) if year else mmdd,
|
||||
"saints": saints_by_date.get(mmdd, []),
|
||||
"dictons": dictons_by_date.get(mmdd, []),
|
||||
}
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Génère un JSON saints+dictons: date, saints[], dictons[]"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--saints-json",
|
||||
default="calendrier_lunaire/saints_dictons/saints_france.json",
|
||||
help="Fichier JSON des saints (clé MM-DD)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dictons-file",
|
||||
required=True,
|
||||
help="Fichier texte/markdown des dictons (avec dates MM-DD, DD/MM ou MMDD)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
default="calendrier_lunaire/saints_dictons/saints_dictons.json",
|
||||
help="Fichier JSON de sortie",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--year",
|
||||
type=int,
|
||||
help="Optionnel: convertit MM-DD en YYYY-MM-DD",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
saints_path = Path(args.saints_json)
|
||||
dictons_path = Path(args.dictons_file)
|
||||
output_path = Path(args.output)
|
||||
|
||||
saints_by_date = load_saints(saints_path)
|
||||
dictons_by_date = parse_dictons_text(dictons_path)
|
||||
rows = build_output(saints_by_date, dictons_by_date, args.year)
|
||||
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_path.write_text(json.dumps(rows, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(f"JSON généré: {output_path} ({len(rows)} dates)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
from datetime import date, timedelta
|
||||
from html import unescape
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
MONTHS_FR = {
|
||||
1: "janvier", 2: "février", 3: "mars", 4: "avril", 5: "mai", 6: "juin",
|
||||
7: "juillet", 8: "août", 9: "septembre", 10: "octobre", 11: "novembre", 12: "décembre",
|
||||
}
|
||||
|
||||
|
||||
def fetch_html(url: str) -> str:
|
||||
req = Request(url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)"})
|
||||
with urlopen(req, timeout=25) as resp:
|
||||
raw = resp.read()
|
||||
charset = (resp.headers.get_content_charset() or "utf-8").lower()
|
||||
try:
|
||||
return raw.decode(charset, errors="replace")
|
||||
except Exception:
|
||||
return raw.decode("utf-8", errors="replace")
|
||||
|
||||
|
||||
def clean_html_text(s: str) -> str:
|
||||
s = re.sub(r"<br\s*/?>", " ", s, flags=re.I)
|
||||
s = re.sub(r"<[^>]+>", "", s)
|
||||
s = unescape(s)
|
||||
s = s.replace("\xa0", " ")
|
||||
return re.sub(r"\s+", " ", s).strip()
|
||||
|
||||
|
||||
def parse_saints(html: str) -> list[str]:
|
||||
rows = re.findall(r'<p[^>]*class="sd-name"[^>]*>(.*?)</p>', html, flags=re.I | re.S)
|
||||
out, seen = [], set()
|
||||
for row in rows:
|
||||
txt = clean_html_text(row)
|
||||
if txt and txt not in seen:
|
||||
out.append(txt)
|
||||
seen.add(txt)
|
||||
return out
|
||||
|
||||
|
||||
def parse_dictons(html: str) -> list[str]:
|
||||
rows = re.findall(r'<p[^>]*class="dict"[^>]*>(.*?)</p>', html, flags=re.I | re.S)
|
||||
out = []
|
||||
for row in rows:
|
||||
txt = clean_html_text(row)
|
||||
if txt:
|
||||
out.append(txt)
|
||||
return out
|
||||
|
||||
|
||||
def parse_prenoms(html: str) -> list[str]:
|
||||
block = re.search(
|
||||
r'<h2[^>]*>[^<]*Pr[^<]*noms[^<]*f[^<]*ter[^<]*</h2>.*?<ul[^>]*>(.*?)</ul>',
|
||||
html,
|
||||
flags=re.I | re.S,
|
||||
)
|
||||
target = block.group(1) if block else ""
|
||||
rows = re.findall(r'<li[^>]*>(.*?)</li>', target, flags=re.I | re.S)
|
||||
out, seen = [], set()
|
||||
for row in rows:
|
||||
txt = clean_html_text(row)
|
||||
if txt and txt not in seen:
|
||||
out.append(txt)
|
||||
seen.add(txt)
|
||||
return out
|
||||
|
||||
|
||||
def iter_mmdd_full_year(year: int):
|
||||
d = date(year, 1, 1)
|
||||
end = date(year, 12, 31)
|
||||
while d <= end:
|
||||
yield d.strftime("%m%d"), d
|
||||
d += timedelta(days=1)
|
||||
# assure 29 février même année non bissextile
|
||||
if year % 4 != 0 or (year % 100 == 0 and year % 400 != 0):
|
||||
yield "0229", None
|
||||
|
||||
|
||||
def scrape_day(base_url: str, mmdd: str, d: date | None) -> dict:
|
||||
url = f"{base_url.rstrip('/')}/{mmdd}.html"
|
||||
html = fetch_html(url)
|
||||
if d:
|
||||
label = f"{d.day:02d} {MONTHS_FR[d.month]}"
|
||||
iso = d.isoformat()
|
||||
else:
|
||||
label = "29 février"
|
||||
iso = None
|
||||
|
||||
return {
|
||||
"date": label,
|
||||
"date_iso": iso,
|
||||
"mmdd": mmdd,
|
||||
"saints": parse_saints(html),
|
||||
"dictons": parse_dictons(html),
|
||||
"prenoms_a_feter": parse_prenoms(html),
|
||||
"source_url": url,
|
||||
}
|
||||
|
||||
|
||||
def _ts() -> str:
|
||||
return datetime.now().strftime("%H:%M:%S")
|
||||
|
||||
|
||||
def _log(message: str, enabled: bool) -> None:
|
||||
if enabled:
|
||||
print(f"[{_ts()}] {message}", flush=True)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Scrape saints/dictons pour toute une année (inclut 29 février)")
|
||||
ap.add_argument("--year", type=int, default=date.today().year)
|
||||
ap.add_argument("--base", default="https://www.saint-dicton.com")
|
||||
ap.add_argument("--sleep-ms", type=int, default=150, help="Pause entre requêtes")
|
||||
ap.add_argument("--limit", type=int, default=0, help="Limiter le nb de jours (test rapide)")
|
||||
ap.add_argument("--out", default="", help="Fichier de sortie JSON (sinon stdout)")
|
||||
ap.add_argument("--log-every", type=int, default=10, help="Affiche un log de progression tous les N jours")
|
||||
ap.add_argument("--quiet", action="store_true", help="Réduit les logs")
|
||||
args = ap.parse_args()
|
||||
|
||||
results = []
|
||||
count = 0
|
||||
verbose = not args.quiet
|
||||
log_every = max(1, args.log_every)
|
||||
_log(f"Démarrage scrape année={args.year}, base={args.base}", verbose)
|
||||
for mmdd, d in iter_mmdd_full_year(args.year):
|
||||
url = f"{args.base.rstrip('/')}/{mmdd}.html"
|
||||
_log(f"[{count + 1}] fetch {mmdd} -> {url}", verbose)
|
||||
try:
|
||||
results.append(scrape_day(args.base, mmdd, d))
|
||||
_log(f"[{count + 1}] ok {mmdd}", verbose and ((count + 1) % log_every == 0 or count == 0))
|
||||
except Exception as e:
|
||||
results.append({
|
||||
"mmdd": mmdd,
|
||||
"date_iso": d.isoformat() if d else None,
|
||||
"error": str(e),
|
||||
"source_url": url,
|
||||
})
|
||||
_log(f"[{count + 1}] erreur {mmdd}: {e}", True)
|
||||
count += 1
|
||||
if args.limit and count >= args.limit:
|
||||
_log(f"Arrêt par --limit={args.limit}", verbose)
|
||||
break
|
||||
if args.sleep_ms > 0:
|
||||
time.sleep(args.sleep_ms / 1000)
|
||||
|
||||
payload = {
|
||||
"year": args.year,
|
||||
"count": len(results),
|
||||
"includes_feb29": any(r.get("mmdd") == "0229" for r in results),
|
||||
"data": results,
|
||||
}
|
||||
|
||||
txt = json.dumps(payload, ensure_ascii=False, indent=2)
|
||||
if args.out:
|
||||
with open(args.out, "w", encoding="utf-8") as f:
|
||||
f.write(txt)
|
||||
_log(f"Fichier écrit: {args.out}", True)
|
||||
else:
|
||||
print(txt)
|
||||
_log(f"Terminé: {len(results)} jours", verbose)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"04-23": "Saint Georges",
|
||||
"04-25": "Saint Marc",
|
||||
"05-11": "Saint Mamert",
|
||||
"05-12": "Saint Pancrace",
|
||||
"05-13": "Saint Servais",
|
||||
"05-14": "Saint Boniface",
|
||||
"05-19": "Saint Yves",
|
||||
"05-25": "Saint Urbain"
|
||||
}
|
||||
Reference in New Issue
Block a user