34 KiB
title, description, published, date, tags, editor, dateCreated
| title | description | published | date | tags | editor | dateCreated |
|---|---|---|---|---|---|---|
| FEEDGINE | true | 2026-03-26T13:12:31.315Z | markdown | 2026-03-26T13:12:31.315Z |
POAS Feed Enricher — Technische Dokumentation
Projekt: POAS Feed Enricher Sprache: Python 3.11 Stand: März 2026
Inhaltsverzeichnis
- Systemübersicht
- Berechnungsformeln
- Programmfunktionen
- Installation ohne Docker (Direktinstallation)
- Installation mit Docker
- Konfiguration
- Betrieb und Überwachung
- Systemarchitektur (Diagramme)
1. Systemübersicht
Der POAS Feed Enricher ist eine automatisierte Datenpipeline, die täglich einen Google-Shopping-Produktfeed mit profitabilitätsbasierten Custom Labels anreichert und an Google Merchant Center (GMC) ausliefert.
Das Programm verbindet drei Datenquellen:
| Datenquelle | Inhalt |
|---|---|
| Shop-XML-Feed (ZIP-Download) | Produktdaten: ID, Titel, Preis, Bild-URL, Kategorie usw. |
| Google Ads API | Werbeleistung pro Produkt: Klicks, Conversions, Kosten, Umsatz |
| COGS-CSV (Einkaufspreise) | Netto-Einkaufspreis je Artikel-ID |
Das Ergebnis ist ein angereicherter XML-Feed, in dem jedes Produkt zwei zusätzliche Felder erhält:
g:custom_label_0— Hauptgruppe (Longtail,Core,Feeder)g:custom_label_4— Untergruppe (z. B.stars,potentials,underperformer,invisibles,nd_low_valueusw.)
Diese Labels werden in Google Ads als Segmentierungskriterien für Gebotsstrategien verwendet.
Ablauf auf einen Blick
flowchart TD
A["Shop-XML-Feed (ZIP)"] --> D["Pipeline"]
B["Google Ads API"] --> D
C["COGS-CSV (Einkaufspreise)"] --> D
D --> E["Angereicherter XML-Feed"]
E --> F["Google Merchant Center"]
E --> G["nginx Feed-Server"]
D --> H["Telegram-Benachrichtigung"]
2. Berechnungsformeln
2.1 POAS — Profit on Ad Spend
POAS (Profit on Ad Spend) ist die zentrale Kennzahl des Systems. Sie misst, wie profitabel der Werbeeinsatz für ein einzelnes Produkt ist.
Formel
\text{Marge} = \text{Conversion-Umsatz} - (\text{Einkaufspreis} \times \text{Anzahl Conversions})
\text{POAS} = \frac{\text{Marge}}{\text{Werbekosten}}
Schritt-für-Schritt-Erklärung
Schritt 1 — Marge berechnen:
Marge = conversions_value − (cogs_eur × conversions)
Dieses Beispiel dient ausschließlich zur Veranschaulichung der Formel.
| Variable | Bedeutung | Quelle |
|---|---|---|
conversions_value |
Gesamtumsatz aller Conversions für dieses Produkt (in EUR) | Google Ads API |
cogs_eur |
Netto-Einkaufspreis pro Einheit (in EUR) | COGS-CSV |
conversions |
Anzahl der erzielten Käufe im Betrachtungszeitraum | Google Ads API |
Schritt 2 — POAS berechnen:
POAS = Marge / cost_eur
Dieses Beispiel dient ausschließlich zur Veranschaulichung der Formel.
| Variable | Bedeutung | Quelle |
|---|---|---|
cost_eur |
Gesamte Werbeausgaben für dieses Produkt (in EUR) | Google Ads API (cost_micros ÷ 1.000.000) |
Rechenbeispiel — Verlustbringendes Produkt
| Größe | Wert |
|---|---|
| Werbekosten (30 Tage) | 10,00 € |
| Conversion-Umsatz | 120,00 € |
| Einkaufspreis pro Einheit | 50,00 € |
| Anzahl Conversions | 3 |
Marge = 120,00 € − (50,00 € × 3) = 120,00 € − 150,00 € = −30,00 €
POAS = −30,00 € / 10,00 € = −3,0
Beispielrechnung zur Veranschaulichung — keine realen Daten.
In diesem Fall ist das Produkt verlustbringend (negativer POAS). Es wird der Gruppe Feeder zugeordnet.
Rechenbeispiel — Profitables Produkt
Werbekosten: 10,00 €
Umsatz: 120,00 €
EK-Preis: 30,00 €
Conversions: 2
Marge = 120,00 € − (30,00 € × 2) = 60,00 €
POAS = 60,00 € / 10,00 € = 6,0 → Gruppe: Core
Beispielrechnung zur Veranschaulichung — keine realen Daten.
2.2 Produktgruppen-Zuordnung
Die Zuordnung zu Gruppen und Untergruppen erfolgt nach folgender Logik:
flowchart TD
START(["Produkt"]) --> A{"Klicks = 0?"}
A -- Ja --> LT["Longtail"]
LT --> LT1{"Preis?"}
LT1 -- "unter 50 €" --> LT_L["nd_low_value"]
LT1 -- "50 € bis 200 €" --> LT_M["nd_mid_value"]
LT1 -- "über 200 €" --> LT_H["nd_high_value"]
A -- Nein --> B{"POAS >= 1,0?"}
B -- Ja --> CORE["Core"]
CORE --> C{"Klicks > 30 UND Conversions >= 2?"}
C -- Ja --> CORE_S["stars"]
C -- Nein --> CORE_P["potentials"]
B -- Nein --> FEEDER["Feeder"]
FEEDER --> D{"Klicks > 3?"}
D -- Ja --> F_U["underperformer"]
D -- Nein --> F_I["invisibles"]
Gruppe: Longtail
Bedingung: Das Produkt hatte im Betrachtungszeitraum keine Klicks (d. h. es läuft keine oder nur sehr geringe Werbung).
| Untergruppe | Bedingung | Bedeutung |
|---|---|---|
nd_low_value |
Preis < 50 € | Günstige Produkte ohne Werbehistorie |
nd_mid_value |
50 € ≤ Preis ≤ 200 € | Mittlere Preisklasse ohne Werbehistorie |
nd_high_value |
Preis > 200 € | Hochpreisige Produkte ohne Werbehistorie |
Strategie: Geringe Gebote, organische Sichtbarkeit nutzen.
Gruppe: Core
Bedingung: Das Produkt hat Klicks und einen POAS ≥ 1,0 (profitabel).
| Untergruppe | Bedingung | Bedeutung |
|---|---|---|
stars |
Klicks > 30 und Conversions ≥ 2 | Bewiesene Top-Performer mit hohem Volumen |
potentials |
Alle anderen Core-Produkte | Profitabel, aber noch geringes Volumen |
Strategie: Gebote erhöhen, Budget aufstocken.
Gruppe: Feeder
Bedingung: Das Produkt hat Klicks, aber POAS < 1,0 (defizitär).
| Untergruppe | Bedingung | Bedeutung |
|---|---|---|
underperformer |
Klicks > 3 | Generiert Klicks, aber keine rentablen Conversions |
invisibles |
Klicks ≤ 3 | Kaum sichtbar, schwache Performance |
Strategie: Gebote senken, Anzeigen und Landingpages optimieren.
2.3 Schwellenwerte (Konfigurierbar)
| Parameter | Standardwert | Beschreibung |
|---|---|---|
POAS_CORE_THRESHOLD |
1.0 |
POAS-Grenzwert für Profitabilität |
LONGTAIL_LOW_MAX |
50.0 |
Preisgrenze Longtail „low" |
LONGTAIL_HIGH_MIN |
200.0 |
Preisgrenze Longtail „high" |
CORE_STARS_MIN_CLICKS |
30 |
Mindestklicks für „stars" |
CORE_STARS_MIN_CONVERSIONS |
2 |
Mindest-Conversions für „stars" |
FEEDER_UNDERPERFORMER_CLICKS |
3 |
Klick-Grenze für „underperformer" |
GOOGLE_ADS_LOOKBACK_DAYS |
90 |
Betrachtungszeitraum in Tagen |
3. Programmfunktionen
3.1 Scheduler (Startup/scheduler.py)
Der Scheduler ist der Haupteinstiegspunkt. Er läuft dauerhaft und startet die Pipeline täglich zur konfigurierten Uhrzeit.
Funktionen:
- Berechnung des nächsten Ausführungszeitpunkts aus
SCHEDULE_HOURundSCHEDULE_MINUTE - Wartemodus in 60-Sekunden-Intervallen (CPU-schonend)
- Unterstützt
--run-now(sofortige Ausführung),--once(einmalig, dann Beenden),--lookback-days N - Sendet Telegram-Benachrichtigung beim Start des Schedulers
3.2 Watchdog / Pipeline-Ausführer (Startup/watchdog.py)
Der Watchdog orchestriert den gesamten Ausführungsablauf und implementiert Fehlerbehandlung.
Funktionen:
- Erstellt datierte Ein- und Ausgabeordner (
Input DD.MM.YYYY,Output DD.MM.YYYY) - Lädt ZIP-Feeds herunter und entpackt die XML-Dateien
- Startet die Pipeline als Subprozess (mit Timeout)
- Retry-Logik: bis zu
MAX_RETRIESVersuche mitRETRY_DELAY_SECONDSPause - Integritätsprüfungen vor der Veröffentlichung (siehe 3.6)
- Telegram-Benachrichtigungen bei Erfolg und Fehler (inkl. Log-Anhang)
3.3 Google Ads Datenabruf (poas_feed_enricher/app.py)
Ruft Werbeleistungsdaten aller Produkte über die Google Ads API ab.
Funktionen:
- OAuth2-Authentifizierung via
google-ads.yaml - Automatische Erkennung und Iteration über alle Unterkonten eines MCC (Multi-Client-Account)
- GAQL-Abfrage auf
shopping_performance_viewfür den konfigurierten Zeitraum - Aggregation: Klicks, Impressionen, Kosten, Conversions, Umsatz je Produkt-ID
- Speicherung als
feed_performance.csv - Exponentielles Backoff bei transienten API-Fehlern (
UNAVAILABLE,DEADLINE_EXCEEDED,RESOURCE_EXHAUSTED)
GAQL-Abfrage (vereinfacht):
SELECT
segments.product_item_id,
metrics.conversions_value,
metrics.conversions,
metrics.cost_micros,
metrics.impressions,
metrics.clicks
FROM shopping_performance_view
WHERE segments.date DURING LAST_90_DAYS
Dies ist kein direkt ausführbarer Code, sondern dient nur zur Veranschaulichung der Abfragestruktur.
3.4 Feed-Transformation (poas_feed_enricher/2_data_transform.py)
Reichert den Shop-XML-Feed mit POAS-Labels an.
Funktionen:
- Streaming-XML-Verarbeitung mit
lxml.iterparse(speichereffizient für Feeds > 200 MB) - Laden der Einkaufspreise (COGS) aus CSV-Datei
- Laden der Ads-Performance-Daten aus
feed_performance.csv - Produktfilter: Entfernt Produkte ohne gültiges Bild und mit Preis ≤ 0
- POAS-Berechnung und Label-Zuordnung je Produkt
- Passthrough aller Originalfelder + Einfügen von
g:custom_label_0undg:custom_label_4 - Generierung eines Audit-Reports (Anzahl Produkte, Trefferquoten, Label-Verteilung)
3.5 Label Engine (poas_feed_enricher/processors/label_engine.py)
Reine Berechnungsfunktion (keine Seiteneffekte) zur POAS-Labelberechnung.
Eingaben: Preis, Einkaufspreis, Ads-Performance-Daten
Ausgabe: LabelResult mit custom_label_0 und custom_label_4
3.6 Integritätsprüfungen (Startup/watchdog.py)
Vor der Veröffentlichung des Feeds werden drei Sicherheitsprüfungen durchgeführt:
| Prüfung | Bedingung | Fehlerfall |
|---|---|---|
| Zero-Check | Ausgabe-Feed enthält > 0 Produkte | Feed ist leer |
| Integritäts-Check | Keine Produkte mit fehlender g:id |
Strukturfehler im XML |
| Sanity-Check | Produktanzahl weicht < ±1000 vom Vorrun ab | Anomaler Produktverlust |
Schlägt eine Prüfung fehl, wird der Feed nicht veröffentlicht und eine Telegram-Warnung gesendet.
3.7 Feed-Veröffentlichung (Startup/feed_publisher.py)
Kopiert den fertigen Feed in das nginx-Ausgabeverzeichnis.
Funktionen:
- Kopiert den erzeugten XML-Feed nach
feed-serve/poas_feed.xml - Erstellt ein ZIP-Archiv als Download-Variante
- Aktualisiert
.last_feed_countfür den Sanity-Check des nächsten Runs
3.8 Ordner-Rotation (Startup/cleanup.py)
Verwaltet Speicherplatz durch automatische Rotation der datierten Ordner.
Funktionen:
- Listet alle
Input DD.MM.YYYY- undOutput DD.MM.YYYY-Ordner auf - Behält die neuesten
KEEP_LAST_N(Standard: 3) Ordner - Löscht ältere Ordner automatisch
3.9 Telegram-Benachrichtigungen (Startup/telegram_notifier.py)
Sendet Status-Nachrichten an konfigurierte Telegram-Chats.
Nachrichten-Typen:
| Ereignis | Inhalt |
|---|---|
| Scheduler-Start | Programmstart, geplante Uhrzeit, Lookback-Tage |
| Pipeline gestartet | Ausführungsbeginn |
| Erfolg | Feed-Statistiken, Label-Verteilung, Feed-URL |
| Fehler | Fehlermeldung + vollständige Log-Datei als Anhang |
| Feed gesperrt | Grund der Sicherheitsprüfung (Zero/Integrität/Sanity) |
Beispielformat einer Erfolgsmeldung:
POAS Pipeline — SUCCESS
Datum: TT.MM.JJJJ HH:MM
Ausgabe: Output TT.MM.JJJJ
--- Feed-Zusammenfassung ---
Quell-XML: XXX.XXX
Endgültiger Feed: XXX.XXX
Ads-Treffer: XXX.XXX
--- Label-Verteilung ---
Longtail: XX.XXX (XX,X %)
nd_low_value: XX.XXX (XX,X %)
nd_mid_value: XX.XXX (XX,X %)
nd_high_value: XX.XXX (XX,X %)
Core: XX.XXX (XX,X %)
stars: XX.XXX (XX,X %)
potentials: XX.XXX (XX,X %)
Feeder: XX.XXX (XX,X %)
underperformer: XX.XXX (XX,X %)
invisibles: XX.XXX (XX,X %)
Feed veröffentlicht: OK
GMC Feed: https://[FEED-SERVER]/[CLIENT_PREFIX]/poas_feed.xml
Dies ist ein Beispielformat — Zahlen und URLs sind Platzhalter.
4. Installation ohne Docker (Direktinstallation)
Voraussetzungen
- Ubuntu 24.04 LTS (oder kompatible Linux-Distribution)
- Python 3.11
- nginx
- Git
4.1 System-Abhängigkeiten installieren
sudo apt-get update
sudo apt-get install -y \
python3.11 python3.11-venv python3-pip \
gcc libxml2-dev libxslt1-dev \
nginx git unzip curl
Beispielbefehl für Ubuntu 24.04 — Paketnamen können je nach Distribution abweichen.
4.2 Projektdateien einrichten
# Ins gewünschte Verzeichnis wechseln
cd /opt
# Projektarchiv entpacken
tar -xzf poas-feed-enricher.tar.gz
mv poas-feed-enricher poas-pipeline
cd poas-pipeline
# Python Virtual Environment erstellen
python3.11 -m venv .venv
source .venv/bin/activate
# Python-Abhängigkeiten installieren
pip install --upgrade pip
pip install -r requirements.txt
Beispielbefehle zur Veranschaulichung — Pfade und Dateinamen sind an die eigene Umgebung anzupassen.
4.3 Umgebungskonfiguration
# .env-Datei erstellen und befüllen
cp .env.example .env
nano .env
Beispielbefehl — der tatsächliche Editor und Dateiname können abweichen.
Die folgenden Variablen müssen gesetzt werden (Details siehe Abschnitt 6):
TELEGRAM_BOT_TOKEN=...
TELEGRAM_CHAT_IDS=...
SHOP_NAME=...
SHOP_URL=...
FEED_ZIPS=...
GOOGLE_ADS_DEVELOPER_TOKEN=...
GOOGLE_ADS_CLIENT_ID=...
GOOGLE_ADS_CLIENT_SECRET=...
GOOGLE_ADS_REFRESH_TOKEN=...
GOOGLE_ADS_LOGIN_CUSTOMER_ID=...
Platzhalter — alle Werte sind durch eigene Zugangsdaten zu ersetzen.
4.4 Eingabedateien bereitstellen
# Einkaufspreise (COGS) — Pflichtdatei
cp /pfad/zu/einkaufspreise.csv Datei/einkaufspreise.csv
# Google Produktkategorie-Mapping (optional)
cp /pfad/zu/kategorien.csv Datei/google_product_category_mapping.csv
Pfade sind an die eigene Umgebung anzupassen.
Format der COGS-Datei (semikolon-getrennt, ohne Kopfzeile):
ARTIKEL-001;12.50
ARTIKEL-002;0.00
ARTIKEL-003;89.99
Beispieldaten zur Veranschaulichung des Formats — keine realen Artikel.
Format der Kategorie-Datei (komma-getrennt, mit Kopfzeile):
id,google_product_category
ARTIKEL-001,8227
ARTIKEL-002,3025
Beispieldaten zur Veranschaulichung des Formats — keine realen Artikel.
4.5 Google Ads API konfigurieren
Die Datei poas_feed_enricher/google-ads.yaml wird beim Docker-Start automatisch generiert. Bei Direktinstallation muss sie manuell erstellt werden:
developer_token: "[IHR_DEVELOPER_TOKEN]"
client_id: "[IHR_CLIENT_ID]"
client_secret: "[IHR_CLIENT_SECRET]"
refresh_token: "[IHR_REFRESH_TOKEN]"
login_customer_id: "[IHR_MCC_ID]"
use_proto_plus: true
Alle Felder in eckigen Klammern sind Platzhalter und müssen durch echte Google Ads API-Zugangsdaten ersetzt werden.
4.6 nginx konfigurieren
# nginx-Konfiguration einspielen
sudo cp nginx.client.conf /etc/nginx/sites-available/poas-feed
sudo ln -s /etc/nginx/sites-available/poas-feed /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Beispielbefehle für Ubuntu mit nginx — an die eigene Serverumgebung anpassen.
Ausgabeverzeichnis für den Feed erstellen:
mkdir -p feed-serve
sudo chown -R $USER:www-data feed-serve
sudo chmod -R 755 feed-serve
Beispielbefehle — Benutzer und Berechtigungen sind je nach Serversetup anzupassen.
4.7 Als Systemd-Service einrichten
Servicedatei unter /etc/systemd/system/poas-pipeline.service anlegen:
[Unit]
Description=POAS Feed Enricher Pipeline
After=network.target
[Service]
Type=simple
User=[SYSTEMBENUTZER]
WorkingDirectory=/opt/poas-pipeline
ExecStart=/opt/poas-pipeline/.venv/bin/python Startup/scheduler.py
Restart=on-failure
RestartSec=30
EnvironmentFile=/opt/poas-pipeline/.env
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Vorlage für eine systemd-Servicedatei — Pfade und Benutzername sind anzupassen.
# Service aktivieren und starten
sudo systemctl daemon-reload
sudo systemctl enable poas-pipeline
sudo systemctl start poas-pipeline
# Status prüfen
sudo systemctl status poas-pipeline
# Logs verfolgen
sudo journalctl -fu poas-pipeline
Beispielbefehle zur Veranschaulichung — Servicename ist ggf. anzupassen.
4.8 Manuelle Testausführung
# Virtual Environment aktivieren
source .venv/bin/activate
# Einmalige sofortige Ausführung (ohne auf geplante Zeit zu warten)
python Startup/scheduler.py --once
# Sofort ausführen und danach weiter nach Zeitplan laufen
python Startup/scheduler.py --run-now
# Mit abweichendem Lookback-Zeitraum
python Startup/scheduler.py --once --lookback-days 60
Beispielbefehle zur Veranschaulichung der verfügbaren Startoptionen.
5. Installation mit Docker
Voraussetzungen
- Docker Engine ≥ 24.0
- Docker Compose Plugin ≥ 2.20
- Mindestens 4 GB freier Speicher (Feeds können mehrere hundert MB groß sein)
5.1 Docker und Docker Compose installieren
# Docker installieren (Ubuntu)
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
# Version prüfen
docker --version
docker compose version
Installationsbefehle für Ubuntu — für andere Systeme die offizielle Docker-Dokumentation verwenden.
5.2 Projektdateien einrichten
cd /opt
tar -xzf poas-feed-enricher.tar.gz
mv poas-feed-enricher poas-pipeline
cd poas-pipeline
Beispielbefehle — Dateiname und Zielverzeichnis sind anzupassen.
5.3 Docker Compose konfigurieren
# Vorlage kopieren
cp docker-compose.sample.yaml docker-compose.yaml
# Konfiguration bearbeiten
nano docker-compose.yaml
Beispielbefehle — alle Platzhalter in der kopierten Datei anschließend mit echten Werten befüllen.
Alle Platzhalter [WERT] müssen durch eigene Werte ersetzt werden. Vollständige Konfigurationsstruktur:
services:
pipeline:
build: .
container_name: poas-pipeline
restart: unless-stopped
environment:
# Telegram
TELEGRAM_BOT_TOKEN: "[BOT_TOKEN]"
TELEGRAM_CHAT_IDS: "[CHAT_ID_1],[CHAT_ID_2]"
# Shop-Informationen
SHOP_NAME: "[SHOPNAME]"
SHOP_URL: "https://[SHOP-DOMAIN]"
SHOP_DESCRIPTION: "[KURZBESCHREIBUNG]"
# Feed-ZIP-URLs (Format: URL|ZIP-Name|XML-Name, mehrere durch ; getrennt)
FEED_ZIPS: "https://[SHOP-DOMAIN]/export/feed1.zip|feed1.zip|feed1.xml"
# Google Ads API
GOOGLE_ADS_DEVELOPER_TOKEN: "[DEVELOPER_TOKEN]"
GOOGLE_ADS_CLIENT_ID: "[CLIENT_ID]"
GOOGLE_ADS_CLIENT_SECRET: "[CLIENT_SECRET]"
GOOGLE_ADS_REFRESH_TOKEN: "[REFRESH_TOKEN]"
GOOGLE_ADS_LOGIN_CUSTOMER_ID: "[MCC_KONTO_ID]"
# Zeitplan (UTC)
SCHEDULE_HOUR: "0"
SCHEDULE_MINUTE: "0"
# Lookback-Zeitraum
LOOKBACK_DAYS: "90"
# Fehlerbehandlung
MAX_RETRIES: "3"
RETRY_DELAY_SECONDS: "60"
KEEP_LAST_N: "3"
# Feed-Veröffentlichung
FEED_PUBLISH_ENABLED: "true"
CLIENT_PREFIX: "[SHOP-KÜRZEL]"
volumes:
- poas-logs:/app/Startup/logs
- feed-serve:/app/feed-serve
nginx:
image: nginx:alpine
container_name: poas-nginx
restart: unless-stopped
ports:
- "8525:80"
volumes:
- feed-serve:/usr/share/nginx/feeds:ro
- ./nginx.client.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- pipeline
volumes:
poas-logs:
feed-serve:
Vorlage für docker-compose.yaml — alle Werte in eckigen Klammern sind Platzhalter und müssen ersetzt werden.
5.4 Eingabedateien bereitstellen
Die Eingabedateien müssen vor dem ersten Start in den Datei/-Ordner gelegt werden, da dieser in das Docker-Image gebacken wird:
# Einkaufspreise (Pflicht)
cp /pfad/zu/einkaufspreise.csv Datei/einkaufspreise.csv
# Kategorie-Mapping (optional)
cp /pfad/zu/kategorien.csv Datei/google_product_category_mapping.csv
Pfade sind an die eigene Umgebung anzupassen.
Hinweis: Nach Änderungen an Dateien im
Datei/-Ordner muss das Docker-Image neu gebaut werden (docker compose build).
5.5 Container starten
# Image bauen und Container starten
docker compose up -d
# Logs verfolgen
docker compose logs -f pipeline
# Status prüfen
docker compose ps
Standardbefehle für Docker Compose v2.
5.6 Manuelle Ausführung im Container
# Sofortige einmalige Ausführung
docker compose exec pipeline python Startup/scheduler.py --once
# Sofort ausführen (Scheduler läuft weiter)
docker compose exec pipeline python Startup/scheduler.py --run-now
# Mit angepasstem Lookback-Zeitraum
docker compose exec pipeline python Startup/scheduler.py --once --lookback-days 60
Beispielbefehle zur Veranschaulichung der manuellen Ausführungsoptionen.
5.7 Container-Verwaltung
# Container stoppen
docker compose stop
# Container stoppen und entfernen
docker compose down
# Container stoppen, Volumes löschen
# Achtung: Logs und Feed-Daten gehen dabei verloren!
docker compose down -v
# Image neu bauen (nach Code- oder Dateiänderungen)
docker compose build --no-cache
docker compose up -d
Beispielbefehle für gängige Container-Verwaltungsaufgaben.
5.8 Architektur mit Docker
graph TB
subgraph Host["Server"]
subgraph DockerNetwork["Docker-Netzwerk"]
PIPELINE["poas-pipeline\nPython 3.11-slim"]
NGINX["poas-nginx\nnginx:alpine, Port 8525"]
end
subgraph Volumes["Docker Volumes"]
V1[("poas-logs")]
V2[("feed-serve")]
end
end
INTERNET["Internet\nGoogle Ads API / Telegram / Shop-ZIP"]
GMC["Google Merchant Center"]
TELEGRAM["Telegram"]
PIPELINE --> V1
PIPELINE --> V2
NGINX --> V2
PIPELINE --> INTERNET
NGINX -->|"Port 8525"| GMC
PIPELINE --> TELEGRAM
6. Konfiguration
Alle Konfigurationsparameter werden über die .env-Datei (Direktinstallation) oder die environment-Sektion in docker-compose.yaml gesetzt.
6.1 Pflichtparameter
| Variable | Beispielformat | Beschreibung |
|---|---|---|
TELEGRAM_BOT_TOKEN |
[TOKEN]:AAG... |
Token des Telegram-Bots |
TELEGRAM_CHAT_IDS |
[ID1],[ID2] |
Chat-IDs (kommagetrennt) |
SHOP_NAME |
[Shopname] |
Name des Shops (für Feed-Titel) |
SHOP_URL |
https://[shop-domain] |
Shop-URL (für Feed-Link) |
SHOP_DESCRIPTION |
[Beschreibung] |
Shop-Beschreibung |
FEED_ZIPS |
(siehe Format unten) | ZIP-Feed-URLs mit Dateinamen |
GOOGLE_ADS_DEVELOPER_TOKEN |
[TOKEN] |
Google Ads Developer Token |
GOOGLE_ADS_CLIENT_ID |
[ID].apps.googleusercontent.com |
OAuth2 Client-ID |
GOOGLE_ADS_CLIENT_SECRET |
GOCSPX-[...] |
OAuth2 Client-Secret |
GOOGLE_ADS_REFRESH_TOKEN |
1//04[...] |
OAuth2 Refresh-Token (langlebig) |
GOOGLE_ADS_LOGIN_CUSTOMER_ID |
[10-stellige ID] |
Google Ads MCC-Konto-ID |
Format von FEED_ZIPS:
Mehrere ZIP-Feeds werden durch ; getrennt. Jeder Feed besteht aus drei durch | getrennten Teilen:
URL|ZIP-Dateiname|XML-Dateiname
Formatbeschreibung — keine funktionsfähige Konfiguration.
Beispiel mit zwei Feeds:
FEED_ZIPS=https://[SHOP]/export/feed1.zip|feed1.zip|feed1.xml;https://[SHOP]/export/feed2.zip|feed2.zip|feed2.xml
Beispielformat — Platzhalter durch eigene URLs und Dateinamen ersetzen.
6.2 Optionale Parameter
| Variable | Standard | Beschreibung |
|---|---|---|
SCHEDULE_HOUR |
0 |
Stunde der täglichen Ausführung (UTC) |
SCHEDULE_MINUTE |
0 |
Minute der täglichen Ausführung (UTC) |
LOOKBACK_DAYS |
90 |
Google Ads Betrachtungszeitraum (Tage) |
MAX_RETRIES |
3 |
Maximale Wiederholungsversuche bei Fehler |
RETRY_DELAY_SECONDS |
60 |
Wartezeit zwischen Versuchen (Sekunden) |
KEEP_LAST_N |
3 |
Anzahl zu behaltender datierter Ordner |
CLIENT_PREFIX |
[shop-kuerzel] |
Präfix für Feed-URL-Pfad auf nginx |
FEED_PUBLISH_ENABLED |
false |
Feed nach nginx veröffentlichen |
FEED_SERVER_PORT |
8080 |
Lokaler Feed-Server-Port |
FILTER_OUT_OF_STOCK |
false |
Nicht vorrätige Produkte ausfiltern |
7. Betrieb und Überwachung
7.1 Täglicher Normalbetrieb
Im Normalbetrieb ist kein manueller Eingriff erforderlich. Der Scheduler startet täglich zur konfigurierten Uhrzeit automatisch:
sequenceDiagram
participant S as Scheduler
participant W as Watchdog
participant API as Google Ads API
participant ZIP as Shop-ZIP-Server
participant N as nginx
participant TG as Telegram
S->>TG: Scheduler gestartet
Note over S: Wartet bis SCHEDULE_HOUR:SCHEDULE_MINUTE
loop Täglich
S->>W: execute_pipeline()
W->>ZIP: ZIP-Feeds herunterladen
ZIP-->>W: XML-Dateien
W->>API: Google Ads Daten abrufen
API-->>W: feed_performance.csv
W->>W: XML anreichern (POAS-Labels)
W->>W: Integritätsprüfungen
W->>N: Feed veröffentlichen
W->>W: Alte Ordner löschen
W->>TG: Erfolgsmeldung mit Statistiken
end
7.2 Logs einsehen
Docker:
# Live-Logs des Pipeline-Containers
docker compose logs -f pipeline
# Spezifische Log-Datei lesen
docker compose exec pipeline cat Startup/logs/pipeline_run_JJJJMMTT_HHMMSS.txt
# Alle Log-Dateien auflisten
docker compose exec pipeline ls -lht Startup/logs/
Beispielbefehle — Dateinamen mit echtem Datum und Uhrzeit ersetzen.
Direktinstallation:
# Systemd-Journal
sudo journalctl -fu poas-pipeline
# Einzelne Log-Datei
cat Startup/logs/pipeline_run_JJJJMMTT_HHMMSS.txt
Dateinamen mit echtem Datum und Uhrzeit ersetzen.
7.3 Feed-Status prüfen
# Aktuellen Feed ansehen
ls -lh feed-serve/
# Produktanzahl im Feed zählen
grep -c '<item>' feed-serve/poas_feed.xml
# Letzte bekannte Produktanzahl
cat feed-serve/.last_feed_count
Beispielbefehle für die Direktinstallation.
Docker:
docker compose exec nginx ls -lh /usr/share/nginx/feeds/
7.4 Sofortige manuelle Ausführung
# Docker
docker compose exec pipeline python Startup/scheduler.py --once
# Direktinstallation
source .venv/bin/activate
python Startup/scheduler.py --once
Beispielbefehle für beide Betriebsarten.
7.5 Feed-URL-Struktur
Nach erfolgreicher Ausführung und Veröffentlichung ist der Feed über nginx erreichbar:
| Endpunkt | Beschreibung |
|---|---|
http://[SERVER-IP]:[PORT]/poas_feed.xml |
Aktueller GMC-Feed (XML) |
http://[SERVER-IP]:[PORT]/download |
Feed als ZIP-Download |
Bei Verwendung eines zentralen Reverse-Proxies:
https://[FEED-SERVER]/[CLIENT_PREFIX]/poas_feed.xml
Platzhalter in eckigen Klammern durch die eigene Serveradresse und den konfigurierten Präfix ersetzen.
7.6 Häufige Fehler und Lösungen
| Fehler | Ursache | Lösung |
|---|---|---|
google-ads.yaml nicht gefunden |
Credentials-Datei fehlt | entrypoint.sh ausführen oder Datei manuell erstellen |
Invalid OAuth2 credentials |
Refresh-Token abgelaufen | Neuen Refresh-Token via OAuth-Flow generieren |
ZIP download failed |
Shop-Server nicht erreichbar | Netzwerk prüfen, URL in FEED_ZIPS prüfen |
| Feed leer (0 Produkte) | Alle Produkte ausgefiltert | Filter-Logik und Eingabe-XML prüfen |
| Sanity-Check fehlgeschlagen | Produktzahl ändert sich um > 1000 | .last_feed_count prüfen, ggf. zurücksetzen |
RESOURCE_EXHAUSTED (Google Ads) |
API-Kontingent erschöpft | LOOKBACK_DAYS reduzieren oder nächsten Tag abwarten |
8. Systemarchitektur (Diagramme)
8.1 Komponentenübersicht
graph TD
subgraph INPUT["Eingabedaten"]
ZIP["Shop-XML-Feeds (ZIP)"]
COGS["COGS-CSV (Einkaufspreise)"]
ADS["Google Ads API"]
end
subgraph PIPELINE["Pipeline (Python 3.11)"]
SCHED["scheduler.py — Zeitsteuerung"]
WATCH["watchdog.py — Orchestrator"]
APP["app.py — Ads-Datenabruf"]
TRANS["2_data_transform.py — XML-Anreicherung"]
LABEL["label_engine.py — POAS-Berechnung"]
PUB["feed_publisher.py — Veröffentlichung"]
CLEAN["cleanup.py — Speicherverwaltung"]
TG["telegram_notifier.py — Benachrichtigung"]
end
subgraph OUTPUT["Ausgabe"]
FEED["poas_feed.xml (angereicherter GMC-Feed)"]
NGINX["nginx — Feed-Server"]
TELEGRAM["Telegram — Benachrichtigung"]
end
ZIP --> WATCH
COGS --> TRANS
ADS --> APP
APP --> TRANS
SCHED --> WATCH
WATCH --> APP
WATCH --> TRANS
TRANS --> LABEL
LABEL --> TRANS
WATCH --> PUB
PUB --> FEED
FEED --> NGINX
WATCH --> CLEAN
WATCH --> TG
TG --> TELEGRAM
8.2 Dateistruktur
poas-pipeline/
├── .env ← Konfiguration & Credentials
├── Dockerfile ← Docker-Image-Definition
├── docker-compose.yaml ← Docker-Deployment
├── docker-compose.sample.yaml ← Vorlage für docker-compose.yaml
├── entrypoint.sh ← Docker-Startskript (generiert google-ads.yaml)
├── nginx.client.conf ← nginx-Konfiguration
├── requirements.txt ← Python-Abhängigkeiten
├── setup.sh ← Direktinstallations-Skript (Ubuntu)
│
├── poas_feed_enricher/ ← Kern-Pipeline
│ ├── config.py ← Schwellenwerte & Konstanten
│ ├── run.py ← Einstiegspunkt (Schritt 1 + 2)
│ ├── app.py ← Google Ads API-Abruf
│ ├── 2_data_transform.py ← XML-Anreicherung
│ ├── google-ads.yaml ← Google Ads Credentials (auto-generiert)
│ ├── loaders/
│ │ ├── ads_loader.py ← CSV-basierter Ads-Loader
│ │ ├── cogs_loader.py ← COGS-CSV-Loader
│ │ └── google_ads_api.py ← Live Google Ads API-Client
│ └── processors/
│ ├── label_engine.py ← POAS-Labelberechnung
│ └── gmc_validator.py ← GMC XML-Validierung
│
├── Startup/ ← Orchestrierungsschicht
│ ├── config.py ← Zentrale .env-Konfiguration
│ ├── scheduler.py ← Täglicher Zeitplaner
│ ├── watchdog.py ← Pipeline-Ausführer + Retry
│ ├── cleanup.py ← Ordner-Erstellung & -Rotation
│ ├── feed_publisher.py ← Feed nach nginx kopieren
│ ├── telegram_notifier.py ← Telegram-Benachrichtigungen
│ └── logs/ ← Ausführungs-Logs
│
├── Datei/ ← Statische Eingabedaten
│ ├── einkaufspreise.csv ← Einkaufspreise (COGS)
│ └── google_product_category_mapping.csv
│
└── feed-serve/ ← Ausgabe (nginx-Verzeichnis)
├── poas_feed.xml ← Aktueller GMC-Feed
├── poas_feed.zip ← Feed als ZIP-Download
└── .last_feed_count ← Sanity-Check-Baseline
Schematische Dateistruktur zur Orientierung — tatsächliche Dateinamen können abweichen.
8.3 Pipeline-Sequenzdiagramm (detailliert)
sequenceDiagram
participant SCH as Scheduler
participant WDG as Watchdog
participant CLN as Cleanup
participant APP as Ads-Abruf
participant TRN as XML-Anreicherung
participant PUB as Feed-Publisher
participant TG as Telegram
SCH->>TG: Scheduler gestartet
Note over SCH: Wartet bis SCHEDULE_HOUR:SCHEDULE_MINUTE (UTC)
SCH->>WDG: execute_pipeline(lookback_days)
WDG->>TG: Pipeline gestartet
WDG->>CLN: Datierten Input-Ordner erstellen
CLN->>CLN: ZIP-Feeds herunterladen
CLN->>CLN: XMLs entpacken und umbenennen
CLN-->>WDG: Liste der XML-Dateien
WDG->>APP: Google Ads Daten abrufen
APP->>APP: Credentials laden
APP->>APP: MCC-Unterkonten auflisten
loop Je Unterkonto
APP->>APP: GAQL-Abfrage ausführen
end
APP->>APP: feed_performance.csv schreiben
APP-->>WDG: Fertig
WDG->>TRN: XML anreichern
TRN->>TRN: COGS-CSV laden
TRN->>TRN: Performance-Daten laden
loop Je Produkt (Streaming)
TRN->>TRN: Preis und Bild-URL prüfen
TRN->>TRN: POAS berechnen
TRN->>TRN: Label zuweisen
TRN->>TRN: XML mit Labels schreiben
end
TRN-->>WDG: poas_feed.xml + Statistiken
WDG->>WDG: Integritätsprüfungen (Zero / Integrität / Sanity)
alt Alle Prüfungen bestanden
WDG->>PUB: Feed veröffentlichen
PUB->>PUB: XML nach feed-serve/ kopieren
PUB->>PUB: ZIP-Archiv erstellen
PUB-->>WDG: OK
WDG->>CLN: Alte Ordner rotieren
WDG->>TG: Erfolgsmeldung mit Statistiken
else Prüfung fehlgeschlagen
WDG->>TG: Warnung: Feed gesperrt (Grund)
end
8.4 POAS-Entscheidungsbaum (Labelzuordnung)
flowchart TD
P(["Produkt"]) --> F1{"Preis = 0 oder kein Bild?"}
F1 -- Ja --> OUT["Verworfen — nicht im Feed"]
F1 -- Nein --> F2{"Klicks = 0?"}
F2 -- Ja --> LT["Longtail — custom_label_0"]
LT --> F3{"Preis?"}
F3 -- "unter 50 EUR" --> LL["nd_low_value — custom_label_4"]
F3 -- "50 bis 200 EUR" --> LM["nd_mid_value — custom_label_4"]
F3 -- "über 200 EUR" --> LH["nd_high_value — custom_label_4"]
F2 -- Nein --> CALC["POAS berechnen"]
CALC --> F4{"POAS >= 1,0?"}
F4 -- Ja --> CORE["Core — custom_label_0"]
CORE --> F5{"Klicks > 30 UND Conversions >= 2?"}
F5 -- Ja --> CS["stars — custom_label_4"]
F5 -- Nein --> CP["potentials — custom_label_4"]
F4 -- Nein --> FEEDER["Feeder — custom_label_0"]
FEEDER --> F6{"Klicks > 3?"}
F6 -- Ja --> FU["underperformer — custom_label_4"]
F6 -- Nein --> FI["invisibles — custom_label_4"]
Dokumentation für POAS Feed Enricher — alle Namen, URLs, IDs und Zahlen in dieser Dokumentation sind Platzhalter und dienen ausschließlich der Veranschaulichung.