Files
mediatheque-search/mediatheque_search.py
kawa-inspiron 93aa4fa2ad Initial: Script de recherche médiathèques Plaine Commune
- Recherche par mot-clé, auteur, titre, sujet
- Utilise Selenium avec Firefox headless
- Affiche titre, auteur, type, disponibilité et résumé
- Fonctionne sur le mesh KAWA
2026-05-15 19:14:32 +02:00

234 lines
7.7 KiB
Python

#!/usr/bin/env python3
"""
Médiathèques Plaine Commune - Recherche de livres
Script de recherche dans le catalogue des médiathèques de Plaine Commune
Utilise Selenium pour interagir avec l'interface web (JavaScript requis).
Usage:
python mediatheque_search.py "Carl Jung"
python mediatheque_search.py --author "Jung"
python mediatheque_search.py --title "rêves"
python mediatheque_search.py --subject "psychologie"
"""
import argparse
import sys
import time
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from webdriver_manager.firefox import GeckoDriverManager
class MediathequeSearch:
"""Recherche dans le catalogue des médiathèques de Plaine Commune"""
BASE_URL = "https://mediatheques-plainecommune.fr/iguana/www.main.cls"
SEARCH_URL = "https://mediatheques-plainecommune.fr/iguana/www.main.cls?surl=search&searchProfile=Recherche"
def __init__(self, headless=True):
"""Initialise le driver Selenium"""
options = Options()
if headless:
options.add_argument("--headless")
options.add_argument("--width=1920")
options.add_argument("--height=1080")
service = Service(GeckoDriverManager().install())
self.driver = webdriver.Firefox(service=service, options=options)
self.driver.implicitly_wait(5)
def _reject_cookies(self):
"""Refuse la bannière cookies RGPD"""
try:
refuse_btn = WebDriverWait(self.driver, 3).until(
EC.element_to_be_clickable((By.ID, "gdpr-cookie-refuse"))
)
refuse_btn.click()
time.sleep(0.5)
return True
except:
return False
def _load_search_page(self):
"""Charge la page de recherche"""
self.driver.get(self.BASE_URL + "?surl=axiell")
time.sleep(2)
self._reject_cookies()
self.driver.get(self.SEARCH_URL)
time.sleep(3)
self._reject_cookies()
def search(self, query, max_results=20):
"""
Effectue une recherche dans le catalogue
Args:
query: Terme de recherche
max_results: Nombre maximum de résultats à retourner
Returns:
Liste de dictionnaires contenant les résultats
"""
try:
self._load_search_page()
# Entrer la requête
search_input = self.driver.find_element(By.CSS_SELECTOR, "input[id*='inputWords']")
search_input.clear()
search_input.send_keys(query)
search_input.send_keys(Keys.RETURN)
# Attendre les résultats
time.sleep(5)
self._reject_cookies()
# Extraire les résultats
results = []
items = self.driver.find_elements(By.CSS_SELECTOR, "li.listItem")
for item in items[:max_results]:
try:
result = self._extract_result(item)
if result:
results.append(result)
except Exception:
continue
# Infos de pagination
try:
page_info = self.driver.find_element(By.CSS_SELECTOR, ".pageInfo").text
except:
page_info = None
return {
"query": query,
"results": results,
"page_info": page_info,
"total_found": len(results)
}
finally:
self.driver.quit()
def _extract_result(self, item):
"""Extrait les informations d'un résultat"""
result = {}
# Titre
try:
result["title"] = item.find_element(By.CSS_SELECTOR, ".briefMainTitle").text.strip()
except:
result["title"] = "N/A"
# Auteur
try:
result["author"] = item.find_element(By.CSS_SELECTOR, ".BriefAuthorpco").text.strip()
except:
result["author"] = "N/A"
# Type de document
try:
result["type"] = item.find_element(By.CSS_SELECTOR, ".briefTypeDoc").text.strip()
except:
result["type"] = "N/A"
# Disponibilité
try:
result["availability"] = item.find_element(By.CSS_SELECTOR, ".briefAvailability span").text.strip()
except:
result["availability"] = "N/A"
# Résumé
try:
summary = item.find_element(By.CSS_SELECTOR, ".briefResume").text.strip()
result["summary"] = summary[:200] + "..." if len(summary) > 200 else summary
except:
result["summary"] = ""
return result
def search_author(self, author, max_results=20):
"""Recherche par auteur"""
return self.search(f"auteur: {author}", max_results)
def search_title(self, title, max_results=20):
"""Recherche par titre"""
return self.search(f"titre: {title}", max_results)
def search_subject(self, subject, max_results=20):
"""Recherche par sujet"""
return self.search(f"sujet: {subject}", max_results)
def print_results(data):
"""Affiche les résultats formatés"""
print(f"\n{'='*60}")
print(f"RECHERCHE: {data['query']}")
print(f"{'='*60}\n")
for i, result in enumerate(data["results"], 1):
print(f"📚 {i}. {result['title']}")
print(f" Auteur: {result['author']}")
print(f" Type: {result['type']}")
print(f" Disponibilité: {result['availability']}")
if result['summary']:
print(f" Résumé: {result['summary']}")
print()
if data['page_info']:
print(f"📄 {data['page_info']}")
def main():
parser = argparse.ArgumentParser(
description="Recherche dans le catalogue des médiathèques de Plaine Commune",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Exemples:
%(prog)s "Carl Jung"
%(prog)s --author "Jung"
%(prog)s --title "rêves"
%(prog)s --subject "psychologie" --max 50
"""
)
parser.add_argument("query", nargs="?", help="Terme de recherche")
parser.add_argument("--author", "-a", help="Rechercher par auteur")
parser.add_argument("--title", "-t", help="Rechercher par titre")
parser.add_argument("--subject", "-s", help="Rechercher par sujet")
parser.add_argument("--max", "-m", type=int, default=20, help="Nombre max de résultats (défaut: 20)")
parser.add_argument("--no-headless", action="store_true", help="Afficher le navigateur")
args = parser.parse_args()
# Déterminer le type de recherche
if args.author:
searcher = MediathequeSearch(headless=not args.no_headless)
results = searcher.search_author(args.author, args.max)
elif args.title:
searcher = MediathequeSearch(headless=not args.no_headless)
results = searcher.search_title(args.title, args.max)
elif args.subject:
searcher = MediathequeSearch(headless=not args.no_headless)
results = searcher.search_subject(args.subject, args.max)
elif args.query:
searcher = MediathequeSearch(headless=not args.no_headless)
results = searcher.search(args.query, args.max)
else:
parser.print_help()
sys.exit(1)
print_results(results)
return 0
if __name__ == "__main__":
sys.exit(main())