chartbastan/_bmad-output/implementation-artifacts/2-1-implémenter-le-scraper-twitter-avec-rate-limiting.md
2026-02-01 09:31:38 +01:00

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 tweepy ou tweepy-async pour 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
  • 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 tweets dans 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
  • 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 tweets dans 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:

  1. backend/app/scrapers/ (répertoire scrapers)
  2. backend/app/scrapers/__init__.py
  3. backend/app/scrapers/twitter_scraper.py (module Twitter)
  4. backend/app/models/tweet.py (modèle SQLAlchemy pour tweets)
  5. backend/app/schemas/tweet.py (schéma Pydantic pour tweets)

Fichiers à modifier:

  1. backend/requirements.txt (ajouter tweepy)
  2. 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 tweets dans SQLite
  • Conventions snake_case
  • Indexes optimisés

Code Organization:

  • Module dans backend/app/scrapers/
  • Séparation clear des responsabilités

Library/Framework Requirements

Packages:

  • tweepy ou tweepy-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 TwitterScraper avec 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
  • Database Schema: Table tweets créé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 tweets avec conventions TypeScript
    • Mapping snake_case ↔ camelCase
  • 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 tweepy synchrone pour simplicité initiale
  • Peut migrer vers tweepy-async dans 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_case cohérente avec SQLAlchemy
  • Indexes optimisés pour requêtes fréquentes:
    • tweet_id unique pour éviter doublons
    • created_at pour tri temporel
    • match_id et match_id+source pour filtrage
  • source colonne 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__.py
  • backend/app/scrapers/twitter_scraper.py
  • backend/app/scrapers/README.md
  • backend/app/models/tweet.py
  • backend/app/schemas/tweet.py
  • backend/alembic/versions/20260117_0001_create_tweets_table.py
  • backend/tests/__init__.py
  • backend/tests/test_twitter_scraper.py
  • backend/tests/test_tweet_model.py
  • backend/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)