Passer au contenu

Comment transformer les Web Scrapers en API de données

Le Web scraping est une technique puissante pour extraire de grandes quantités de données de sites Web. Cependant, récupérer des données à la demande peut s’avérer difficile. Dans ce guide complet, nous apprendrons comment transformer un web scraper Python en une API de données en temps réel à l'aide de FastAPI.

Pourquoi créer une API Scraping ?

Voici quelques avantages clés de la fourniture de données récupérées via une API :

  • Données en temps réel – Les API peuvent récupérer des données à la demande plutôt que de s’appuyer sur des lots obsolètes mis en cache.

  • Requêtes personnalisées – Les API permettent une interrogation personnalisée des données plutôt que de simplement vider des sites Web entiers.

  • Évolutivité – Les API gèrent les pics de trafic et s’adaptent à des milliers d’utilisateurs.

  • Fiabilité – Les API réessayent les requêtes ayant échoué et mettent en œuvre une logique de scraping robuste.

  • Flexibilité – Les API peuvent implémenter diverses méthodes de livraison de données telles que des webhooks.

  • Abstraction – Les API cachent la logique et l’infrastructure de scraping complexes aux consommateurs d’API.

Architecture de l'API de grattage

À un niveau élevé, une API de web scraping suit cette architecture :

Architecture de l'API de grattage

  1. L'API reçoit une demande
  2. Scraper récupère les données
  3. Les données sont mises en cache
  4. L'API renvoie des données récupérées

Les composants clés sont :

  • Serveur API – Gère les demandes des clients et fournit des données récupérées.

  • Racloir – Récupère les données à la demande des sites cibles.

  • Cache – Stocke les données récupérées pour éviter le grattage redondant.

  • Base de données – Stocke éventuellement les données récupérées pour une analyse historique.

Pourquoi utiliser FastAPI ?

Il existe de nombreux excellents frameworks API. Pour les grattoirs, je recommande API rapide parce que:

  • FastAPI est très rapide – parfait pour les API de récupération de données.

  • Il fournit des documents automatiques, une validation, une sérialisation, etc.

  • Prend en charge asyncio pour le scraping asynchrone.

  • Simple à utiliser et à apprendre. Flexible pour les petites et grandes API.

  • Possède un excellent écosystème d'outils de scraping comme httpx, parsel, etc.

Configuration de l'API de grattage

Nous utiliserons les bibliothèques principales suivantes :

  • API rapide – Notre framework API

  • httpx – Client HTTP asynchrone

  • parcelle – Analyseur HTML/XML

  • loguru – Utilitaire de journalisation

Installez les packages :

pip install fastapi uvicorn httpx parsel loguru

Pour ce guide, nous récupérerons quelques données boursières de base de Yahoo Finance.

Créer l'API

Configurons l'application FastAPI initiale avec un seul point de terminaison :

from fastapi import FastAPI

app = FastAPI() 

@app.get("/stock/{symbol}")  
async def get_stock(symbol: str):
    return { "stock": symbol } 

Cette API de base renvoie simplement le symbole boursier que nous fournissons.

Démarrons le serveur et testons-le :

uvicorn main:app --reload
import httpx

print(httpx.get("http://localhost:8000/stock/AAPL").json())

# {‘stock‘: ‘AAPL‘}

Notre API est prête à recevoir des requêtes. Ensuite, faisons en sorte qu'il récupère certaines données.

Grattage des données boursières

Pour obtenir les données d'un titre, nous allons :

  1. Construisez l'URL Yahoo Finance à partir du symbole
  2. Récupérer la page HTML
  3. Analyser les valeurs à l'aide de XPath
from parsel import Selector # for xpath parsing

