Zum Inhalt springen

Backlink-Daten als JSON: $100 Guthaben, keine Karte erforderlich, Anmeldung mit Google.

SEOMasters.dev
SEOMasters.devRegistrieren

Bulk-DR für eine Kundenliste als 40-Zeilen-Python-Skript

Sie haben eine Tabelle mit Domains und ein Dashboard, das eine Zahl pro Zeile benötigt. Ein Backlink-Domain-Checker für diesen Job ist im Wesentlichen Infrastruktur, und dieses Skript ist genau diese Infrastruktur.

Hände, die Holzblöcke auf einer Werkbank sortieren

Das Skript

Es liest domains.txt, sendet 200 Domains pro Anfrage, speichert jede Rohantwort in SQLite und gibt die Kosten des Laufs aus. Wir speichern den gesamten Payload, weil Feldnamen am besten aus einer echten Antwort kopiert werden, nicht aus einem Blogbeitrag.

python
import os, sqlite3, time, json, requests

BASE = "https://seomasters.dev/api.php"
KEY = os.environ["SEO_API_KEY"]
BATCH = 200

db = sqlite3.connect("dr.db")
db.execute("create table if not exists raw (ts int, batch text, body text)")

domains = [d.strip() for d in open("domains.txt") if d.strip()]
spent = 0
for i in range(0, len(domains), BATCH):
    chunk = domains[i:i + BATCH]
    for attempt in range(4):
        try:
            r = requests.get(BASE, timeout=30,
                params={"action": "bulk_dr", "domains": ",".join(chunk)},
                headers={"Authorization": "Bearer " + KEY})
            break
        except requests.RequestException:
            time.sleep(2 ** attempt)
    else:
        raise SystemExit(f"network gave up at batch {i}")
    if r.status_code in (401, 402):
        raise SystemExit(f"HTTP {r.status_code}: fix the key or top up, do not retry")
    body = r.json()
    if not body.get("ok"):
        raise SystemExit(f"ok=false at batch {i}: {json.dumps(body)[:300]}")
    db.execute("insert into raw values (?,?,?)", (int(time.time()), str(i), r.text))
    db.commit()
    print(body.get("_credits"))

Was jeder Teil tut

action=bulk_dr

Ein Credit pro Aufruf. Die dokumentierte Obergrenze liegt bei 2.000 Domains pro Aufruf.

domains=

Ein GET-Parameter hier. Der Endpunkt akzeptiert auch einen POST-Body, was die bessere Wahl ist, sobald die Listen länger werden. Testen Sie den Separator mit zwei Domains, bevor Sie einen vollständigen Lauf starten.

BATCH = 200

Unsere Gewohnheit, keine Grenze: 200 kurze Domains halten die URL deutlich unter den gängigen Proxy-Grenzen.

_credits

Vorhanden bei authentifizierten Antworten: die Kosten des Aufrufs und der verbleibende Betrag. Das Ausgeben jedes Batches ist die günstigste Audit-Spur.

Rohes body-Feld

Sie parsen später, einmalig, anhand einer gespeicherten Probe. Eine Schemaänderung auf unserer Seite kostet Sie dann eine Abfrage, keinen Neulauf.

Messingwaage, die Gewichte ausbalanciert, um präzise Berechnung zu zeigen

Ein hypothetischer Lauf mit der Berechnung

Dies ist ein Beispiel, keine Kundenstory. Stellen Sie sich eine Agentur vor, die 1.800 Kunden- und Prospekt-Domains in einem Google Sheet verfolgt und jeden Montag ein DR-Update benötigt.

Bei 200 Domains pro Batch sind das 9 Anfragen. Neun Anfragen zu je 1 Credit ergeben 9 Credits, oder $0,0009 pro Lauf, und über 52 Montage hinweg beträgt die Gesamtsumme 468 Credits, was auf unter fünf Cent gerundet wird.

Kosten sind nicht das Limit. Frische ist es. Der Index wird etwa monatlich neu aufgebaut, daher zeigen wöchentliche Läufe drei von vier Wochen denselben Wert, und ein monatlicher Lauf erfasst dieselbe Bewegung für etwa ein Viertel der Aufrufe.

Führen Sie es also monatlich aus und behalten Sie das wöchentliche Sheet für manuelle Bearbeitungen bei. Details dazu, warum der Score stillstehen kann, finden Sie in der DR-Referenz.

Dinge, die in diesem Skript schiefgehen können

SymptomUrsacheLösung
SystemExit: HTTP 402Guthaben ist nullAufladen über die Kontoseite, Neustart; abgeschlossene Batches sind bereits in SQLite gespeichert
SystemExit: HTTP 401Env-Var leer oder Tippfehler im KeyPrint len(KEY) und prüfen Sie das Präfix sk_
ok=false bei Batch 3Fehlerhafter Wert in der Liste, z. B. eine vollständige URLSchema und Pfad vor dem Senden entfernen, siehe Fehler-Tabelle
Gleiches DR wie letzte WocheIndex wurde noch nicht neu aufgebautErwartet; monatlich vergleichen
Lange URL abgelehntBatch zu groß für GETBATCH verringern oder auf POST-Body umstellen

