Scraper SN: Sachsen (ParlDok, Wahl 2029-09-02) #26

Closed
opened 2026-04-08 22:22:11 +02:00 by tobias · 3 comments
Owner

Wahltermin

2029-09-02 — Sachsen (SN), aktuell 8. Wahlperiode.

Backend

Feld Wert
Doku-System ParlDok
Base-URL https://edas.landtag.sachsen.de
dokukratie-Scraper sn
Drucksachen-Format 8/1234

Adapter-Strategie

ParlDok (1/2)ParLDokAdapter für MV existiert bereits. SN nutzt EDAS, das laut bundeslaender.py auf ParlDok basiert. Erste Frage beim Reverse-Engineering: ist es ParlDok 8.x mit demselben JSON-API-Schema (/parldok/Fulltext/Search) oder eine ältere Version? Wenn 8.x → einfacher Registry-Eintrag mit anderem base_url und prefix.

Was zu tun ist

  1. Live-Backend anschauen — falls SPA, HAR-Trace einer realen Suche aus DevTools ziehen (siehe Vorgehen in #12 für ParlDok bzw. #13 für eUI).
  2. Adapter in webapp/app/parlamente.py implementieren — entweder als neue Subklasse von ParlamentAdapter oder als zweiter Registry-Eintrag eines existierenden parametrisierbaren Adapters.
  3. Eintrag in der ADAPTERS-Registry am Ende der Datei.
  4. Smoke-Test lokal: ADAPTERS["SN"].search("Schule", limit=10) liefert echte Anträge mit Datum + Fraktionen, sortiert newest-first.
  5. Aktivierung via Folge-Issue (siehe Hängt mit … zusammen unten) — dieses Issue ist nur der Adapter selbst, nicht das Indexieren der Wahlprogramme oder das Frontend-aktiv-Setzen.

Akzeptanzkriterien

  • parlamente.py::ADAPTERS["SN"] existiert und ist instanziierbar
  • search(query="Schule", limit=10) liefert ≥3 echte Drucksachen mit korrektem Datum, Fraktionen, PDF-Link
  • get_document(drucksache) für eine reale Drucksache der laufenden WP liefert das Dokument zurück
  • download_text(drucksache) extrahiert Text aus dem PDF
  • Folge-Issue für Frontend-Aktivierung verlinkt
## Wahltermin **2029-09-02** — Sachsen (SN), aktuell 8. Wahlperiode. ## Backend | Feld | Wert | |---|---| | Doku-System | `ParlDok` | | Base-URL | https://edas.landtag.sachsen.de | | dokukratie-Scraper | `sn` | | Drucksachen-Format | `8/1234` | ## Adapter-Strategie **ParlDok (1/2)** — `ParLDokAdapter` für MV existiert bereits. SN nutzt EDAS, das laut bundeslaender.py auf ParlDok basiert. Erste Frage beim Reverse-Engineering: ist es ParlDok 8.x mit demselben JSON-API-Schema (`/parldok/Fulltext/Search`) oder eine ältere Version? Wenn 8.x → einfacher Registry-Eintrag mit anderem `base_url` und `prefix`. ## Was zu tun ist 1. Live-Backend anschauen — falls SPA, HAR-Trace einer realen Suche aus DevTools ziehen (siehe Vorgehen in #12 für ParlDok bzw. #13 für eUI). 2. Adapter in `webapp/app/parlamente.py` implementieren — entweder als neue Subklasse von `ParlamentAdapter` oder als zweiter Registry-Eintrag eines existierenden parametrisierbaren Adapters. 3. Eintrag in der `ADAPTERS`-Registry am Ende der Datei. 4. Smoke-Test lokal: `ADAPTERS["SN"].search("Schule", limit=10)` liefert echte Anträge mit Datum + Fraktionen, sortiert newest-first. 5. Aktivierung via Folge-Issue (siehe `Hängt mit … zusammen` unten) — dieses Issue ist nur der Adapter selbst, nicht das Indexieren der Wahlprogramme oder das Frontend-aktiv-Setzen. ## Akzeptanzkriterien - [ ] `parlamente.py::ADAPTERS["SN"]` existiert und ist instanziierbar - [ ] `search(query="Schule", limit=10)` liefert ≥3 echte Drucksachen mit korrektem Datum, Fraktionen, PDF-Link - [ ] `get_document(drucksache)` für eine reale Drucksache der laufenden WP liefert das Dokument zurück - [ ] `download_text(drucksache)` extrahiert Text aus dem PDF - [ ] Folge-Issue für Frontend-Aktivierung verlinkt
Author
Owner

Hinweise aus dokukratie/sn.yml

Klassifikations-Korrektur: SN ist in bundeslaender.py als doku_system="ParlDok" eingetragen, aber dokukratie/sn.yml zeigt ASP.NET Webforms mit __CALLBACKID-Postbacks — das ist eine ganz andere Engine als ParlDok 8.x von MV.

  • Endpoint: https://edas.landtag.sachsen.de/parlamentsdokumentation/parlamentsarchiv/suchmaske_einfach.aspx
  • Trefferliste: https://edas.landtag.sachsen.de/parlamentsdokumentation/parlamentsarchiv/trefferliste.aspx?NavSeite=1
  • Vorgangsdetail: https://edas.landtag.sachsen.de/parlamentsdokumentation/parlamentsarchiv/treffer_vorgang.aspx
  • Form: .//form[@id="aspnetForm"]
  • POST-Felder: __CALLBACKID, __CALLBACKPARAM, plus die langen ctl00_masterContentCallback_content_suchmaske_*-IDs (typische ASP.NET-Webforms-Field-Namen)
  • Items: .//td[@class="dxdvItem_EDAS dx-al"]
  • Drucksache: .//td[@class="text"]/a[contains(text(), "Drs")]
  • Title: .//td[@class="text"]/b
  • PDF-URL via body[@onload]-Skript-Trick

Synergie: Komplett eigenständig, NICHT mit MV ParlDok teilbar. Eigener EDASAdapter oder ASPNetWebFormsAdapter. Reverse-Engineering der __CALLBACKID-Postbacks ist die größte Herausforderung — ASP.NET-Webforms haben üblicherweise auch ein __VIEWSTATE-Feld, das mit jedem Request mitgeschickt werden muss.

bundeslaender.py-Korrektur: SN.doku_system="ParlDok" → sollte "ASP.NET/EDAS" oder "Eigensystem" sein, weil keine ParlDok-Kompatibilität mit MV/HH besteht.

## Hinweise aus dokukratie/sn.yml **Klassifikations-Korrektur**: SN ist in `bundeslaender.py` als `doku_system="ParlDok"` eingetragen, aber dokukratie/sn.yml zeigt **ASP.NET Webforms** mit `__CALLBACKID`-Postbacks — das ist eine **ganz andere Engine** als ParlDok 8.x von MV. - Endpoint: `https://edas.landtag.sachsen.de/parlamentsdokumentation/parlamentsarchiv/suchmaske_einfach.aspx` - Trefferliste: `https://edas.landtag.sachsen.de/parlamentsdokumentation/parlamentsarchiv/trefferliste.aspx?NavSeite=1` - Vorgangsdetail: `https://edas.landtag.sachsen.de/parlamentsdokumentation/parlamentsarchiv/treffer_vorgang.aspx` - Form: `.//form[@id="aspnetForm"]` - POST-Felder: `__CALLBACKID`, `__CALLBACKPARAM`, plus die langen `ctl00_masterContentCallback_content_suchmaske_*`-IDs (typische ASP.NET-Webforms-Field-Namen) - Items: `.//td[@class="dxdvItem_EDAS dx-al"]` - Drucksache: `.//td[@class="text"]/a[contains(text(), "Drs")]` - Title: `.//td[@class="text"]/b` - PDF-URL via `body[@onload]`-Skript-Trick **Synergie:** Komplett eigenständig, **NICHT mit MV ParlDok teilbar**. Eigener `EDASAdapter` oder `ASPNetWebFormsAdapter`. Reverse-Engineering der `__CALLBACKID`-Postbacks ist die größte Herausforderung — ASP.NET-Webforms haben üblicherweise auch ein `__VIEWSTATE`-Feld, das mit jedem Request mitgeschickt werden muss. **bundeslaender.py-Korrektur:** `SN.doku_system="ParlDok"` → sollte `"ASP.NET/EDAS"` oder `"Eigensystem"` sein, weil keine ParlDok-Kompatibilität mit MV/HH besteht.
tobias added the
phase-3
scraper
labels 2026-04-08 23:16:37 +02:00
Author
Owner

Phase-J-Recherche-Befund (autonomer Run #59)

HAR-Trace TEMP/edas.landtag.sachsen.de.har analysiert. EDAS lässt sich nicht autonom adaptieren, zwei harte Hindernisse:

1. Vollwertiger ASP.NET-Webforms-Postback-Flow

3-Step-Workflow:

  1. POST suchmaske_einfach.aspx (status 200) — initial Form-State setzen mit gigantischem __VIEWSTATE (>5KB base64) plus alle DevExpress-Control-IDs als Hidden-Felder
  2. POST suchmaske_einfach.aspx (status 0) — Click auf den Suchbutton, browser-side abgebrochen, DevExpress Callback-API mit eigenem Wire-Format. __EVENTTARGET=ctl00$masterContentCallback$content$suchmaske$tblSearch$tabSuche$panelUmSuchmaskeEinfach$suchmaskeEinfachCallback$btn_EinfSuche
  3. GET trefferliste.aspx?NavSeite=1 — lädt die Result-Page aus der Server-Session

Direktzugriff auf trefferliste.aspx ohne vorherige Session redirected zu EDASError.aspx?error=session. Ein autonomer Adapter müsste den vollen Postback-Flow inklusive __VIEWSTATE-Deserialisierung und DevExpress-Wire-Format simulieren — geschätzter Aufwand: 8–15h Reverse-Engineering plus laufende Wartung bei jedem Server-Update.

2. robots.txt: Disallow: /

User-agent: *
Disallow: /

Der Sächsische Landtag verbietet ausdrücklich automatisches Crawling. Ein scrapender Adapter wäre rechtlich/ethisch fragwürdig — das ist ein qualitatives Signal, das die anderen 9 aktivierten Landtage nicht haben.

Empfehlung

Phase J vertagt. Sinnvolle Alternativen für künftige Sessions:

  1. Anfrage an Sachsen-Landtag für maschinenlesbaren API-Zugang oder Robots-Allowlist für unsere User-Agent — der politische Bildungsauftrag wäre ein gutes Argument
  2. Manuelle CSV-Export-Pipeline für Sachsen — der Landtag bietet vermutlich CSV-/XLS-Exports an, die periodisch heruntergeladen und in unsere DB importiert werden können
  3. Headless-Browser-Adapter (Playwright/Selenium) — würde das ASP.NET-Postback-Problem trivial lösen, ist aber Adapter-Schicht-übergreifend ein neuer Stack

Die anderen drei Phase-J-Adapter aus dem autonomen Run sind erfolgreich:

  • #56 BundestagAdapter (DIP-API) — Phase G, Commit 0f7d35f
  • #24 HE StarWebHEAdapter — Phase H, Commit 4a8986e
  • #21 HB PARiSHBAdapter — Phase I, Commit 278d74f
## Phase-J-Recherche-Befund (autonomer Run #59) HAR-Trace `TEMP/edas.landtag.sachsen.de.har` analysiert. **EDAS lässt sich nicht autonom adaptieren**, zwei harte Hindernisse: ### 1. Vollwertiger ASP.NET-Webforms-Postback-Flow 3-Step-Workflow: 1. POST `suchmaske_einfach.aspx` (status 200) — initial Form-State setzen mit gigantischem `__VIEWSTATE` (>5KB base64) plus alle DevExpress-Control-IDs als Hidden-Felder 2. POST `suchmaske_einfach.aspx` (status 0) — Click auf den Suchbutton, browser-side abgebrochen, **DevExpress Callback-API** mit eigenem Wire-Format. `__EVENTTARGET=ctl00$masterContentCallback$content$suchmaske$tblSearch$tabSuche$panelUmSuchmaskeEinfach$suchmaskeEinfachCallback$btn_EinfSuche` 3. GET `trefferliste.aspx?NavSeite=1` — lädt die Result-Page aus der Server-Session Direktzugriff auf `trefferliste.aspx` ohne vorherige Session redirected zu `EDASError.aspx?error=session`. Ein autonomer Adapter müsste den vollen Postback-Flow inklusive `__VIEWSTATE`-Deserialisierung und DevExpress-Wire-Format simulieren — geschätzter Aufwand: 8–15h Reverse-Engineering plus laufende Wartung bei jedem Server-Update. ### 2. `robots.txt: Disallow: /` ``` User-agent: * Disallow: / ``` Der Sächsische Landtag verbietet ausdrücklich automatisches Crawling. Ein scrapender Adapter wäre rechtlich/ethisch fragwürdig — das ist ein qualitatives Signal, das die anderen 9 aktivierten Landtage nicht haben. ### Empfehlung Phase J **vertagt**. Sinnvolle Alternativen für künftige Sessions: 1. **Anfrage an Sachsen-Landtag** für maschinenlesbaren API-Zugang oder Robots-Allowlist für unsere User-Agent — der politische Bildungsauftrag wäre ein gutes Argument 2. **Manuelle CSV-Export-Pipeline** für Sachsen — der Landtag bietet vermutlich CSV-/XLS-Exports an, die periodisch heruntergeladen und in unsere DB importiert werden können 3. **Headless-Browser-Adapter** (Playwright/Selenium) — würde das ASP.NET-Postback-Problem trivial lösen, ist aber Adapter-Schicht-übergreifend ein neuer Stack Die anderen drei Phase-J-Adapter aus dem autonomen Run sind erfolgreich: - **#56 BundestagAdapter** (DIP-API) — Phase G, Commit `0f7d35f` - **#24 HE StarWebHEAdapter** — Phase H, Commit `4a8986e` - **#21 HB PARiSHBAdapter** — Phase I, Commit `278d74f`
Author
Owner

Erledigt durch Phase J reaktiviert / Commit 19e5fe4

Weg drumherum gefunden: User exportiert wöchentlich manuell aus der EDAS-Suchmaske einen XML-Dump aller Anträge (bis 2500 Treffer/Export). Datei wird unter data/sn-edas-export.xml ins persistent volume des Containers gelegt.

  • SNEdasXmlAdapter parst das XML lokal — keine HTTP-Calls gegen edas.landtag.sachsen.de während des search()/get_document()
  • download_text() resolved die echte PDF-URL on-demand über einen einzelnen GET gegen viewer_navigation.aspx (single GET, kein Postback) und holt dann das PDF von ws.landtag.sachsen.de/images/
  • Mapper-Erweiterung: BÜNDNISGRÜNE/Bündnisgrüne als Sachsen-spezifischer GRÜNE-Eigenname

Live verifiziert: 5 Klima-Anträge inkl. 8/2100 (GRÜNE Fahrradoffensive 2025), 7/2067 mit Koalitionssatz [CDU, SPD, GRÜNE].

Beide robotsignal-Probleme adressiert:

  1. ASP.NET-Postbacks: vermieden via XML-Export
  2. robots.txt: respektiert — wir crawlen edas.landtag.sachsen.de NICHT mehr automatisch, der user macht den Export manuell

Maintenance-Hinweis: das XML enthält die ZUM EXPORT-ZEITPUNKT vorhandenen 2500 neuesten Anträge. Bei wöchentlichem Update-Rhythmus reicht das gut aus, weil ~50-100 neue Anträge/Woche entstehen. Re-Upload via scp data/sn-edas-export.xml vserver:/opt/gwoe-antragspruefer/data/.

## Erledigt durch Phase J reaktiviert / Commit `19e5fe4` Weg drumherum gefunden: User exportiert wöchentlich manuell aus der EDAS-Suchmaske einen XML-Dump aller Anträge (bis 2500 Treffer/Export). Datei wird unter `data/sn-edas-export.xml` ins persistent volume des Containers gelegt. - `SNEdasXmlAdapter` parst das XML lokal — keine HTTP-Calls gegen edas.landtag.sachsen.de während des `search()`/`get_document()` - `download_text()` resolved die echte PDF-URL on-demand über einen einzelnen GET gegen `viewer_navigation.aspx` (single GET, kein Postback) und holt dann das PDF von `ws.landtag.sachsen.de/images/` - Mapper-Erweiterung: `BÜNDNISGRÜNE`/`Bündnisgrüne` als Sachsen-spezifischer GRÜNE-Eigenname Live verifiziert: 5 Klima-Anträge inkl. 8/2100 (GRÜNE Fahrradoffensive 2025), 7/2067 mit Koalitionssatz [CDU, SPD, GRÜNE]. Beide robotsignal-Probleme adressiert: 1. ASP.NET-Postbacks: vermieden via XML-Export 2. robots.txt: respektiert — wir crawlen edas.landtag.sachsen.de NICHT mehr automatisch, der user macht den Export manuell Maintenance-Hinweis: das XML enthält die ZUM EXPORT-ZEITPUNKT vorhandenen 2500 neuesten Anträge. Bei wöchentlichem Update-Rhythmus reicht das gut aus, weil ~50-100 neue Anträge/Woche entstehen. Re-Upload via `scp data/sn-edas-export.xml vserver:/opt/gwoe-antragspruefer/data/`.
Sign in to join this conversation.
No description provided.