Hoppa till innehåll

Backlink-data som JSON: $100 gratis kredit, inget kort, Google-inloggning.

SEOMasters.dev
SEOMasters.devSkapa konto

Bulk DR för en klientlista, som ett Python-jobb på 40 rader

Du har ett kalkylblad med domäner och en dashboard som behöver ett tal per rad. Ett verktyg för att kontrollera backlink-domäner för det jobbet är mest plumbing, och detta skript är plumbinget.

Färdiga träblock som sorteras i staplar på en arbetsbänk

Skriptet

Det läser domains.txt, skickar 200 domäner per begäran, sparar varje rått svar i SQLite och skriver ut vad körningen kostade. Vi lagrar hela payloaden eftersom fältnamn bäst kopieras från ett verkligt svar, inte från ett blogginlägg.

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"))

Vad varje del gör

action=bulk_dr

En kredit per anrop. Den dokumenterade gränsen är 2 000 domäner per anrop.

domains=

En GET-parameter här. Slutpunkten accepterar också en POST-body, vilket är det bättre valet när listorna blir långa. Testa avgränsaren med två domäner innan en full körning.

BATCH = 200

Vår vana, inte en gräns: 200 korta domäner håller URL:en väl under vanliga proxygränser.

_credits

Närvarande på autentiserade svar: kostnaden för anropet och vad som finns kvar. Att skriva ut det varje batch är den billigaste revisionsstigen.

Rå body-kolumn

Du parser senare, en gång, mot ett lagrat exempel. En schemaförändring på vår sida kostar dig då en fråga, inte en omkörning.

Mässvåg som balanserar vikter för att visa exakt beräkning

Ett hypotetiskt körningsexempel, med aritmetiken

Detta är ett exempel, inte en kundberättelse. Tänk dig ett byrå som spårar 1 800 klient- och prospectsdomäner i ett Google Sheet och vill ha DR uppdaterat varje måndag.

Med 200 domäner per batch blir det 9 anrop. Nio anrop till 1 kredit vardera är 9 kredit, eller $0,0009 per körning, och över 52 måndagar blir totalen 468 kredit, vilket avrundas till under fem cent.

Kostnad är inte begränsningen. Fräschhet är det. Indexet byggs om ungefär månatligen, så veckokörningar kommer att visa samma värden tre veckor av fyra, och en månatlig körning fångar samma rörelse för ungefär en fjärdedel av anropen.

Så kör det månatligen och behåll det veckovis sheetet för manuella redigeringar. Detaljer om varför poängen kan stå stilla finns i DR-referensen.

Saker som kan gå fel i detta skript

SymtomOrsakÅtgärd
SystemExit: HTTP 402Saldo är nollLadda upp från kontosidan, kör om; färdiga batchar finns redan i SQLite
SystemExit: HTTP 401Miljövariabel tom eller felaktig nyckelSkriv ut len(KEY) och kontrollera prefixet sk_
ok=false på batch 3Felaktigt värde i listan, t.ex. en fullständig URLTa bort schema och sökväg innan du skickar, se felmattan
Samma DR som förra veckanIndexet har inte byggts om änFörväntat; jämför månatligt
Lång URL avvisadBatch för stor för GETSänk BATCH eller flytt till en POST-body

Batchstorlek, GET och POST för långa listor

Takgränsen är 2 000 domäner per anrop, och skriptet skickar en tiondel av det. Varje anrop kostar samma fasta kredit, så batchstorlek är en fråga om fel och payload, inte om pris.

Varför 200 och inte 2 000

En mindre batch misslyckas billigt. Om en körning stannar mitt i, är svansen som ska köras om bara en batch lång, inte en hel lista lång.

Kortare listor håller också varje sparad body liten nog att skannas för ögat när ett tal ser fel ut, och det är viktigare än ett par sparade anrop.

Om du senare höjer batchstorleken, gör det en gång, i en konstant, och kör om tvådomänstestet. Ett större nummer är ingen anledning att hoppa över testet.

Planera batchen mot den körningslängd du kan tolerera, inte mot takgränsen. En nattlig jobb som måste vara klart innan en morgonrapport kan ha mindre, säkrare batchar än en engångsbackfill.

GET för korta listor, POST för långa

GET-formulären lägger varje domän i URL:en. Det fungerar tills en proxy eller en logglinje trunkerar frågan, vilket är varför tabellen ovan säger att sänka batchen först. När listorna växer, skicka samma domains-värde i en POST-body.

Testa avgränsaren och kodningen med två domäner innan en full körning. En vilkommande kommatecken syns i den första batchen, och det är billigare att se det där.

En POST-body håller också listan borta från åtkomstloggar som många värdar sparar som standard. Det är en liten vinst i integritet för klientlistor, och det kostar en extra header-rad i skriptet.

När du byter till POST, behåll GET-versionen av skriptet som en fallback för korta listor. Två kodvägar låter tunga, men en liten funktion som väljer en baserat på längd är lätt att läsa och testa.

Trimma och ta bort dubbletter innan första begäran