Batch-Größe, GET und POST für lange Listen

Die Obergrenze liegt bei 2.000 Domains pro Aufruf, und das Skript sendet ein Zehntel davon. Jeder Aufruf kostet dieselbe feste Credit-Anzahl, daher ist die Batch-Größe eine Frage von Fehlern und Payload, nicht von Preis.

Warum 200 und nicht 2.000

Ein kleinerer Batch schlägt günstig fehl. Wenn ein Lauf in der Mitte stoppt, ist der Rest, der erneut ausgeführt werden muss, nur ein Batch lang, nicht die gesamte Liste.

Kürzere Listen halten auch jeden gespeicherten Body klein genug, um ihn bei falschen Zahlen mit dem Auge zu scannen, was wichtiger ist als ein paar gespeicherte Aufrufe.

Wenn Sie die Batch-Größe später erhöhen, tun Sie dies einmalig in einer Konstanten und führen Sie den Zwei-Domain-Test erneut durch. Eine größere Zahl ist kein Grund, den Test zu überspringen.

Planen Sie den Batch gegen die Laufzeit, die Sie tolerieren können, nicht gegen die Obergrenze. Ein nächtlicher Job, der vor einem Morgenbericht abgeschlossen sein muss, kann kleinere, sicherere Batches vertragen als ein einmaliger Nachhol-Lauf.

GET für kurze Listen, POST für lange

Die GET-Form packt jede Domain in die URL. Das funktioniert, bis ein Proxy oder eine Log-Zeile die Abfrage abschneidet, weshalb die Tabelle oben empfiehlt, zuerst den Batch zu verringern. Sobald die Listen länger werden, senden Sie denselben domains-Wert in einem POST-Body.

Testen Sie den Separator und die Codierung mit zwei Domains vor einem vollständigen Lauf. Ein fehlendes Komma zeigt sich im ersten Batch, und es ist günstiger, es dort zu sehen.

Ein POST-Body hält die Liste auch aus den Zugriffslogs, die viele Hosts standardmäßig führen. Das ist ein kleiner Privatsphäre-Gewinn für Kundenlisten und kostet eine zusätzliche Header-Zeile im Skript.

Wenn Sie auf POST umstellen, behalten Sie die GET-Version des Skripts als Fallback für kurze Listen bei. Zwei Codepfade klingen aufwendig, aber eine kleine Funktion, die je nach Länge den einen oder anderen wählt, ist leicht lesbar und testbar.

Bereinigen und Duplikate entfernen vor der ersten Anfrage

Leerzeichen entfernen, Host klein schreiben und Duplikate löschen, bevor etwas Ihre Maschine verlässt. Duplikate belegen Platz unter der 2.000-Domain-Obergrenze und sorgen dafür, dass die Anzahl in Ihrem Sheet nicht mit der im Antwort-Objekt übereinstimmt.

Wählen Sie eine Regel für www. und wenden Sie sie in einer einzigen Funktion an, damit dieselbe Seite nie unter zwei Schreibweisen abgefragt wird. Jeder Backlink-Domain-Checker, der über einige hundert Namen hinaus skaliert, benötigt diese Disziplin.

Was der Lauf-Log ausgeben sollte

Geben Sie den _credits-Wert nach jedem Batch aus und schreiben Sie die Batch-Nummer, die Domain-Anzahl und die Zeit daneben. Diese drei Zahlen beantworten fast jede Frage, die ein Finanzleser zu einem Lauf stellen wird.

Ein Lauf, der erst am Ende etwas ausdruckt, gibt Ihnen keinen Hinweis darauf, wann er hängt. Eine Zeile pro Batch ist günstig und verwandelt einen stillen Hang in eine Batch-Nummer, die Sie benennen können.

Halten Sie den Log im selben Ordner wie die SQLite-Datei, damit ein Backup beides kopiert. Ein Log, das woanders gespeichert ist, ist das, das bei einem Vorfall fehlt.

Fertiges Holzverbindungsstück, getrennt vom Rohholz stehend

Drei Erweiterungen, die den Nachmittag wert sind

Speichern Sie die Batch-Nummer neben dem Zeitstempel. Sie ist bereits in der Tabelle oben enthalten und ermöglicht es Ihnen, nur den Batch erneut auszuführen, der nach einem 402-Mid-Run fehlgeschlagen ist.

Erstellen Sie eine kleine Ansicht über die Roh-Tabelle, sobald Sie eine echte Antwort inspiziert haben: Domain, Score, abgerufen-am. Dashboards lesen die Ansicht, und die Roh-Spalte bleibt als Beweis, wenn eine Zahl jemals angezweifelt wird.

Führen Sie es über GitHub Actions oder einen einfachen Cron auf einem kleinen VPS aus. Beides ist in Ordnung. Wichtig ist, dass der Job bei jedem Fehler mit einem Nicht-Null-Exit-Code beendet wird, denn ein stiller Fehler sieht drei Wochen später wie ein veralteter Chart aus.

