Pipeline de collecte, nettoyage et analyse de données de films scrappées sur Letterboxd, réalisé dans le cadre de l'UV IC05 - Technologies pour la documentation et l'indexation dans l'hypermédia. L'objectif du projet est de répondre à la question suivante : qu'est-ce qui rend un film populaire sur Letterboxd ? un casting connu ? le pays de production ? le genre ? et peut-on le mesurer plutôt que l'affirmer ?
Pour ceci le projet utilise une DB PostgreSQL, la librairie Selenium pour naviguer à travers les pages de Letterboxd, et Pyhton pour l'extraction, nettoyage et analyse des données.
docker-compose.yml PostgreSQL 16 + Adminer
db/schema.sql Schéma normalisé et vues SQL
db/seed/ Échantillon de données (chargé automatiquement au premier démarrage)
src/ic05/
collecte/ Scraping Letterboxd -> JSON brut (data/raw/films/)
nettoyage/ JSON brut -> PostgreSQL (idempotent, upserts)
analyse/ PostgreSQL -> statistiques, figures, graphes réseau (output/)
scripts/ CLI alternative + migration de l'échantillon legacy
notebooks/ Notebook de démonstration
tests/ Tests unitaires (nettoyage + parsing du scraper)
Contrairement aux conseils de Mme BELLON Anne, nous n'avons que très peu utilisé la librairie Selenium , pour les raisons suivantes :
- le bloc
<script type="application/ld+json">de chaque page contient déjà le titre, l'année, le réalisateur, les genres, le(s) pays d'origine, le casting complet et la note agrégée ; - les vues et les likes sont exposés par un endpoint statique (
/csi/film/<slug>/stats/) avec le nombre exact dans l'attributaria-label; - seuls les thèmes (
/film/<slug>/genres/) demandent une requête HTTP de plus.
Résultat : le détail d'un film se scrape avec 3 requêtes requests simples, sans navigateur (voir src/ic05/collecte/films.py), ce qui s'avère bien plus rapide que de faire tourner Selenium sur un/des navigateur(s).
Selenium ne sert ici qu'à parcourir le catalogue par décennie (/films/decade/...), dont la grille de posters est peuplée en React côté client.
Prérequis : Docker, Python 3.11+.
cp .env.example .env
make up # démarre Postgres (+ échantillon de données seedé) et Adminer
make install # crée le venv et installe le package ic05
make analyser # lance toutes les analyses -> output/figures, output/network, output/resultats.jsonAdminer est accessible sur http://localhost:8080 (serveur db, utilisateur/mot de passe/base : voir .env).
Pour relancer le pipeline de collecte depuis zéro (nécessite Chrome/Chromium installé localement pour collecte-liens, voir Limites) :
make scrape-liens DECENNIE=2010s # remplit liens_films
make scrape-films # scrape le détail -> data/raw/films/*.json
make nettoyer # charge data/raw/films/ dans PostgreSQLChaque module de src/ic05/analyse/ formule une analyse sur les données récupérées :
| Module | Intérêt |
|---|---|
stats_temporel.py |
Vérifie l'hypothèse (doublons de titre/année) plutôt que de la laisser ouverte ; ajoute l'évolution de l'engagement (nb de votants) |
stats_notoriete.py |
Acteurs "notables" détectés génériquement (percentile de vues), test de Mann-Whitney (avec/sans acteur notable) et régression OLS log(vues) ~ acteur + pays + genre + année pour isoler l'effet acteur du reste |
stats_pays.py |
ANOVA (le pays a-t-il un effet significatif sur la note ? avec quelle taille d'effet η²) + carte choroplèthe interactive |
stats_genres.py |
Heatmap genre × pays généralisée à tous les pays significatifs, test du χ² d'indépendance genre/pays, évolution des proportions de genres dans le temps |
reseau.py |
Graphe acteur-acteur (networkx) avec centralités et détection de communautés (Louvain), graphe réalisateur-acteur pour détecter les duos récurrents, graphe de co-occurrence des genres - rendu interactif exporté en HTML (pyvis) |
make analyser (ou ic05 analyser) exécute les cinq modules et écrit :
- des figures statiques dans
output/figures/*.png; - des figures interactives (carte, heatmap) dans
output/figures/*.html; - des graphes réseau interactifs dans
output/network/*.html; - l'ensemble des résultats chiffrés (tests statistiques, régressions, tops) dans
output/resultats.json.
Le notebook notebooks/exploration.ipynb montre comment appeler ces modules directement pour explorer les résultats de façon interactive.
- Catalogue et Selenium : parcourir le catalogue par décennie (
collecte-liens) nécessite un Chrome/Chromium installé localement (Selenium pilote un navigateur headless, la grille de posters étant peuplée côté client). Le scraping du détail d'un film (collecte-films), en revanche, ne dépend plus de Selenium et peut donc tourner plus facilement (voirtests/test_collecte_films.py). - Volumétrie : Letterboxd semble être derrière Cloudflare ; un run de collecte massif (des dizaines de milliers de films) reste lent et doit respecter un délai entre requêtes (
IC05_SCRAPE_DELAYdans.env), sans garantie de ne jamais être bloqué. - Pays déduit de la langue : quand une fiche film n'indique pas de pays de production, on retombe sur la langue principale (
est_langue = TRUEdansfilm_pays) - approximation déjà présente dans le projet original.