In welchen Ländern schaltet ein B2B-Wettbewerber in diesem Quartal Anzeigen? Wie lange laufen die Creatives, welche ungefähre Reichweite kaufen sie ein – und welche Zielgruppensegmente sprechen sie an? All das steht oft öffentlich in der Anzeigenbibliothek der jeweiligen Plattform, die viele Anbieter aus Transparenzgründen bereitstellen müssen. Nur: Nach ein paar Minuten in den DevTools zeigt sich häufig, dass es keinen sauberen JSON-Endpunkt gibt. Stattdessen liefert der Server eine komplette HTML-Seite. Du schreibst einen Parser, die Daten kommen rein – und drei Wochen später rollt die Plattform ein Redesign aus. Der Parser wirft keinen Fehler, extrahiert aber kein einziges Feld mehr und liefert stillschweigend Datensätze voller leerer Werte zurück. Fast hättest du auf Basis leerer Daten entschieden.
Hier geht es darum, einen solchen Parser auch nach einem Redesign wartbar zu halten – und darum, was ein Modell bei dieser Wartung tatsächlich leisten kann. Vorab die Datengrenze: Sämtliche Daten stammen aus den öffentlichen Ad Libraries und Creative Centern der jeweiligen Plattformen. Der Zugriff erfolgt mit dem eigenen Account über den normalen Login, ohne Signaturen, ohne Umgehungen und ohne nicht öffentliche Endpunkte. Diese Grenze ist ausdrücklich Teil des Parser-Designs.
Warum B2B-Ad-Intelligence oft direkt aus HTML kommen muss
Auch bei Anzeigenbibliotheken gibt es sehr unterschiedliche Zugriffsmodelle: Manche Consumer-Angebote wie Metas Ad Library stellen strukturierte Suchen und JSON bereit. Bei anderen ist bereits für die Suche eine Session nötig. Die meisten B2B-Plattformen liefern ihre Anzeigenbibliothek dagegen ausschließlich als serverseitig gerendertes HTML – ganz ohne JSON-Endpunkt. Der Grund: Diese Bibliotheken sind ein Compliance-Artefakt, keine Produkt-API.
Sie existieren, um Vorgaben zur Werbetransparenz zu erfüllen, nicht damit Entwickler sie ansprechen können. Es gibt keine Versionsnummer, kein Changelog und kein Versprechen zur Abwärtskompatibilität. Die Seite richtet sich an Menschen, der Server rendert HTML – und genau diese Webseite ist die einzige „API“, die du bekommst.
Damit ist die Fragilität unvermeidbar: Du hängst an Implementierungsdetails einer fremden Oberfläche, der Anbieter kann sie jederzeit ändern und muss dich darüber nicht informieren. Wenn sich in einer JSON-API ein Feld ändert, ist das zumindest klar als Änderung erkennbar. Ein HTML-Redesign gilt auf Plattformseite dagegen bloß als normale Frontend-Iteration. Vermeiden lässt sich das nicht. Du kannst den Parser nur so bauen, dass er kontrolliert degradiert, leicht zu reparieren ist und nicht unbemerkt leere Werte zurückliefert.
Ein handgeschriebener Streaming-Parser spart vor allem Denkaufwand
Der naheliegende Weg wäre lxml oder BeautifulSoup: den kompletten DOM-Baum aufbauen und sich mit .find() bis zu den gesuchten Feldern vorarbeiten. Das funktioniert, ist für dieses Ziel aber die falsche Abstraktion. Der DOM-Baum ist ein Zwischenprodukt beim Rendern im Browser – MDN beschreibt das DOM als aus dem Dokument erzeugten Knotenbaum, auf den Skripte strukturell zugreifen. Wenn du nur einige Felder extrahieren willst, brauchst du diesen Baum nicht – und solltest dich nicht an seine Struktur ketten.
Meine Lösung ist eine Unterklasse von HTMLParser aus der Python-Standardbibliothek: etwas mehr als achthundert Zeilen, genauer 881, vollständig streamingbasiert. Einige starttag-, data- und endtag-Callbacks treiben eine State Machine an. Sie sammelt beim Durchlaufen der Seite Werte ein, gibt beim Erreichen einer Card-Grenze einen Datensatz aus, setzt ihren Zustand zurück und macht weiter. Ein vollständiger DOM-Baum entsteht zu keinem Zeitpunkt.
Der praktische Gewinn ist zweifach. Erstens Speicher: Das HTML einer Detailseite umfasst schnell einige zehn bis einige hundert KB. Ein DOM-Baum hält die komplette Seitenstruktur im Speicher, ein Streaming-Parser nur den Zustand „Wo bin ich gerade?“ und „Wie weit bin ich in dieser Card?“. Wichtiger ist aber der Denkaufwand. Sobald du .find('div').find('div')[2] schreibst, bindest du den Parser an eine hierarchische Position im DOM. Genau diese Hierarchie wird bei einem Redesign gern verschoben: ein zusätzlicher Container, ein ausgelagerter Wrapper – und jede Position kippt. Eine State Machine zwingt dich stattdessen zur Frage: „Ist das, was ich gerade lese, semantisch der Anfang einer Card, eine Impression-Zahl oder ein Targeting-Tag?“ Positionen ändern sich, Semantik deutlich seltener.
Drei Prinzipien, damit Redesigns nicht alles lahmlegen
Am Ende sind es drei Regeln, jede aus einem Redesign gelernt.
Erstens: an Semantik ankern, nicht an Positionen. Die State Machine reagiert auf semantische Signale: Feldbezeichnungen wie „total impressions“ oder „run dates“, rollenhaltige Marker und semantische Grenzen eines Blocks – niemals auf „den dritten Knoten von oben“. Der Test ist simpel: Würde die Extraktion weiter funktionieren, wenn das Element verschoben oder von einem zusätzlichen Container umschlossen wird? Falls ja, ist der Anker brauchbar. Positionsanker brechen beim ersten Redesign, semantische Anker überstehen die meisten rein visuellen Umbauten.
Zweitens: Bei fehlenden Feldern degradiert weiterarbeiten, statt Fehler zu werfen. Vor dem Sammeln jeder Card initialisierst du einen Template-Datensatz mit leeren Standardwerten: leere Strings für Text, None für Zahlen und leere Arrays für Listen. Was sich finden lässt, wird gefüllt; was fehlt, bleibt leer. Das Scheitern eines einzelnen Felds darf weder die gesamte Card noch die ganze Seite unbrauchbar machen. Fehlt bei einer Anzeige der CTA-Text, willst du trotzdem ihre Impressionen und Ziel-Länder erhalten. Es wäre das schlechteste Design, wenn ein unwichtiges fehlendes Feld die verwertbare Intelligence der Seite zerstört.
Drittens: Ergebnisse mit Vollständigkeit kennzeichnen, damit „leer“ und „defekt“ nicht gleich aussehen. Das ist der am leichtesten übersehene und teuerste Punkt. „0 Anzeigen geparst“ kann zwei völlig verschiedene Ursachen haben: Es gab tatsächlich keine Anzeigen, weil dieser Werbetreibende in diesem Quartal nichts geschaltet hat. Oder die Seitenstruktur hat sich geändert und kein Anker greift mehr – der Parser ist kaputt. Diese Fälle müssen im Rückgabewert unterscheidbar sein. Dafür gibst du zusätzliche Belege mit aus: die gezählte Zahl erkannter Cards, die von der Seite selbst ausgewiesene Gesamtzahl und den Pagination-Status. Eine Kombination wie „0 Cards gefunden, aber die Seitenmetadaten erwarten einen Batch und es gibt keinen Marker für eine Folgeseite“ lässt sich dann als Strukturbruch statt als echte Leere bewerten. Der Parser sollte einen klaren Fehler ausgeben, nicht stoisch eine leere Liste zurückliefern.
Auch die zuvor beschriebene Datengrenze wird auf dieser Ebene durchgesetzt: Der Parser prüft, ob er auf eine Login-Seite umgeleitet wurde. Erkennt er im Titel eine Login- oder Signup-Seite, bricht er mit einem Fehler ab, statt darauf loszuparsen. Er verarbeitet nur öffentliche Seiten, die mit dem eigenen Account normal sichtbar sind, hält an einer Login-Schranke an und versucht nie, sie zu umgehen.
Der eigentliche Wert steckt in den Filterdimensionen
Bis hierhin könnte der Eindruck entstehen, es gehe darum, jedes Feld jeder Anzeige sauber auszulesen. Das ist nicht der Kern. Die Felder einer einzelnen Anzeige sind für sich genommen wenig wertvoll. Entscheidend sind die Dimensionen, entlang derer sich Anzeigen aufschlüsseln lassen. Die Suchfilter der Ad Library sind bereits eine fertige Liste solcher Intelligence-Dimensionen. Als programmierbare Query-Parameter organisiert, liefern sie nicht „eine Anzeige“, sondern den Launch-Ausschnitt eines Wettbewerbers:
Land: In welchen Märkten wirbt ein Unternehmen – und in welchen nicht? Wenn ein B2B-Anbieter plötzlich in einem bestimmten Land Anzeigen schaltet, kann das einen Expansionsschritt früher sichtbar machen als die eigene Website.
Laufzeitfenster: Start- und Enddatum zeigen, wie lange ein Creative lief. Lange laufende Anzeigen sind ein starkes Signal: Niemand zahlt dauerhaft für ein Creative, das nicht konvertiert. Die Laufzeit ist damit selbst ein mit echtem Geld validiertes A/B-Testergebnis des Wettbewerbers.
Impression-Spanne: Min-/Max-Werte dienen als grober Proxy für das Budget. Absolut präzise sind sie nicht, für ein Ranking der priorisierten Kampagnen reichen sie aber aus.
Targeting-Facetten: Welche Targeting-Kriterien werden ein- oder ausgeschlossen? Das ist die direkteste Form von Zielgruppen-Intelligence: Wen hält die Gegenseite für einen wahrscheinlichen Käufer ihres Produkts?
Felder zu extrahieren ist das Mittel, diese Dimensionen sind das Ziel. Beim Bau des Parsers lohnt es sich daher, rückwärts zu denken: Welche kleinste Menge an Feldern muss zuverlässig extrahiert werden, damit sich nach diesen Dimensionen suchen und sortieren lässt? Der Rest der hübschen Zusatzfelder kann außen vor bleiben, ohne die Intelligence zu schwächen.
Nach dem Redesign: Modell auf alte und neue HTML-Version ansetzen
Ein Parser dieser Art wird bei einem Redesign irgendwann brechen. Genau beim Reparieren kann ein Modell sinnvoll helfen – aber ausdrücklich nicht beim Parsen selbst. Parsing ist deterministische Arbeit für eine fest kodierte State Machine; dort gehören keine Modellaufrufe hinein (deterministische Arbeit nicht an ein Modell delegieren – derselbe Grundsatz wie beim Reverse Engineering). Die Rolle des Modells liegt in der Wartung.
Der Ablauf: Öffne die Ad Library mit deinem eigenen Account, bewahre sowohl das vor dem Redesign gespeicherte HTML als auch das neue HTML auf, gib beides zusammen mit der Feldliste des aktuellen Parsers an das Modell. Es soll anhand des Old-New-Diffs benennen, bei welchen Feldern sich die semantischen Anker geändert haben, wo der neue Anker liegt und welche wenigen Zeilen minimal angepasst werden müssen. Das ist ein klassischer Fall für ein Reasoning-Modell: Es muss erkennen, ob die entsprechende Semantik in der neuen Struktur noch vorhanden ist, und einen konkret umsetzbaren Reparaturplan liefern – nicht bloß feststellen, dass „die Struktur angepasst wurde“. Die einzelnen Schritte stellen unterschiedliche Anforderungen; ein einziges Modellniveau für alles kostet entweder unnötig Geld oder Präzision:
Schritt | Erforderliche Fähigkeit | Empfehlung | model id |
|---|---|---|---|
Eine komplette Seite mit SSR-HTML lesen und alte mit neuer Struktur abgleichen | Langer Kontext, verarbeitet eine Detailseite mit einigen zehn bis einigen hundert KB auf einmal | Kimi K3 |
|
Nach einem Redesign den Old-New-Diff auswerten, Ankerdrift beurteilen und den minimalen Fix vorschlagen | Starkes Reasoning, erklärt anhand der Struktur statt nur das Phänomen zu wiederholen | Claude Opus 5 |
|
Cards von hunderten Werbetreibenden in großer Menge normalisieren und mit Intelligence-Tags versehen | Günstig, hunderte bis tausende Aufrufe mit hoher Parallelität | Claude Sonnet 5 |
|
Abweichungen zuordnen, wenn der Fixture-Vergleich fehlschlägt | Mittleres Reasoning, erklärt anhand von „erwartetem Feld versus tatsächlicher Extraktion“ | GPT-5.6 Sol |
|
Die zweite Stufe ist der Kern – und der einzige Schritt, bei dem ein Modellwechsel sichtbar bessere Ergebnisse liefert. Ob das Reasoning-Niveau den Aufwand wert ist, musst du nicht glauben, sondern kannst es schnell testen:
Speichere das HTML einer Seite vor einem Redesign und danach erneut ab. Öffne die Ad Library dazu mit deinem eigenen Account und sichere die Seite.
Gib beide HTML-Versionen zusammen mit der aktuellen Feldliste des Parsers an
claude-opus-5undgpt-5.6-sol.Prüfe genau eine Sache: Nennt der Vorschlag eine konkrete Änderung des semantischen Ankers – etwa „bisher wurde am Label 'total impressions' verankert; in der neuen Version hat sich die Container-Rolle dieses Labels geändert, verwende stattdessen X“ – oder bleibt er bei „die Struktur wurde angepasst, erneute Anpassung empfohlen“?
Den ersten Vorschlag kannst du direkt umsetzen, der zweite sagt praktisch nichts aus. Genau dieser Unterschied ist dein Auswahlkriterium. Er entscheidet unmittelbar, wie viele Runden blinden Ausprobierens du am Tag eines Redesigns drehen musst.
Schon ein Durchlauf zeigt den Unterschied direkter als jeder Benchmark.
Die eigentliche Hürde: Wechselkosten zwischen Modellen
Die vier Stufen stammen von drei Anbietern, mit drei SDKs, drei Authentifizierungsschemata und drei Fehlerformaten. Drei Clients für unterschiedliche Arbeitsschritte anzubinden, rechnen die meisten für „gelegentliche Parser-Reparaturen plus Datenbereinigung im Bulk“ durch und entscheiden sich dagegen. Am Ende läuft alles über ein einziges Modell – auch der Redesign-Fix landet dann bei einer Stufe, die nur „erneute Anpassung empfohlen“ sagt. Das kostet Zeit, ohne dass klar wird, warum.
AIReiter vereinheitlicht diese Ebene: ein Key, ein OpenAI-kompatibles Interface, alle vier Stufen dahinter. Zum Wechseln genügt es, das Feld model im Request-Body anzupassen.
# Redesign-Fix: die Reasoning-Stufe
curl https://aireiter.com/api/v1/chat/completions \
-H "Authorization: Bearer $AIREITER_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-5",
"messages": [{"role": "user", "content": "<old-new HTML diff + current field list, ask where the anchor drifted>"}]
}'
# Intelligence im Bulk normalisieren: nur das Modellfeld ändern, der Rest bleibt gleich
# "model": "claude-sonnet-5"
# Langer Kontext für die komplette Seite: "model": "kimi-k3"
# Abweichungen zuordnen: "model": "gpt-5.6-sol"
Wenn du bereits das OpenAI SDK nutzt, setzt du base_url auf https://aireiter.com/api/v1 und musst sonst nichts ändern. Mit dem Anthropic SDK verwendest du POST /api/v1/messages und denselben Key.
Preislich laufen Claude-Modelle mit 30 % Rabatt auf den Listenpreis, GPT-Modelle zum halben Preis, und Kimi K3 ist über denselben Key aufrufbar. Für diesen Workflow trifft der Rabatt die wesentlichen Kosten: Nicht der Redesign-Fix ist teuer – das ist ein seltener, aber wertvoller Aufruf. Der kostendichte Teil ist die Intelligence-Normalisierung im Bulk. Du beobachtest zwanzig Wettbewerber, jeweils mit Dutzenden bis Hunderten Cards, und gibst sie alle ans Modell, damit es Promise, Zielgruppe und Laufzeit ausliest. Dieser aufrufintensivste Schritt läuft auf Claude Sonnet mit 30 % Rabatt. Direkt danach folgt der lange Kontext zum Einlesen vollständiger Seiten und zum Strukturabgleich. Die beiden teuersten Teile profitieren somit unmittelbar vom Rabatt.
Ohne Anmeldung ausprobieren: Gib beiden Modellen manuell ein HTML-Paar vor und nach einem Redesign und vergleiche, welches wirklich benennen kann, wo der Anker abgedriftet ist. Danach entscheidest du, ob du es integrierst.
Nach dem Fix und der Normalisierung der Roh-Cards im Bulk folgt als nächster Schritt, diese Intelligence in die Creative-Pipeline für Entscheidungen und Asset-Generierung einzuspeisen. Das ist die Aufgabe von der vollständigen Pipeline vom Keyword bis zur fertigen Anzeige. Dieser Artikel behandelt ihren frühesten Abschnitt: öffentliche Daten zuverlässig einzusammeln.
Ob die Felder stimmen, entscheidet am Ende das Fixture
Der Fix-Vorschlag eines Modells bleibt nur ein Vorschlag, bis du ihn validiert hast. „Ändere den Anker auf X“ klingt plausibel, aber X muss nicht für jede Card-Variante funktionieren. B2B-Anzeigen kommen als Bild-Text-Anzeige, nur als Text, als Carousel, mit oder ohne Landingpage. Die zwei Beispiele, die das Modell gesehen hat, decken möglicherweise nicht alle Varianten ab.
Was das verhindert, ist derselbe Mechanismus, der Modellhalluzinationen beim Reverse Engineering bremst: ein Vergleich gegen feste Vektoren. Lege eine Sammlung bekannter Eingaben – einige gespeicherte HTML-Seiten aus der Praxis – zusammen mit ihren nachweislich korrekten Ausgaben ab, also Feldwerte, die du einmal manuell geprüft hast. Diese Fixture kommt ins Repository. Bei jeder Parser-Änderung, egal ob selbst geschrieben oder nach Modellvorschlag, führst du den Fixture-Satz erneut aus und vergleichst Feld für Feld. Nur so lässt sich nach einem Upstream-Redesign schnell unterscheiden: „Habe ich den Vorschlag falsch umgesetzt oder hat sich die Seite erneut verändert?“ Wenn alles besteht, ist die Änderung korrekt. Scheitern nur einzelne Fälle, zeigen deren Felder unmittelbar, auf welcher Ebene das Problem liegt. Das Modell erzeugt Reparaturvorschläge, das Fixture entscheidet über deren Richtigkeit – diese Rollen dürfen nicht vermischt werden. Die vollständige Methode dieses differenziellen Vergleichs beschreibt der Beitrag zum Differential Testing; der Ad-Library-Parser nutzt dieselbe Schranke. Ohne diese Ebene setzt du Modell-Sicherheit mit Korrektheit gleich. Dann passiert leicht: „Vorschlag umgesetzt, kein Fehler sichtbar, ausgerollt – und drei Tage später fällt auf, dass die Daten eines Landes die ganze Zeit leer waren.“
Fazit
B2B-Ad-Intelligence muss oft aus HTML extrahiert werden, weil die Anzeigenbibliothek ein Compliance-Artefakt und keine Produkt-API ist: ohne API-Vertrag und mit Redesigns jederzeit. Widerstandsfähig wird der Parser durch drei Prinzipien: an Semantik statt Position ankern, bei fehlenden Feldern degradiert weiterarbeiten und Ergebnisse mit Vollständigkeit markieren, damit „leer“ und „defekt“ unterscheidbar bleiben. Der echte Wert liegt nicht in den Feldern einer einzelnen Anzeige, sondern in den Dimensionen Land, Laufzeitfenster, Impression-Spanne und Targeting-Facetten, über die sich der Launch eines Wettbewerbers aufschlüsseln lässt. Das Modell hat dabei eine klar begrenzte Rolle: nicht beim Parsing mit seiner deterministischen State Machine, sondern in der Wartung. Beim Vergleich alter und neuer HTML-Versionen nach einem Redesign helfen Reasoning-Modelle bei Reparaturvorschlägen; für die Normalisierung vieler Cards zu Intelligence ist die günstige, hochparallel ausführbare Stufe zuständig. Ob die extrahierten Felder korrekt sind, entscheidet jedoch immer das Fixture. Wenn alle Stufen über ein einheitliches Interface angebunden sind, bleibt als Reibung nur noch das Ändern eines model-Felds – gelöst durch die Wahl deines Modells.