Das Sortieren der Ausgabe ist eine Frontend-Aufgabe. Sortieren Sie das Portfolio nach Score, bucketen Sie es oder vergleichen Sie es mit dr_distribution (1 Credit), um zu sehen, wo jeder Kunde im Vergleich zu allen 181.132.097 Domains steht.

Feld-Drift und die Roh-Tabelle

Ein langlaufender Job schlägt leise fehl, wenn sich ein Feld in der Struktur ändert. Die Roh-Spalte sorgt dafür, dass dieser Fehler laut und kostengünstig zu beheben bleibt.

Speichern Sie den Roh-Body zuerst

Die Roh-Spalte ist das Sicherheitsnetz. Wenn ein Feld umbenannt wird oder ein neues erscheint, führen Sie eine Abfrage über gespeicherte Antworten durch, anstatt die gesamte Liste erneut zu kaufen. Das spätere Parsen ist günstig, während das Neukaufen nicht ist.

Halten Sie auch eine gespeicherte Probe pro Woche in einer eigenen Datei bereit. Wenn das Parsen fehlschlägt, vergleichen Sie die Probe dieser Woche mit der letzten, die funktioniert hat.

Schreiben Sie den Body genau so, wie er empfangen wurde, ohne Formatierung oder Beschneiden. Jede Änderung, die Sie vor dem Speichern am Text vornehmen, ist eine Änderung, die Sie später nicht rückgängig machen können.

Wie sich ein Drift in der Praxis zeigt

Sagen wir, ein Dashboard zeigt plötzlich leere Scores für einen Batch. Die Roh-Zeile ist noch vorhanden. Sie öffnen sie, vergleichen die Schlüssel mit der Probe von letzter Woche und finden den Unterschied in wenigen Minuten.

Die Lösung ist dann eine einzige Zeile in der Ansicht, kein Neulauf der gesamten Liste. Das ist der ganze Punkt des Speicherns des Bodies.

Halten Sie eine kurze Notiz neben der Ansicht, welches Feld sie liest und wann Sie sie zuletzt mit einer echten Antwort abgeglichen haben. Die Notiz dauert eine Minute zu schreiben und spart später eine lange Suche.

Migrationen mit den Daten ordnen

Erstellen Sie die Roh-Tabelle vor der Ansicht und die Ansicht vor jeder Dashboard-Abfrage. Halten Sie die Create-Anweisungen in einer Datei, führen Sie sie in der richtigen Reihenfolge aus und protokollieren Sie die Batch-Nummer mit jeder Zeile.

Getrennte Datenbanken pro Kunde drifteten genauso wie eine gemeinsam genutzte Datenbank. Die Regel bleibt dieselbe: ein gespeicherter Body pro Aufruf und ein Ort zum Parsen.

Den Parser neben die Tests legen

Legen Sie die Parse-Funktion in dasselbe Repository wie einen Test, der einen gespeicherten Body liest. Der Test ist der Ort, an dem ein gedriftetes Feld zuerst auffällt, lange bevor ein Chart es zeigt.

Nennen Sie die Felder, die der Parser erwartet, in einer Konstanten nahe dem Dateianfang. Ein Reviewer kann so alle Abhängigkeiten von der Antwortstruktur auf einem Bildschirm sehen.

Erster Lauf, in der richtigen Reihenfolge

  1. 1

    Eine Domain testen

    Rufen Sie bulk_dr mit einer bekannten Domain auf und lesen Sie die gesamte Antwort. Notieren Sie die Schlüssel, bevor Sie mit dem Parsen beginnen.

  2. 2

    Zwei Batches ausführen

    Geben Sie dem Skript eine Liste von 400 Domains, damit genau zwei Batches laufen. Prüfen Sie, ob der _credits-Abgleich zwei Aufrufe zu je einem Credit entspricht.

  3. 3

    Gespeicherte Zeilen zählen

    Öffnen Sie die SQLite-Datei und vergleichen Sie die Zeilenanzahl mit der Batch-Anzahl. Eine Diskrepanz bedeutet, dass ein Batch übersprungen oder doppelt gespeichert wurde, und Sie finden das heraus, bevor das Dashboard es merkt.

  4. 4

    Vollständige Liste planen

    Erst nachdem die Testläufe sauber durchgelaufen sind, fügen Sie die vollständige Liste und den Scheduler hinzu. Halten Sie den Nicht-Null-Exit bei, damit ein Fehler in der ersten Nacht laut wird.

Bevor Sie es planen

  • Liste deduplizieren und klein schreiben
  • Entscheiden Sie einmalig in einer Funktion, ob www. bleibt oder nicht
  • Halten Sie den Key in einem Secret-Store, nicht in crontab
  • Bei einem Nicht-Null-Exit-Code alarmieren
  • Berechnen Sie die Credit-Budget-Zahlen für Ihr eigenes Volumen