Files
mesh-configs/skills/devops/web-hosting-deployment/references/deutsche-bahn-timetable-data.md
T

8.3 KiB
Raw Blame History

Deutsche Bahn / Fahrplan-Datenabruf: Realität & Workarounds (2026-08-08)

Nützlich beim Bau von Reise-/Verbindungs-Websites (z.B. Zugverbindungs-Liste urlaub.perfekt-ohne-en.de), die echte Fahrtzeiten abfragen müssen.

Kernbefund: DB automatisiert NICHT von einem Server aus

Beim Versuch, echte deutsche Nahverkehrs-Verbindungen (RE/RB/S-Bahn) automatisiert zu bekommen, schlugen ALLE Server-seitigen Wege fehl:

Weg Ergebnis
https://www.bahn.de/ im Browserbase-Browser ❌ Bot-Erkennung: Fehler 753 „Verhalten Ihres Browsers ähnelt dem eines Bots" — auch im echten Browser (stealth-Warnung: „Running WITHOUT residential proxies").
DB-Web-API (/web/api/reiseloesung) ❌ OPS_BLOCKED
VRR EFA (efa.vrr.de) ⚠️ Funktionierte für Verbund-Haltestellen (Radevormwald/Remscheid), liefert aber keine Fernziele (Frankfurt/Hannover außerhalb des VRR-Gebiets) — nur lokale Verbunde.
api.transport.rest / GTFS ❌ nicht erreichbar (DNS/Timeout)
Web-Suche (Firecrawl) ❌ nicht konfiguriert

Warum es so ist: bahn.de + DB-API betreiben aktive Anti-Bot-Abwehr, unabhängig davon ob der Zugriff von einem Python-Skript oder einem headless-Browser kommt. Das ist eine bewusste, dauerhafte Serverseite — kein Setup-Fehler, der sich fixen lässt.

