Ich habe mir einen eigenen NBA-Stats-Server auf dem Raspberry 5 gebaut
Seit ich denken kann, bin ich NBA-Fan. Die 90er Jahre waren für mich die absolute Goldzeit. Jordan, Pippen, die Bulls-Dynastie, Shaq, Kobe… ich habe jede Minute verschlungen, jede Highlight-VHS zerlegt, jede Statistik ausgeschnitten und jedes Stickeralbum gefüttert. Basketball war damals nicht einfach nur ein Sport, es war meine Religion.
Ist es für mich heute irgendwie noch immer, aber heutzutage ist alles digital. Jede Bewegung wird getrackt, jede Aktion landet in Datenbanken von Apps oder Websites und dort nicht selten hinter Paywalls.
Da ich aber nun mal irgendwie ein Nerd bin und Wert auf Datenschutz lege, hab ich beschlossen, Statistiken und Analysen unter meine Kontrolle zu bringen und mir meine eigene kleine NBA-Statistikzentrale auf einem Raspberry Pi zu bauen.
Hardware & System
Der Raspberry Pi 5 ist ideal. Nicht nur, weil ich eh einen rumzufliegen habe. Er ist kompakt, stromsparend, leistungsfähig genug für Python, PostgreSQL und gelegentliche Analysen. Ich nutze Raspberry Pi OS Lite 64-Bit, weil es schlank ist, stabil läuft und genau das bietet, was ein Server-Setup braucht. Also kein Desktop-Schnickschnack.
Grundsetup
sudo apt update
sudo apt upgrade -y
sudo apt install postgresql postgresql-contrib git python3-pip -y
Diese Befehle aktualisieren das System, installieren PostgreSQL (die Datenbank), Git (für Codeverwaltung) und Python 3 mit Pip (damit ich Bibliotheken wie Requests oder SQLAlchemy installieren kann).
Dazu ein Python virtuelles Environment, um System-Pakete sauber zu halten:
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install requests sqlalchemy psycopg2-binary pandas matplotlib
Damit erstelle ich eine isolierte Python-Umgebung, in der ich alle benötigten Bibliotheken installieren kann, ohne dass die System-Pakete durcheinander geraten.
PostgreSQL-Setup
sudo -i -u postgres
createuser pi -P # Passwort eingeben (ggf. URL-kodiert, falls Sonderzeichen @, : etc.)
createdb nba
psql -c "GRANT ALL PRIVILEGES ON DATABASE nba TO pi;"
exit
Hier erstelle ich einen neuen Datenbank-User pi, lege eine Datenbank nba an und gebe dem volle Rechte. So sind meine Daten sauber getrennt und sicher.
Balldontlie API mit Pagination (next_cursor)
Die balldontlie API liefert JSON-Daten zu Spielern, Teams, Spielen und Statistiken. Bisschen nervig, aber seit kurzem braucht es einen API-Key (kostenlos nach Registrierung).
import requests
API_KEY = "DEIN_API_KEY"
headers = {"Authorization": f"Bearer {API_KEY}"}
url = "https://api.balldontlie.io/nba/v1/players?per_page=100"
cursor = None
while True:
paged_url = url
if cursor:
paged_url += f"&cursor={cursor}"
response = requests.get(paged_url, headers=headers)
response.raise_for_status() # Fehler sofort sichtbar
data = response.json()
# Hier die Daten speichern...
cursor = data.get('meta', {}).get('next_cursor')
if not cursor:
break # keine weiteren Seiten, Schleife beenden
Dieser Code ruft alle Spieler der NBA ab. Mit next_cursor hole ich sie Seite für Seite ab, bis keine mehr übrig ist. response.raise_for_status() sorgt dafür, dass Fehler wie ein falscher API-Key sofort angezeigt werden.
Daten in PostgreSQL speichern (SQLAlchemy 2.x-konform)
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('postgresql://pi:DEIN_PASSWORT@localhost/nba')
metadata = MetaData()
players = Table('players', metadata,
Column('id', Integer, primary_key=True),
Column('first_name', String),
Column('last_name', String),
Column('team', String)
)
metadata.create_all(engine)
# Beispiel Insert
with engine.begin() as conn:
for p in data['data']:
conn.execute(
players.insert().values(
id=p['id'],
first_name=p['first_name'],
last_name=p['last_name'],
team=p['team']['full_name']
)
)
Hier wird die Datenbanktabelle players angelegt und die Spielerdaten eingefügt. Das with engine.begin() sorgt dafür, dass die Daten sicher und atomar eingefügt werden. SQLAlchemy 2.x verwendet diese Schreibweise statt des alten engine.execute().
Automatische Updates per Cronjob
crontab -e
Öffnet die Cronjob-Liste für den User.
0 2 * * * /usr/bin/python3 /home/pi/nba_fetch.py >> /home/pi/nba_fetch.log 2>&1
Script ausführbar machen (chmod +x nba_fetch.py) oder explizit mit /usr/bin/python3 starten. Absolute Pfade verwenden, Cron kennt andere Umgebungsvariablen.
Grafana auf dem Raspberry Pi
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/grafana.gpg
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
sudo systemctl enable grafana-server
sudo systemctl start grafana-server
sudo systemctl status grafana-server
Diese Befehle fügen das offizielle Grafana-Repository hinzu, installieren die Software und sorgen dafür, dass sie automatisch startet. Grafana ist ein Tool, mit dem ich meine NBA-Daten in übersichtlichen Diagrammen und Dashboards anzeigen und interaktiv auswerten kann. Mit sudo systemctl status grafana-server kann ich prüfen, ob Grafana läuft.
Zugriff: http://IP_DEINES_PI:3000/
Login: admin / admin (Passwort sofort ändern!)
PostgreSQL als Datenquelle
- Host:
localhost:5432 - Datenbank:
nba - User:
pi - Passwort: DEIN_PASSWORT
Damit kann Grafana direkt auf die NBA-Daten zugreifen.
Erstes Dashboard – Spieler pro Team
SELECT team, COUNT(*) AS player_count
FROM players
GROUP BY team
ORDER BY player_count DESC;
Diese SQL-Abfrage zählt, wie viele Spieler jedes Team hat. In Grafana wähle ich den Panel-Typ „Bar chart“ und kann die Daten interaktiv anzeigen. Optional: im Query inspector prüfen, ob Daten zurückkommen, falls das Diagramm leer bleibt.
Ready
Jetzt habe ich ne vollständige, lokale NBA-Statistikzentrale
- Daten automatisch per API abrufen (inkl.
next_cursorPagination) - In PostgreSQL speichern (SQLAlchemy 2.x-konform)
- Automatisch aktualisieren per Cronjob
- Interaktiv visualisieren mit Grafana
Endlich mein eigener NBA-Stats-Server, genau so, wie ich ihn will.
Piehnat