Files

133 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.