Ta bort mellanslag, gör värdnamnet till gemener och ta bort dubbletter innan något lämnar din maskin. Dubbletter tar upp utrymme under 2 000-domängränsen, och de får antalet i ditt sheet att stämma överens med antalet i svaret.

Välj en regel för www. och tillämpa den i en enda funktion, så att samma webbplats aldrig slås upp under två stavningar. Alla backlink-domänskontroller som skalar över några hundra namn behöver den disciplinen.

Vad körningsloggen bör säga

Skriv ut _credits-värdet efter varje batch, och skriv batchnumret, domänantalet och tiden bredvid det. De tre talen svarar på nästan alla frågor en finansiell läsare kan ha om en körning.

En körning som inte skriver ut något förrän i slutet ger dig ingen ledtråd när den fastnar. En rad per batch är billig, och den gör en tyst hängning till ett batchnummer du kan namnge.

Behåll loggen i samma mapp som SQLite-filen, så att en backup kopierar båda. En logg som finns någon annanstans är den som försvinner under en incident.

Färdigt träfack som står åtskilt från rått virke

Tre tillägg som är värda eftermiddagen

Spara batchnumret bredvid tidsstämpeln. Det finns redan i tabellen ovan, och det låter dig köra om bara batchen som misslyckades efter en 402 mitt i körningen.

Skriv en liten vy över råtabellen när du har inspekterat ett verkligt svar: domän, poäng, hämtad-vid. Dashboards läser vyn, och råkolumnen finns kvar som ditt bevis om ett tal någonsin ifrågasätts.

Kör det från GitHub Actions eller en vanlig cron på en liten VPS. Båda är bra. Det viktiga är att jobbet avslutas med icke-noll vid något fel, eftersom ett tyst fel ser ut som en inaktuell graf tre veckor senare.

Att sortera utdata är en front-end-fråga. Rangordna portföljen efter poäng, bucketa den, eller jämför den med dr_distribution (1 kredit) för att se var varje klient sitter bland alla 181 132 097 domäner.

Fältförskjutning och råtabellen

Ett långkörande jobb misslyukas tyst när ett fält ändrar form. Råkolumnen finns där så att felet förblir högljutt och billigt att fixa.

Spara den rå body:n först

Råkolumnen är säkerhetsnätet. Om ett fält döps om eller ett nytt dyker upp, kör du en fråga över lagrade svar istället för att betala för hela listan igen. Att parsa senare är billigt, medan att köpa om är det inte.

Behåll ett sparat exempel per vecka i en egen fil också. När en parsing bryts, diff:a denna veckas exempel mot den sista som fungerade.

Skriv body:n exakt som den mottogs, utan vacker utskrift eller trimning. Any change you make to the text before storing it is a change you cannot undo later.

Vad en förskjutning ser ut i praktiken

Säg att en dashboard plötsligt visar tomma poäng för en batch. Den råda raden finns fortfarande kvar. Du öppnar den, jämför dess nycklar med förra veckans exempel, och hittar skillnaden på några minuter.

Åtgärden är då en enda rad i vyn, inte en omkörning av hela listan. Det är hela poängen med att lagra body:n.

Behåll en kort anteckning bredvid vyn som säger vilket fält den läser och när du senast kontrollerade den mot ett verkligt svar. Anteckningen tar en minut att skriva och sparar en lång sökning senare.

Ordna migrationerna med datan

Skapa råtabellen före vyn, och vyn före någon dashboard-fråga. Håll create-satserna i en fil, kör dem i ordning och registrera batchnumret med varje rad.

Separata databaser per klient förskjuts på samma sätt som en delad databas gör. Regeln förblir densamma: en lagrad body per anrop, och en plats att parsa den.

Håll parsaren bredvid testerna

Placera parse-funktionen i samma repository som ett test som läser en sparad body. Testet är den plats där ett förskjutet fält först visas, långt innan en graf gör det.

Namnge fälten som parsaren förväntar sig i en konstant nära toppen av filen. En granskare kan då se alla beroenden på response-formen på en enda skärm.

Första körningen, i ordning

  1. 1

    Testa en domän

    Ring bulk_dr med en enda domän du känner till, och läs hela svaret. Skriv ner nycklarna innan du skriver någon parseringskod.

  2. 2

    Kör två batchar

    Ge skriptet en lista på 400 domäner, så att exakt två batchar körs. Kontrollera att _credits-minskningen matchar två anrop till en kredit vardera.

  3. 3

    Räkna de lagrade raderna

    Öppna SQLite-filen och jämför radantalet med batchantalet. En mismatch betyder att en batch hoppades över eller sparades två gånger, och du hittar det innan dashboards gör det.

  4. 4

    Schemalägg hela listan

    Bara efter att testkörningarna är rena, lägg till hela listan och schemaläggaren. Behåll icke-noll-avslutningen, så att ett fel blir högljutt på första natten.

Innan du schemalägger det

  • Ta bort dubbletter och gör listan till gemener först
  • Bestäm en gång, i en funktion, om www. ska finnas kvar eller inte
  • Behåll nyckeln i ett secret store, inte i crontab
  • Varna vid ett icke-noll-avslutningskod
  • Kör kreditbudgeten talen för din egen volym