async def scrape_stock(symbol: str):

    url = f"https://finance.yahoo.com/quote/{symbol}"

    async with httpx.AsyncClient() as client:
       response = await client.get(url)

    sel = Selector(response.text)

    # parse summary values 
    values = sel.xpath(‘//div[contains(@data-test,"summary-table")]//tr‘)
    data = {}
    for value in values:
        label = value.xpath("./td[1]/text()").get()
        val = value.xpath("./td[2]/text()").get()
        data[label] = val

    # parse price 
    data["price"] = sel.xpath(‘//fin-streamer[@data-symbol=$symbol]/@value‘, symbol=symbol).get()

    return data

Ce scraper renvoie un dictionnaire contenant les données analysées. Connectons-le à notre API :

from fastapi import FastAPI
from yahoo_finance import scrape_stock

app = FastAPI()

@app.get("/stock/{symbol}")
async def get_stock(symbol: str):
    data = await scrape_stock(symbol) 
    return data

Désormais, lorsque nous appellerons notre API, elle récupérera les dernières données :

http http://localhost:8000/stock/AAPL

HTTP/1.1 200 OK
Content-Length: 340

{
  "52 Week Range": "142.00 - 182.94",
  "Beta (5Y Monthly)": "1.25", 
  "Diluted EPS (ttm)": "6.05",
  "Earnings Date": "Oct 27, 2022",
  "Ex-Dividend Date": "Aug 05, 2022",
  "Forward Dividend & Yield": "0.92 (0.59%)",
  "Market Cap": "2.44T",
  "Open": "156.76",
  "PE Ratio (ttm)": "25.60",
  "Previous Close": "153.72",
  "Price": "155.33",  
  "Volume": "53,978,024"
}

Ajout de la mise en cache

Gratter chaque demande est un gaspillage. Ajoutons la mise en cache pour ne gratter un stock qu'une fois toutes les 5 minutes.

Nous utiliserons un simple dict pour stocker les données récupérées saisies par symbole boursier :

STOCK_CACHE = {} 

async def scrape_stock(symbol):

   if symbol in STOCK_CACHE:
       return STOCK_CACHE[symbol] 

   data = ... # scrape 
   STOCK_CACHE[symbol] = data

   return data

Désormais, les requêtes répétées renverront les données mises en cache au lieu de les supprimer à chaque fois.

Nous pouvons également vider périodiquement l'ancien cache :

import time

CACHE_MAX_AGE = 300 # seconds

async def clear_expired_cache():
   curr_time = time.time()
   for symbol, data in STOCK_CACHE.items():
       if curr_time - data["ts"] > CACHE_MAX_AGE:
           del STOCK_CACHE[symbol]

# run every 5 minutes   
clear_cache_task = asyncio.create_task(clear_expired_cache()) 

Cela garantit que notre cache ne se développera pas de manière illimitée.

Ajout de webhooks

Pour les tâches de scraping longues, nous pouvons utiliser des webhooks pour renvoyer les résultats de manière asynchrone :

@app.get("/stock/{symbol}")
async def get_stock(symbol: str, webhook: str):

   if webhook:
      task = asyncio.create_task(fetch_stock(symbol, webhook))
      return {"msg": "Fetching data"}

   data = await scrape_stock(symbol)  
   return data

async def fetch_stock(symbol, url):
   data = await scrape_stock(symbol)

   async with httpx.AsyncClient() as client:
      await client.post(url, json=data)

Désormais, au lieu d'attendre la fin du scraping, notre API renverra immédiatement un statut et fournira les données de manière asynchrone au webhook de rappel.

Nous pouvons tester cela en utilisant un outil comme Webhook.site.

Mise à l'échelle de l'API Scraping

À mesure que le trafic augmente, voici quelques techniques de mise à l’échelle :

  • Ajouter la mise en cache de l'API – Utilisez un cache comme Redis pour réduire la charge de scraping.

  • Exécuter plusieurs processus – Évoluez sur plusieurs cœurs/serveurs avec gunicorn.

  • Déchargement du grattage – Déplacez les grattoirs vers des travailleurs comme Celery ou RabbitMQ.

  • Utiliser les services de scraping – Tirez parti des API de scraping comme Scrapfly ou ScraperAPI.

  • Optimiser les grattoirs – Assurez-vous que les grattoirs sont efficaces et évitez les interdictions.

  • Ajouter des bases de données – Stockez les données récupérées dans des bases de données pour une analyse plus approfondie.

Conclusion

Dans ce guide, nous avons construit une API de web scraping en Python à l'aide de FastAPI. Les principaux points à retenir sont :

  • FastAPI fournit un excellent cadre pour récupérer les API.

  • Nous pouvons générer des scrapers pour récupérer des données à la demande.

  • La mise en cache et les webhooks aident à surmonter les limitations du scraping.

  • Il existe de nombreuses stratégies d’optimisation et de mise à l’échelle à mesure que le trafic augmente.

Les API de scraping libèrent la richesse des données sur les sites Web. En générant des API au lieu du scraping statique, nous pouvons fournir des données personnalisées à faible latence et à grande échelle.

Mots clés:

Prendre part à la conversation

Votre adresse email n'apparaitra pas. Les champs obligatoires sont marqués *