Meteen naar de inhoud

Hoe u webscrapers kunt omzetten in data-API's

Webscraping is een krachtige techniek om grote hoeveelheden gegevens uit websites te halen. Het kan echter een uitdaging zijn om gegevens op aanvraag te verzamelen. In deze uitgebreide handleiding leren we hoe u een Python-webschraper kunt omzetten in een realtime gegevens-API met behulp van FastAPI.

Waarom een ​​scraping-API bouwen?

Hier zijn enkele belangrijke voordelen van het leveren van geschraapte gegevens via een API:

  • Realtime gegevens – API's kunnen gegevens op verzoek verzamelen in plaats van te vertrouwen op verouderde, in de cache opgeslagen batches.

  • Aangepaste zoekopdrachten – API's maken het op maat opvragen van gegevens mogelijk in plaats van alleen maar hele websites te dumpen.

  • Schaalbaarheid – API's verwerken verkeerspieken en schalen naar duizenden gebruikers.

  • Betrouwbaarheid – API's proberen mislukte verzoeken opnieuw en implementeren robuuste scraping-logica.

  • Flexibiliteit – API's kunnen verschillende methoden voor het leveren van gegevens implementeren, zoals webhooks.

  • Abstractie – API's verbergen complexe scraping-logica en infrastructuur voor API-consumenten.

API-architectuur schrapen

Op een hoog niveau volgt een webscraping-API deze architectuur:

API-architectuur schrapen

  1. API ontvangt verzoek
  2. Scraper haalt gegevens op
  3. Gegevens worden in de cache opgeslagen
  4. API retourneert geschrapte gegevens

De belangrijkste componenten zijn:

  • API-server – Behandelt klantverzoeken en levert geschraapte gegevens.

  • Krabber – Haalt gegevens op aanvraag op van doelsites.

  • cache – Slaat geschrapte gegevens op om overtollig schrapen te voorkomen.

  • Database – Slaat optioneel geschraapte gegevens op voor historische analyse.

Waarom FastAPI gebruiken?

Er zijn veel uitstekende API-frameworks beschikbaar. Voor schrapers raad ik aan FastAPI omdat:

  • FastAPI is erg snel – perfect voor API's voor het schrapen van gegevens.

  • Het biedt automatische documenten, validatie, serialisatie, enz.

  • Ondersteunt asyncio voor asynchroon schrapen.

  • Eenvoudig te gebruiken en te leren. Flexibel voor kleine tot grote API's.

  • Heeft een geweldig ecosysteem van scrapingtools zoals httpx, parsel, enz.

API-instellingen schrapen

We gebruiken de volgende kernbibliotheken:

  • FastAPI – Ons API-framework

  • httpx – Asynchrone HTTP-client

  • perceel – HTML/XML-parser

  • loguru – Logprogramma

Installeer de pakketten:

pip install fastapi uvicorn httpx parsel loguru

Voor deze handleiding verzamelen we enkele basisaandelengegevens van Yahoo Finance.

Maak de API

Laten we de initiële FastAPI-app instellen met één enkel eindpunt:

from fastapi import FastAPI

app = FastAPI() 

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

Deze basis-API retourneert alleen het aandelensymbool dat wij leveren.

Laten we de server starten en testen:

uvicorn main:app --reload
import httpx

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

# {‘stock‘: ‘AAPL‘}

Onze API is klaar om verzoeken te ontvangen. Laten we vervolgens wat gegevens laten schrapen.

Voorraadgegevens schrapen

Om de gegevens van een aandeel te verkrijgen, doen we het volgende:

  1. Bouw de Yahoo Finance-URL op basis van het symbool
  2. Haal de HTML-pagina op
  3. Parseer waarden met 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

Deze scraper retourneert een woordenboek met de geparseerde gegevens. Laten we het verbinden met onze 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

Wanneer we nu onze API aanroepen, haalt deze de nieuwste gegevens op:

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

Caching toevoegen

Elk verzoek inwilligen is verspilling. Laten we caching toevoegen, zodat we slechts één keer per 5 minuten een voorraad schrapen.

We gebruiken een eenvoudige dict om de geschraapte gegevens op te slaan, ingetoetst door het aandelensymbool:

STOCK_CACHE = {} 

async def scrape_stock(symbol):

   if symbol in STOCK_CACHE:
       return STOCK_CACHE[symbol] 

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

   return data

Nu zullen herhaalde verzoeken gegevens in de cache retourneren in plaats van elke keer te schrappen.

We kunnen ook periodiek de oude cache wissen:

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

Dit zorgt ervoor dat onze cache niet onbeperkt groeit.

Webhooks toevoegen

Voor lange scraptaken kunnen we webhooks gebruiken om resultaten asynchroon te retourneren:

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

In plaats van te wachten tot het scrapen is voltooid, retourneert onze API nu onmiddellijk een status en levert de gegevens asynchroon aan de callback-webhook.

We kunnen dit testen met behulp van een tool als Webhook.site.

De Scraping-API schalen

Naarmate het verkeer toeneemt, volgen hier enkele schaaltechnieken:

  • Voeg API-caching toe – Gebruik een cache zoals Redis om de scrapingbelasting te verminderen.

  • Voer meerdere processen uit – Schaal over kernen/servers heen met gunicorn.

  • Schrapen uitladen – Verplaats schrapers naar werkers zoals Celery of RabbitMQ.

  • Gebruik schrapservices – Maak gebruik van scraping-API's zoals Scrapfly of ScraperAPI.

  • Optimaliseer schrapers – Zorg ervoor dat schrapers efficiënt zijn en vermijd verboden.

  • Databases toevoegen – Bewaar geschraapte gegevens in databases voor verdere analyse.

Conclusie

In deze handleiding hebben we een webscraping-API in Python gebouwd met behulp van FastAPI. De belangrijkste afhaalrestaurants zijn:

  • FastAPI biedt een uitstekend raamwerk voor het schrapen van API's.

  • We kunnen scrapers genereren om gegevens op aanvraag op te halen.

  • Caching en webhooks helpen bij het overwinnen van scrapingbeperkingen.

  • Er zijn veel optimalisatie- en schaalstrategieën naarmate het verkeer groeit.

Door API's te schrapen, wordt de rijkdom aan gegevens op websites ontgrendeld. Door API's te genereren in plaats van statisch scrapen, kunnen we op schaal aangepaste gegevens met lage latentie leveren.

Tags:

Doe mee aan het gesprek

Uw e-mailadres wordt niet gepubliceerd. Verplichte velden zijn gemarkeerd *