Funktionierende Workarounds

  1. Regionale EFA (Verbund-API) für lokale Strecken: https://efa.vrr.de/std3/ (JSON via XSLT_STOPFINDER_REQUEST / XSLT_TRIP_REQUEST2) funktioniert für Haltestellen im jeweiligen Verkehrsverbund. Nur für Nahziele im Verbund nutzen, nicht für Fernziele.

  2. Nutzer liefert die Zeiten manuell (bewährt): Auf bahn.de / DB-Navigator funktioniert die Suche für menschliche Nutzer einwandfrei. Bei 1-2 Zielen: Nutzer gibt die schnellste Verbindung durch ☞ Master trägt die echten Zeiten ein. Das ist zuverlässig + kostenlos + umgeht die Bot-Sperre.

  3. Schätz-/Richtwerte mit ehrlichem Hinweis (Standard-Fall für viele Ziele): Wenn keine echte Verifikation möglich ist, Netzwerk-Takte/Geographie als Richtwert aufführen und auf der Seite + im PDF deutlich markieren: „vor der Fahrt gegen bahn.de/DB-Navigator prüfen". NICHT als echte Fahrzeit ausgeben.

  4. DB-Deep-Link pro Verbindung (Nutzer-Idee): statt die Zeiten automatisiert zu verifizieren, einen Link auf die DB-Suche VORBEFÜLLEN, den der Nutzer in seinem Browser öffnet. Da der Nutzer keine Bot-Sperre hat, sieht er dort die echten aktuellen Zeiten.

    ⚠️ WICHTIG — LIVE-VERIFIZIERT am 2026-08-08 (Muss über den früheren Stand): Die Deutsche Bahn bietet 2026 KEIN stabiles URL-Deep-Link-Format mehr an. Alle vorher als „stabil" geltenden Schemata sind empirisch tot:

    • ❌ reiseauskunft.bahn.de/bin/query.exe/dn?... → toter DNS (DNS_PROBE_FINISHED_NXDOMAIN), Domain existiert nicht mehr.
    • ❌ reiseauskunft.de/bin/query.exe/... → toter Pfad („Seite existiert nicht mehr").
    • ❌ www.bahn.de/buchung/fahrplan/suche?... mit Query-Parametern → DB-SPA übernimmt die Eingaben NICHT (zeigt nur leere Suchseite).
    • ❌ www.bahn.de/buchung/fahrplan/suche#query=start:…;destination:… → undokumentierte interne SPA-Fragmentroute, bricht (Einträge werden nicht übernommen, nur Fehlermeldung → leere Suche).

    ✅ Funktionierende, ehrliche Lösung 2026-08-08: Ein einfacher, zuverlässiger Link auf die noch erreichbare Sucheseite https://www.bahn.de/buchung/fahrplan/suche auf einer Zwischenseite, die Start+Ziel (readonly) anzeigt + einen Button „In DB-Suche öffnen" (neuer Tab) + Fallback-Link. Der Nutzer tippt Start/Ziel dort manuell ein (10 Sekunden) — aber der Link landet garantiert auf einer funktonierenden Seite statt auf einem Deadlink. Verifiziere jede DB-URL mit curl -s -o /dev/null -w "%{http_code}" VOR dem Einsatz — Sub-Agenten-Reviews können veraltete Formate als „korrekt/getestet" bestätigen, ohne es live geprüft zu haben (passiert hier real). Eine muntere Vorbefüllung ist 2026 nicht mehr zuverlässig möglich; wer sie trotzdem versucht, scheitert an der SPA.

    ⚠️ Die offizielle DB-API (api.deutschebahn.com) wäre die robusteste Quelle für programmatische Zeiten, braucht aber einen API-Key — und ist weiterhin vorbehaltlich Bot/Key-Zugang zu prüfen.

Google Maps als ÖPNV-Quelle? NEIN — ICE lässt sich nicht rausfiltern (2026-08-08)

Nutzer-Idee beim Bau einer Verbindungs-Site: „Kann man Google Maps-Öffis-Daten nehmen?" — ehrliche Antwort, die in Zukunft nicht neu hergeleitet werden muss:

  • Google Maps zeigt ÖPNV (Bus, S-Bahn, RE, ICE, IC), aber als schnellste Route schlägt es standardmäßig ICE/IC vor.
  • Es gibt KEINEN sauberen „nur Regionalverkehr / Deutschlandticket"-Filter in Google Maps (nur ein grober Transit-Filter). regional-only von ICE zu trennen ist nicht zuverlässig möglich.
  • Dieselben GTFS-Fahrpläne wie überall (DB/Verkehrsverbünde) — also nichts Besseres /qchlechteres als reiseauskunft.
  • Google Maps blockt zudem Bots (wie die DB), sodass saubere Automatisierung nicht klappt.
  • Fazit: Für eine „kein ICE / nur Deutschlandticket“-Anforderung ist unser DB-Deep-Link zur noch erreichbaren Sucheseite (https://www.bahn.de/buchung/fahrplan/suche, ohne Vorbefüllung, siehe Abschnitt 4) besser — die Seite hat einen expliziten Deutschlandticket-Filter, den der Nutzer einen Klick entfernt sieht. (Der früher genannte query.exe/dn-Deep-Link ist tot — siehe Abschnitt 4.)

Label nach ECHTER Fahrtzeit, nicht nach Wunsch-Kategorie (2026-08-08)

Nutzer: „Nicht EINE Fahrt 5-7h in der Liste, ich sehe nur <5h oder >7h." Ursache war zweifach, beides wartbar:

  1. Label-Mismatch-Bug: 168 Ebene-2-Zeilen waren als Kernziel (5-7h) markiert, obwohl ihre Fahrtzeiten real 0,3–4h waren. Beim Seeden wird gern „5-7h" als Wunsch-Category an die Fernziele gehängt. Fix: Kategorie aus der echten Fahrtzeit ableiten (Parser ~h → float, dann <5 → unter 5h, 5–7 → Kernziel (5-7h), >7 → über 7h), nicht als Design-Wunsch beim INSERT setzen.
  2. Geographische Realität: per reinem Nahverkehr sind echte 5–7h-Ziele selten (viele Großstädte entweder <3h Nahzone oder >8h Fernzone). Das ist kein Bug, sondern Physik — die Seite soll alle Kategorien ehrlich anzeigen und „5-7h" nur dort führen, wo es sie wirklich gibt (ab weiter südlich/östlich gelegenen Städten: Frankfurt→München/Dresden, Hannover→Stuttgart etc.).

Ehrlichkeits-Regel (Nutzer erwartet dies)

  • Niemals fabrizierte „fahrhatige" Verbindungen oder konkrete Uhrzeiten als real ausgeben, wenn sie nur geschätzt sind.
  • Schätzwerte IMMER als Bereich (~5-5,5 h) + mit Verweis auf bahn.de labeln.

RAPTOR statt Dijkstra für echte GTFS-Routing (2026-08-19, Team-Unraid)

Für GTFS-Verbindungssuche auf großen Knoten ist einfacher Dijkstra instabil — bei benachbarten Zielen kamen absurde Differenzen (Mainz→Berlin 11h vs. Nachbar Wiesbaden 3h). Standard-Lösung ist RAPTOR (Round-Based Public Transit Optimized Route): erweitert die Reichweite Runde um Runde über echte Trips und behandelt "Zug nehmen → aussteigen → umsteigen" natürlich, statt auf einem statischen Graphen zu kürzen.

  • Stop-Mehrdeutigkeit (ein Bahnhof, mehrere stop_ids, nur 1 aktiv in st) → eindeutig über parent_station/Geo-Mapping auflösen.
  • Umstiegsbuffer (4 min) + sauberes Ziel-Stop-Matching verhindern die "benachbarte Ziele, extrem abweichende Zeiten"-Inkonsistenz.
  • Verifizieren mit Plausibilitäts-Ankern (z.B. Trier→Saarbrücken ~1h, Köln→Aachen ~50min) — nie den Algorithmus blind vertrauen.
  • Wenn der Nutzer einen Konkreten echten Wert liefert (z.B. „Trier 6h"), ihn bevorzugt übernehmen statt der Schätzung.

Fakten zur Ziel-Menge (Zugverbindungs-Website, Kontext)

  • Schwelle kann variieren (250k → 100k Einwohner → weit mehr Ziele, ~33 in dieser Site).
  • Reichweite per reinem Nahverkehr (kein ICE) ab Bergisch-Land/Ruhrgebiet: die meisten Großstädte sind entweder <3h (Nahzone: Köln, Düsseldorf, Ruhrgebiet) oder >8h (Fernzone: Berlin, München). Echte 5–7h-Ziele sind selten — das sind genau die Grenzfälle (Frankfurt ~5h, Wiesbaden ~5,5h, Hannover ~5,5–6,5h, Kassel ~6,5–7h). Regional-Agenten (SwitchHermes/LaptopHermes) lieferten dazu gute Geographie-Analysen.