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 :
- L'API reçoit une demande
- Scraper récupère les données
- Les données sont mises en cache
- 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 :
- Construisez l'URL Yahoo Finance à partir du symbole
- Récupérer la page HTML
- 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.