Ajout des logs détaillés par cinéma et film

This commit is contained in:
2026-02-19 14:22:17 +01:00
parent a2ad46555f
commit 67465d0a8f
+54 -20
View File
@@ -6,13 +6,13 @@ from bs4 import BeautifulSoup
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
app = Flask(__name__) app = Flask(__name__)
# Formatage des logs pour inclure l'heure et le niveau
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger("CineSynthese") logger = logging.getLogger("CineSynthese")
CACHE_FILE = "/data/movies_cache.json" CACHE_FILE = "/data/movies_cache.json"
HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'} HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'}
# --- CONFIGURATION FUSEAU HORAIRE ---
paris_tz = pytz.timezone("Europe/Paris") paris_tz = pytz.timezone("Europe/Paris")
def get_today_formats(): def get_today_formats():
@@ -24,9 +24,10 @@ def get_today_formats():
"cratere_date": f"{day_short}. {now.day:02d}/{now.month:02d}" "cratere_date": f"{day_short}. {now.day:02d}/{now.month:02d}"
} }
# --- 1. SCRAPER ABC (Ton code précis) --- # --- 1. SCRAPER ABC ---
def scrape_abc(): def scrape_abc():
url = "https://abc-toulouse.fr/horaires.html" url = "https://abc-toulouse.fr/horaires.html"
logger.info("📡 [ABC] Début du scraping...")
movies = [] movies = []
try: try:
r = requests.get(url, headers=HEADERS, timeout=15) r = requests.get(url, headers=HEADERS, timeout=15)
@@ -35,7 +36,7 @@ def scrape_abc():
for block in movie_blocks: for block in movie_blocks:
title_tag = block.find('h2') title_tag = block.find('h2')
if not title_tag: continue if not title_tag: continue
title = title_tag.get_text().strip() title = title_tag.get_text(strip=True)
img_tag = block.find('img') img_tag = block.find('img')
img_url = "https://abc-toulouse.fr" + img_tag.get('src') if img_tag else "" img_url = "https://abc-toulouse.fr" + img_tag.get('src') if img_tag else ""
@@ -43,17 +44,22 @@ def scrape_abc():
showtimes_text = next_div.get_text(" ") if next_div else block.get_text(" ") showtimes_text = next_div.get_text(" ") if next_div else block.get_text(" ")
times = re.findall(r'(\d{2}:\d{2})', showtimes_text) times = re.findall(r'(\d{2}:\d{2})', showtimes_text)
if times: if times:
found_times = sorted(list(set(times)))
movies.append({ movies.append({
"title": title, "img": img_url, "times": sorted(list(set(times))), "title": title, "img": img_url, "times": found_times,
"cinema": "ABC", "color": "#00bcd4" "cinema": "ABC", "color": "#00bcd4"
}) })
except: logger.error("Fail ABC") logger.info(f" ✅ [ABC] Trouvé: {title} ({', '.join(found_times)})")
logger.info(f"📊 [ABC] Total: {len(movies)} films récupérés.")
except Exception as e:
logger.error(f"❌ [ABC] Erreur lors du scraping: {e}")
return movies return movies
# --- 2. SCRAPER LE CRATÈRE (Ton code précis) --- # --- 2. SCRAPER LE CRATÈRE ---
def scrape_cratere(): def scrape_cratere():
url_main = "https://www.cinemalecratere.fr/films" url_main = "https://www.cinemalecratere.fr/films"
target_date = get_today_formats()["cratere_date"] target_date = get_today_formats()["cratere_date"]
logger.info(f"📡 [Le Cratère] Début du scraping pour la date: {target_date}...")
movies_dict = {} movies_dict = {}
session = requests.Session() session = requests.Session()
try: try:
@@ -66,6 +72,7 @@ def scrape_cratere():
title = title_tag.get_text(strip=True) if title_tag else "Inconnu" title = title_tag.get_text(strip=True) if title_tag else "Inconnu"
time_match = re.search(r'(\d{2}h\d{2})', item['data-search']) time_match = re.search(r'(\d{2}h\d{2})', item['data-search'])
show_time = time_match.group(0).replace('h', ':') if time_match else "Séance" show_time = time_match.group(0).replace('h', ':') if time_match else "Séance"
if title not in movies_dict: if title not in movies_dict:
img_url = "" img_url = ""
link_tag = item.find('a', href=True) link_tag = item.find('a', href=True)
@@ -75,24 +82,34 @@ def scrape_cratere():
if img_tag: if img_tag:
img_url = img_tag.get('src') img_url = img_tag.get('src')
if not img_url.startswith('http'): img_url = "https://www.cinemalecratere.fr" + img_url if not img_url.startswith('http'): img_url = "https://www.cinemalecratere.fr" + img_url
movies_dict[title] = {"title": title, "img": img_url, "times": [show_time], "cinema": "Le Cratère", "color": "#e62117"} movies_dict[title] = {"title": title, "img": img_url, "times": [show_time], "cinema": "Le Cratère", "color": "#e62117"}
logger.info(f" ✅ [Le Cratère] Nouveau film: {title}")
else: else:
if show_time not in movies_dict[title]["times"]: movies_dict[title]["times"].append(show_time) if show_time not in movies_dict[title]["times"]:
except: logger.error("Fail Cratère") movies_dict[title]["times"].append(show_time)
logger.info(f" [Le Cratère] Séance ajoutée pour {title}: {show_time}")
logger.info(f"📊 [Le Cratère] Total: {len(movies_dict)} films récupérés.")
except Exception as e:
logger.error(f"❌ [Le Cratère] Erreur lors du scraping: {e}")
return list(movies_dict.values()) return list(movies_dict.values())
# --- 3. SCRAPER COSMO (REPRISE STRICTE DE TON CODE app_cosmo.py) --- # --- 3. SCRAPER COSMO ---
def scrape_cosmo(): def scrape_cosmo():
BASE_URL = "https://www.american-cosmograph.fr" BASE_URL = "https://www.american-cosmograph.fr"
url_horaires = f"{BASE_URL}/les-horaires.html" url_horaires = f"{BASE_URL}/les-horaires.html"
target_id = get_today_formats()["cosmo_id"] target_id = get_today_formats()["cosmo_id"]
logger.info(f"📡 [Cosmograph] Début du scraping pour le bloc: {target_id}...")
movies_today = [] movies_today = []
session = requests.Session() session = requests.Session()
try: try:
r = session.get(url_horaires, headers=HEADERS, timeout=15) r = session.get(url_horaires, headers=HEADERS, timeout=15)
soup = BeautifulSoup(r.content, 'html.parser') soup = BeautifulSoup(r.content, 'html.parser')
day_container = soup.find('div', id=target_id) day_container = soup.find('div', id=target_id)
if not day_container: return [] if not day_container:
logger.warning(f" ⚠️ [Cosmograph] Bloc {target_id} non trouvé sur le site.")
return []
items = day_container.find_all('li', class_='heureReady') items = day_container.find_all('li', class_='heureReady')
for item in items: for item in items:
title_tag = item.select_one('.filmTitle a') title_tag = item.select_one('.filmTitle a')
@@ -103,11 +120,11 @@ def scrape_cosmo():
time_raw = time_tag.get_text(strip=True) if time_tag else "" time_raw = time_tag.get_text(strip=True) if time_tag else ""
st_match = re.search(r'\d{2}h\d{2}', time_raw) st_match = re.search(r'\d{2}h\d{2}', time_raw)
show_time = st_match.group(0).replace('h', ':') if st_match else "Séance" show_time = st_match.group(0).replace('h', ':') if st_match else "Séance"
img_url = "" img_url = ""
if detail_path: if detail_path:
detail_url = f"{BASE_URL}{detail_path}"
try: try:
r_detail = session.get(detail_url, headers=HEADERS, timeout=10) r_detail = session.get(f"{BASE_URL}{detail_path}", headers=HEADERS, timeout=10)
soup_detail = BeautifulSoup(r_detail.content, 'html.parser') soup_detail = BeautifulSoup(r_detail.content, 'html.parser')
img_tag = soup_detail.find('img', class_='fc_field_image') img_tag = soup_detail.find('img', class_='fc_field_image')
if img_tag: if img_tag:
@@ -115,9 +132,12 @@ def scrape_cosmo():
if img_url and not img_url.startswith('http'): if img_url and not img_url.startswith('http'):
img_url = f"{BASE_URL}{img_url}" img_url = f"{BASE_URL}{img_url}"
except: pass except: pass
movies_today.append({"title": title, "img": img_url, "times": [show_time], "cinema": "American Cosmograph", "color": "#ffc107"}) movies_today.append({"title": title, "img": img_url, "times": [show_time], "cinema": "American Cosmograph", "color": "#ffc107"})
logger.info(f" ✅ [Cosmograph] Séance trouvée: {title} à {show_time}")
time.sleep(0.1) time.sleep(0.1)
except: pass except Exception as e:
logger.error(f"❌ [Cosmograph] Erreur lors du scraping: {e}")
unique_movies = {} unique_movies = {}
for m in movies_today: for m in movies_today:
@@ -127,27 +147,37 @@ def scrape_cosmo():
if m['times'][0] not in unique_movies[m['title']]['times']: if m['times'][0] not in unique_movies[m['title']]['times']:
unique_movies[m['title']]['times'].append(m['times'][0]) unique_movies[m['title']]['times'].append(m['times'][0])
unique_movies[m['title']]['times'].sort() unique_movies[m['title']]['times'].sort()
logger.info(f"📊 [Cosmograph] Total: {len(unique_movies)} films uniques récupérés.")
return list(unique_movies.values()) return list(unique_movies.values())
# --- LOGIQUE MISE À JOUR --- # --- LOGIQUE MISE À JOUR ---
def update_all_data(): def update_all_data():
logger.info("🕒 Mise à jour programmée...") logger.info("🚀 --- LANCEMENT DE LA MISE À JOUR GLOBALE ---")
start_time = time.time()
try: try:
data = scrape_abc() + scrape_cratere() + scrape_cosmo() abc = scrape_abc()
cratere = scrape_cratere()
cosmo = scrape_cosmo()
data = abc + cratere + cosmo
data = sorted(data, key=lambda x: x['times'][0] if x['times'] else "99:99") data = sorted(data, key=lambda x: x['times'][0] if x['times'] else "99:99")
# Heure de mise à jour calée sur Paris
now_paris = datetime.now(paris_tz) now_paris = datetime.now(paris_tz)
payload = {"last_update": now_paris.strftime("%d/%m/%Y à %H:%M"), "movies": data} update_str = now_paris.strftime("%d/%m/%Y à %H:%M")
payload = {"last_update": update_str, "movies": data}
os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True) os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True)
with open(CACHE_FILE, 'w') as f: json.dump(payload, f) with open(CACHE_FILE, 'w') as f: json.dump(payload, f)
duration = round(time.time() - start_time, 2)
logger.info(f"✨ --- MISE À JOUR TERMINÉE EN {duration}s ---")
logger.info(f"📈 Total final: {len(data)} films à l'affiche aujourd'hui.")
return payload return payload
except Exception as e: except Exception as e:
logger.error(f"Erreur MAJ: {e}") logger.error(f"💥 Erreur critique lors de la mise à jour: {e}")
return None return None
# --- SCHEDULER CALÉ SUR PARIS ---
scheduler = BackgroundScheduler(timezone=paris_tz) scheduler = BackgroundScheduler(timezone=paris_tz)
scheduler.add_job(func=update_all_data, trigger="cron", hour=0, minute=5) scheduler.add_job(func=update_all_data, trigger="cron", hour=0, minute=5)
scheduler.start() scheduler.start()
@@ -155,14 +185,18 @@ scheduler.start()
@app.route('/') @app.route('/')
def index(): def index():
if not os.path.exists(CACHE_FILE): if not os.path.exists(CACHE_FILE):
logger.info("️ Cache inexistant, lancement du premier scraping...")
cache = update_all_data() cache = update_all_data()
else: else:
with open(CACHE_FILE, 'r') as f: cache = json.load(f) with open(CACHE_FILE, 'r') as f: cache = json.load(f)
if isinstance(cache, list): cache = update_all_data() if isinstance(cache, list):
logger.info("️ Cache ancien format détecté, conversion...")
cache = update_all_data()
return render_template('index.html', films=cache["movies"], updated=cache["last_update"]) return render_template('index.html', films=cache["movies"], updated=cache["last_update"])
@app.route('/refresh') @app.route('/refresh')
def refresh(): def refresh():
logger.info("🔄 Rafraîchissement manuel demandé via /refresh")
update_all_data() update_all_data()
return redirect(url_for('index')) return redirect(url_for('index'))