12 KiB
Story 2.1: Implémenter le scraper Twitter avec rate limiting
Status: review
Story
As a développeur, I want implémenter un scraper Twitter avec gestion des rate limits, So que je peux collecter des tweets sur les matchs de football sans dépasser les limites API.
Acceptance Criteria
Given une clé API Twitter est configurée When le scraper Twitter est exécuté Then il collecte des tweets pour un match donné avec mots-clés pertinents And il respecte la limite de 1000 requêtes/heure And il gère les erreurs de rate limit avec retry avec backoff exponentiel And les tweets collectés sont stockés avec timestamp, texte, engagement (retweets, likes)
Given le scraper détecte un rate limit When la limite est atteinte (>90% utilisation) Then une alerte est loggée And le scraper passe en mode priorisation (matchs VIP uniquement) And les données collectées sont sauvegardées avant arrêt
Tasks / Subtasks
-
Installer les dépendances Twitter API (AC: #1)
- Installer
tweepyoutweepy-asyncpour Twitter API - Configurer les credentials Twitter API
- Créer le module scraper dans
backend/app/scrapers/ - Configurer la connexion Twitter API
- Vérifier l'authentification Twitter
- Installer
-
Implémenter le collecteur de tweets (AC: #1)
- Créer la fonction de recherche de tweets par mots-clés
- Extraire les données: texte, timestamp, retweets, likes
- Implémenter le parsing des données Twitter
- Stocker les tweets dans la base de données
- Gérer les erreurs de connexion/timeout
-
Implémenter le rate limiting (AC: #1, #2)
- Configurer le rate limiter pour 1000 req/heure
- Implémenter le suivi de l'utilisation API
- Implémenter l'alerte quand utilisation > 90%
- Implémenter le retry avec backoff exponentiel
- Configurer le mode priorisation (matchs VIP)
-
Implémenter le mode dégradé (AC: #2)
- Définir les matchs VIP dans la configuration
- Implémenter la priorisation dynamique des matchs
- Sauvegarder les données collectées avant arrêt
- Logger l'alerte de mode dégradé
- Vérifier que le scraper continue avec autres sources
-
Créer les schémas de base de données pour tweets (AC: #1)
- Créer la table
tweetsdans SQLite - Définir les colonnes: id, text, created_at, retweet_count, like_count
- Ajouter les colonnes pour match_id et source
- Créer les indexes appropriés
- Générer et appliquer les migrations
- Créer la table
-
Tester le scraper Twitter (AC: #1, #2)
- Tester la collecte de tweets pour un match
- Vérifier que le rate limiting fonctionne
- Tester le mode priorisation VIP
- Vérifier que les données sont stockées correctement
- Tester le retry avec backoff exponentiel
Dev Notes
Architecture Patterns et Contraintes
Stack Technique Imposé:
- Twitter API: Tweepy ou tweepy-async
- Rate Limiting: 1000 requêtes/heure (gratuit)
- Database: SQLite avec Drizzle (Next.js) et SQLAlchemy (FastAPI)
- Queue: RabbitMQ pour traitement asynchrone (Phase 2+)
- Logging: Structuré avec alertes pour rate limits
Configuration Requise:
- Credentials Twitter API: Bearer token ou OAuth 1.0a
- Rate limiter: 1000 req/heure avec alerte à 90%
- Mode dégradé: Priorisation des matchs VIP
- Stockage: Table
tweetsdans SQLite
Intégration avec Architecture Globale:
- Part de la pondération: Twitter 60% (Reddit 25%, RSS 15%)
- Queue RabbitMQ pour découplage scraping et analyse
- Même structure de données que Reddit et RSS
- Format cohérent pour fusion multi-sources
Conventions de Nommage:
- Table:
tweets(snake_case pluriel) - Colonnes:
tweet_id,text,created_at,match_id(snake_case) - Functions:
scrape_twitter_match(),handle_rate_limit()(snake_case) - Variables:
max_tweets_per_hour,is_vip_match(snake_case/UPPER_SNAKE_CASE)
Source Tree Components à Toucher
Fichiers à créer:
backend/app/scrapers/(répertoire scrapers)backend/app/scrapers/__init__.pybackend/app/scrapers/twitter_scraper.py(module Twitter)backend/app/models/tweet.py(modèle SQLAlchemy pour tweets)backend/app/schemas/tweet.py(schéma Pydantic pour tweets)
Fichiers à modifier:
backend/requirements.txt(ajouter tweepy)src/db/schema.ts(ajouter schéma Drizzle pour tweets - Next.js)
Project Structure Notes
Alignment with unified project structure:
- ✅ Twitter scraper dans
backend/app/scrapers/comme spécifié - ✅ Rate limiting à 1000 req/heure comme spécifié
- ✅ Pondération Twitter 60% comme spécifié dans epics
- ✅ Mode dégradé avec priorisation VIP comme spécifié
Conventions de code à respecter:
- Gestion asynchrone avec async/await (si tweepy-async)
- Logging structuré pour monitoring
- Retry avec backoff exponentiel
- Mode dégradé avec priorisation
Intégration avec architecture existante:
- SQLite partagé avec Next.js et FastAPI
- Même convention de nommage
snake_case - Préparation pour intégration RabbitMQ (Phase 2+)
Technical Requirements
Configuration Twitter API:
import tweepy
# Configuration Twitter API
auth = tweepy.BearerToken("YOUR_BEARER_TOKEN")
client = tweepy.Client(bearer_token=auth)
# Rate limiting configuration
MAX_TWEETS_PER_HOUR = 1000
RATE_LIMIT_ALERT_THRESHOLD = 0.9 # 90%
VIP_MATCH_IDS = [1, 2, 3] # IDs des matchs VIP
Scraper Twitter:
async def scrape_twitter_match(match_id: str, keywords: List[str]):
# Collecte des tweets pour un match
tweets = client.search_recent_tweets(
query=f"{' OR '.join(keywords)}",
max_results=100,
tweet_fields=['created_at', 'public_metrics', 'text']
)
# Stockage dans la base de données
for tweet in tweets.data:
save_tweet_to_db(tweet, match_id)
Rate Limiting:
from time import sleep
from math import exp
async def handle_rate_limit(api_calls_remaining: int):
if api_calls_remaining < (MAX_TWEETS_PER_HOUR * (1 - RATE_LIMIT_ALERT_THRESHOLD)):
log_alert("Rate limit approaching 90%")
if api_calls_remaining == 0:
# Mode dégradé: priorisation VIP
enable_vip_mode_only()
wait_time = 3600 # Attendre 1 heure
sleep(wait_time)
Schéma Table Tweets:
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import relationship
class Tweet(Base):
__tablename__ = "tweets"
id = Column(Integer, primary_key=True)
tweet_id = Column(String, unique=True, index=True)
text = Column(String, nullable=False)
created_at = Column(DateTime, nullable=False, index=True)
retweet_count = Column(Integer, default=0)
like_count = Column(Integer, default=0)
match_id = Column(Integer, ForeignKey("matches.id"))
source = Column(String, default="twitter") # twitter, reddit, rss
Architecture Compliance
Conformité avec Architecture Decision Document:
✅ External API Management:
- Rate limiting à 1000 req/heure
- Alertes prédictives (>90% utilisation)
- Mode dégradé avec priorisation VIP
✅ Data Architecture:
- Table
tweetsdans SQLite - Conventions
snake_case - Indexes optimisés
✅ Code Organization:
- Module dans
backend/app/scrapers/ - Séparation clear des responsabilités
Library/Framework Requirements
Packages:
tweepyoutweepy-async
File Structure Requirements
backend/
├── app/
│ ├── scrapers/
│ │ ├── __init__.py
│ │ └── twitter_scraper.py
│ ├── models/
│ │ └── tweet.py
│ └── schemas/
│ └── tweet.py
Testing Requirements
- Tests de collecte de tweets
- Tests de rate limiting
- Tests de mode dégradé
- Tests de stockage en base de données
References
- [Source: _bmad-output/planning-artifacts/epics.md#Story-2.1]
Dev Agent Record
Agent Model Used
GLM-4.7
Completion Notes List
- ✅ Dependencies Twitter API: Tweepy 4.14.0 ajouté à requirements.txt
- ✅ Twitter Scraper Module: Module complet avec rate limiting et mode dégradé
- Classe
TwitterScraperavec authentification Twitter API - Tracking en temps réel des appels API (max 1000/heure)
- Alertes prédictives à 90% d'utilisation
- Mode dégradé automatique pour matchs VIP uniquement
- Retry avec backoff exponentiel
- Logging structuré pour monitoring
- Classe
- ✅ Database Schema: Table
tweetscréée dans SQLite- Colonnes: id, tweet_id, text, created_at, retweet_count, like_count, match_id, source
- Indexes: tweet_id (unique), created_at, match_id, composite (match_id, source)
- Migration Alembic: 20260117_0001_create_tweets_table.py
- ✅ Pydantic Schemas: Schémas de validation pour tweets
- TweetBase, TweetCreate, TweetResponse
- TweetListResponse avec pagination
- TweetStatsResponse pour statistiques
- ✅ Drizzle Schema: Schéma pour Next.js (src/db/schema.ts)
- Table
tweetsavec conventions TypeScript - Mapping snake_case ↔ camelCase
- Table
- ✅ Tests Unitaires: Tests complets pour scraper et modèle
- test_twitter_scraper.py: 10+ tests pour rate limiting, mode VIP, scraping
- test_tweet_model.py: 8+ tests pour modèle SQLAlchemy
- ✅ Documentation: README complet dans backend/app/scrapers/
- Guide d'installation et configuration
- Exemples d'utilisation
- Documentation API et dépannage
Technical Decisions
Pourquoi Tweepy vs tweepy-async:
- Choisie
tweepysynchrone pour simplicité initiale - Peut migrer vers
tweepy-asyncdans Phase 2+ si performance nécessaire - Rate limiting natif de Tweepy suffisant pour Phase 1
Rate Limiting Strategy:
- Tracking côté client (self.api_calls_made)
- Alertes prédictives à 90% pour éviter blocages
- Mode dégradé automatique: matchs VIP seulement
- Backoff exponentiel: minimum 1 minute, maximum 1 heure
Database Schema Design:
- Convention
snake_casecohérente avec SQLAlchemy - Indexes optimisés pour requêtes fréquentes:
tweet_idunique pour éviter doublonscreated_atpour tri temporelmatch_idetmatch_id+sourcepour filtrage
sourcecolonne pour multi-source future (Reddit, RSS)
Mode Dégradé Implementation:
- VIP match IDs configurables dans scraper
- Activation automatique quand rate limit atteint
- Alertes loggées avec niveau WARNING
- Sauvegarde automatique avant arrêt
Integration Points
Backend Integration:
- Module accessible via
app.scrapers.twitter_scraper - Factory function:
create_twitter_scraper() - Compatible avec SQLAlchemy sessions via
save_tweets_to_db()
Frontend Integration (Next.js):
- Schéma Drizzle dans
src/db/schema.ts - Partage de la même base SQLite
- Conventions TypeScript (camelCase) automatiques
Future Integration (Phase 2+):
- RabbitMQ queue pour découplage scraping/analyse
- Workers asynchrones pour multi-source
- Dashboard monitoring temps réel
File List
Fichiers créés:
backend/app/scrapers/__init__.pybackend/app/scrapers/twitter_scraper.pybackend/app/scrapers/README.mdbackend/app/models/tweet.pybackend/app/schemas/tweet.pybackend/alembic/versions/20260117_0001_create_tweets_table.pybackend/tests/__init__.pybackend/tests/test_twitter_scraper.pybackend/tests/test_tweet_model.pybackend/tests/run_tests.py
Fichiers modifiés:
backend/requirements.txt(ajouté tweepy==4.14.0)backend/app/models/__init__.py(ajouté Tweet import)backend/app/schemas/__init__.py(ajouté Tweet schemas)chartbastan/src/db/schema.ts(ajouté tweets table)