Salta al contenuto

Come trasformare i Web Scraper in API di dati

Il web scraping è una tecnica potente per estrarre grandi quantità di dati dai siti web. Tuttavia, raccogliere dati su richiesta può essere difficile. In questa guida completa impareremo come trasformare un web scraper Python in un'API di dati in tempo reale utilizzando FastAPI.

Perché creare un'API di scraping?

Ecco alcuni vantaggi chiave derivanti dalla fornitura di dati ricavati tramite un'API:

  • Dati in tempo reale – Le API possono acquisire dati su richiesta anziché fare affidamento su batch obsoleti e memorizzati nella cache.

  • Query personalizzate – Le API consentono interrogazioni personalizzate dei dati anziché limitarsi a scaricare interi siti Web.

  • Scalabilità – Le API gestiscono i picchi di traffico e si adattano a migliaia di utenti.

  • Affidabilità – Le API riprovano le richieste non riuscite e implementano una solida logica di scraping.

  • Flessibilità – Le API possono implementare vari metodi di consegna dei dati come i webhook.

  • Astrazione – Le API nascondono logiche e infrastrutture di scraping complesse ai consumatori API.

Scraping dell'architettura API

Ad alto livello, un'API di web scraping segue questa architettura:

Scraping dell'architettura API

  1. L'API riceve la richiesta
  2. Il raschiatore recupera i dati
  3. I dati vengono memorizzati nella cache
  4. L'API restituisce dati raschiati

I componenti chiave sono:

  • Server API – Gestisce le richieste dei clienti e fornisce dati raschiati.

  • Raschietto – Recupera i dati su richiesta dai siti di destinazione.

  • Cache – Memorizza i dati raschiati per evitare raschiamenti ridondanti.

  • Banca Dati – Memorizza facoltativamente i dati raschiati per l'analisi storica.

Perché utilizzare FastAPI?

Esistono molti framework API eccellenti. Per i raschiatori, lo consiglio API veloce perché:

  • FastAPI è molto veloce, perfetta per le API di scraping dei dati.

  • Fornisce documenti automatici, convalida, serializzazione, ecc.

  • Supporta asyncio per lo scraping asincrono.

  • Semplice da usare e imparare. Flessibile per API di piccole e grandi dimensioni.

  • Ha un ottimo ecosistema di strumenti di scraping come httpx, parsel, ecc.

Configurazione dell'API di scraping

Utilizzeremo le seguenti librerie principali:

  • API veloce – Il nostro framework API

  • httpx – Client HTTP asincrono

  • pacco – Analizzatore HTML/XML

  • loguru – Utilità di registrazione

Installa i pacchetti:

pip install fastapi uvicorn httpx parsel loguru

Per questa guida, estrarremo alcuni dati azionari di base da Yahoo Finance.

Crea l'API

Impostiamo l'app FastAPI iniziale con un singolo endpoint:

from fastapi import FastAPI

app = FastAPI() 

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

Questa API di base restituisce semplicemente il simbolo azionario che forniamo.

Avviamo il server e testiamolo:

uvicorn main:app --reload
import httpx

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

# {‘stock‘: ‘AAPL‘}

La nostra API è pronta a ricevere richieste. Quindi facciamo in modo che racimola alcuni dati.

Raschiatura dei dati di magazzino

Per ottenere i dati di un titolo:

  1. Crea l'URL di Yahoo Finance dal simbolo
  2. Recupera la pagina HTML
  3. Analizzare i valori utilizzando 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

Questo raschietto restituisce un dizionario contenente i dati analizzati. Colleghiamolo alla nostra 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

Ora, quando chiamiamo la nostra API, recupererà i dati più recenti:

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"
}

Aggiunta della memorizzazione nella cache

Raschiare su ogni richiesta è uno spreco. Aggiungiamo la memorizzazione nella cache in modo da raschiare uno stock solo una volta ogni 5 minuti.

Useremo un semplice dict per memorizzare i dati raschiati digitati dal simbolo di borsa:

STOCK_CACHE = {} 

async def scrape_stock(symbol):

   if symbol in STOCK_CACHE:
       return STOCK_CACHE[symbol] 

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

   return data

Ora le richieste ripetute restituiranno i dati memorizzati nella cache invece di eseguire lo scraping ogni volta.

Possiamo anche svuotare periodicamente la vecchia 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()) 

Ciò garantisce che la nostra cache non cresca senza limiti.

Aggiunta di webhook

Per lavori di scraping lunghi, possiamo utilizzare i webhook per restituire i risultati in modo asincrono:

@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)

Ora, invece di attendere il completamento dello scraping, la nostra API restituirà immediatamente uno stato e consegnerà i dati in modo asincrono al webhook di callback.

Possiamo testarlo utilizzando uno strumento come Webhook.sito.

Ridimensionare l'API di scraping

Man mano che il traffico aumenta, ecco alcune tecniche di ridimensionamento:

  • Aggiungi la memorizzazione nella cache dell'API – Utilizza una cache come Redis per ridurre il carico di scraping.

  • Esegui più processi – Scalabilità su core/server con gunicorn.

  • Scaricare la raschiatura – Sposta i raschiatori su lavoratori come Celery o RabbitMQ.

  • Utilizza i servizi di raschiamento – Sfrutta le API di scraping come Scrapfly o ScraperAPI.

  • Ottimizza i raschiatori – Garantire che gli scraper siano efficienti ed evitare i divieti.

  • Aggiungi database – Archiviare i dati raschiati nei database per ulteriori analisi.

Conclusione

In questa guida abbiamo creato un'API di web scraping in Python utilizzando FastAPI. I punti chiave sono:

  • FastAPI fornisce un framework eccellente per lo scraping delle API.

  • Possiamo generare scraper per recuperare dati su richiesta.

  • La memorizzazione nella cache e i webhook aiutano a superare le limitazioni dello scraping.

  • Esistono molte strategie di ottimizzazione e ridimensionamento man mano che il traffico cresce.

Le API di scraping sbloccano la ricchezza di dati sui siti Web. Generando API anziché lo scraping statico, possiamo fornire dati personalizzati a bassa latenza su larga scala.

Tag:

Partecipa alla conversazione

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati con *