Python Schulung – Kursbezug
Praxis-Merksatz: Ein gutes Script ist bedienbar (CLI), robust (Fehler/Logging) und wiederholbar (Scheduling).
Worum geht’s?
- CLI-Tools mit
argparse: Parameter, Defaults, Hilfe. - Dateien/Ordner:
pathlib, CSV/JSON lesen/schreiben. - APIs: HTTP-Requests (z.B.
requests) + Fehlerbehandlung. - Logging: nachvollziehbare Ausgaben (Info/Warn/Error).
- Scheduling: Cron (Linux/macOS) oder Aufgabenplanung (Windows).
Lehr-/Lernziele
- (LZ1) ein CLI-Skript mit Parametern und Hilfe bauen (
argparse). - (LZ2) Dateien/Ordner sicher verarbeiten (CSV/JSON, Pfade, Encoding).
- (LZ3) Daten über eine API abfragen und Fehler robust behandeln (Timeouts/Statuscodes).
- (LZ4) Logging + Scheduling so einsetzen, dass das Script zuverlässig „im Betrieb“ läuft.
1) CLI: Parameter mit argparse
import argparse
def parse_args():
parser = argparse.ArgumentParser(
prog="kurs_report",
description="Erzeugt einen Kurs-Report (Beispieldaten)."
)
parser.add_argument("--standort", default="Berlin", help="z.B. Berlin, Hamburg")
parser.add_argument("--format", default="Online", help="Online oder Praesenz")
parser.add_argument("--limit", type=int, default=10, help="Max. Anzahl Einträge")
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
print("Parameter:", args)2) Dateien/Ordner: pathlib + CSV/JSON
CSV lesen (Beispiel)
from pathlib import Path
import csv
pfad_csv = Path("daten") / "kurse.csv"
with pfad_csv.open("r", encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
kurse = list(reader)
print("Anzahl Kurse:", len(kurse))
print("Erster Eintrag:", kurse[0])JSON schreiben (Report)
from pathlib import Path
import json
report = {
"anbieter": "SemaTrain",
"standort": "Berlin",
"format": "Online",
"anzahl": 2,
"kurse": [
{"kursname": "Python Schulung", "preis_euro": 1390},
{"kursname": "Python Schulung", "preis_euro": 1490},
],
}
pfad_out = Path("out") / "report.json"
pfad_out.parent.mkdir(parents=True, exist_ok=True)
pfad_out.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
print("Geschrieben:", pfad_out)3) API-Abfrage: requests + Fehlerbehandlung
import requests
def lade_json(url: str, timeout_s: int = 8) -> dict:
try:
resp = requests.get(url, timeout=timeout_s)
resp.raise_for_status() # wirft Exception bei 4xx/5xx
return resp.json()
except requests.Timeout:
raise RuntimeError("Timeout: API antwortet nicht rechtzeitig.")
except requests.HTTPError as e:
raise RuntimeError(f"HTTP-Fehler: {e}")
except requests.RequestException as e:
raise RuntimeError(f"Request-Fehler: {e}")
# Beispiel (URL ersetzen!)
# daten = lade_json("https://api.example.com/kurse")
# print(daten)Hinweis: Installation (venv + pip)
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux:
source .venv/bin/activate
pip install requests4) Logging: statt nur print()
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s"
)
logger = logging.getLogger("kurs_tool")
def main():
logger.info("Start: Kurs-Tool")
standort = "Berlin"
try:
# hier würde echte Arbeit passieren
logger.info("Standort: %s", standort)
logger.info("Report erstellt.")
except Exception:
logger.exception("Unerwarteter Fehler")
raise
finally:
logger.info("Ende.")
if __name__ == "__main__":
main()5) Scheduling: täglich laufen lassen
Cron (Linux/macOS)
# crontab -e
# täglich um 07:15 Uhr:
15 7 * * * /pfad/zur/.venv/bin/python /pfad/zum/kurs_report.py --standort Berlin --format Online >> /pfad/logs/report.log 2>&1Windows Aufgabenplanung
- „Aufgabe erstellen…“ → Trigger: täglich
- Aktion: Programm starten
- Programm/Skript:
python.exe - Argumente:
kurs_report.py --standort Berlin --format Online - „Start in“: Projektordner (damit relative Pfade stimmen)
Praxisaufgabe (Mini)
Sie bauen ein CLI-Tool kurs_report.py, das Kursdaten filtert und einen Report schreibt.
Beitrag zu den Lehr-/Lernzielen: LZ1 (CLI), LZ2 (Dateien/JSON), LZ4 (Logging/Scheduling) – optional LZ3 (API).
- (LZ1) Parameter:
--standort,--format,--out(Pfad), optional--limit. - (LZ2) Kursdaten aus CSV oder aus eingebauter Liste laden (Beispieldaten ok).
- (LZ2) Report als JSON schreiben (inkl. Zeitstempel, Anzahl, Liste).
- (LZ4) Logging: Start/Ende + Anzahl + Fehler als
logger.exception. - (Bonus, LZ3) Alternative Quelle: API laden (requests) und gleiches Report-Format schreiben.
Lösungsvorschlag anzeigen (kompakt, production-nah)
from __future__ import annotations
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from pathlib import Path
import argparse
import json
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s"
)
logger = logging.getLogger("kurs_report")
@dataclass(frozen=True)
class Kurs:
kursname: str
format: str # "Online" / "Praesenz"
standort: str
preis_euro: int
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description="Erzeugt einen Kurs-Report (Beispieldaten).")
p.add_argument("--standort", default="Berlin")
p.add_argument("--format", default="Online")
p.add_argument("--limit", type=int, default=10)
p.add_argument("--out", default="out/report.json")
return p.parse_args()
def lade_beispieldaten() -> list[Kurs]:
return [
Kurs("Python Schulung", "Online", "Berlin", 1390),
Kurs("Python Schulung", "Praesenz", "Hamburg", 1490),
Kurs("Python Schulung", "Online", "Hamburg", 1390),
Kurs("Python Schulung", "Praesenz", "Berlin", 1490),
]
def filtere_kurse(kurse: list[Kurs], standort: str, format: str, limit: int) -> list[Kurs]:
gefiltert = [k for k in kurse if k.standort == standort and k.format == format]
return gefiltert[: max(limit, 0)]
def schreibe_report(pfad: Path, standort: str, format: str, kurse: list[Kurs]) -> None:
report = {
"anbieter": "SemaTrain",
"erstellt_utc": datetime.now(timezone.utc).isoformat(),
"standort": standort,
"format": format,
"anzahl": len(kurse),
"kurse": [asdict(k) for k in kurse],
}
pfad.parent.mkdir(parents=True, exist_ok=True)
pfad.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
def main() -> int:
args = parse_args()
logger.info("Start | standort=%s | format=%s | limit=%s | out=%s", args.standort, args.format, args.limit, args.out)
try:
kurse = lade_beispieldaten()
auswahl = filtere_kurse(kurse, args.standort, args.format, args.limit)
schreibe_report(Path(args.out), args.standort, args.format, auswahl)
logger.info("OK | Report geschrieben | anzahl=%s", len(auswahl))
return 0
except Exception:
logger.exception("Fehler beim Erstellen des Reports")
return 2
finally:
logger.info("Ende")
if __name__ == "__main__":
raise SystemExit(main())Kurz-Takeaways
- LZ1: CLI = reproduzierbar + dokumentiert (Hilfe, Defaults, Parameter).
- LZ2:
pathlib+ CSV/JSON sind Standard für viele Automations-Jobs. - LZ3: APIs robust: Timeout, Statuscodes, Exceptions.
- LZ4: Logging + Scheduling machen aus einem Script ein „Betriebs-Tool“.