Ce construiesc oamenii cu un API de web scraping
Cinci sarcini care acoperă cea mai mare parte a traficului pe care îl servim. Fiecare descrie lucrul concret care se strică atunci când extragi datele singur de pe un site, parametrii care îl rezolvă și cât costă în unități de cerere. API-ul este o primitivă fără stare — un URL intră, un rezultat iese — deci toate cinci sunt tipare pe care le compui, nu produse pe care le activezi.
Monitorizarea prețurilor și a disponibilității
Urmărirea prețurilor concurenței, a stocurilor sau a condițiilor de livrare pe un set de pagini de produs, după un program. Partea grea nu e aproape niciodată să parsezi un preț — e că același URL arată un preț diferit în funcție de locul de unde a venit cererea, că paginile de comerț sunt cele mai agresiv protejate anti-bot de pe web și că un monitor care începe în tăcere să returneze null arată identic cu un produs ieșit din stoc.
Țintirea pe țară face muncă reală aici, nu e decor: "country": "de" și "country": "us" pe același URL de produs sunt două răspunsuri diferite, iar la care te-ai referit este o întrebare de business. Paginile de retail sunt și cel mai greu lucru pe care îl servim, ceea ce face din block_resources cel mai bun flag pentru sarcina asta — o pagină de produs randată fără imagini se extrage identic, se întoarce mai repede și se facturează 1 unitate în loc de 5.
Pentru problema cu null, folosește selectori și citește field_errors. Un câmp de preț care vine null fără field_errors înseamnă că pagina chiar nu are element de preț; același null însoțit de o intrare în field_errors înseamnă că site-ul și-a schimbat markupul și selectorul tău nu se mai compilează. Alerta pe al doilea caz este felul în care un monitor îți spune că s-a stricat, în loc să raporteze liniștit că totul e gratis.
O formă practică: POST /v1/batch cu până la 20 de URL-uri de produs, o hartă fields comună, "country" setat pe piața pe care o urmărești și "block_resources": true dacă paginile au nevoie de randare. Fiecare URL se facturează ca propria cerere livrată. Încercările blocate nu costă nimic, ceea ce contează foarte mult tocmai la sarcina asta, pentru că rata de reîncercare pe site-urile de comerț este cea mai mare dintre toate cele de aici.
Date despre companii și lead-uri
Îmbogățirea unei liste de companii pornind de la propriile lor site-uri: ce face firma, unde se află, ce tehnologii sau funcții menționează, dacă angajează. Intrarea este de obicei un domeniu, iar ieșirea este un rând într-un CRM.
Asta e sarcina la care selectorii sunt unealta greșită și merită spus pe șleau de ce. Zece mii de site-uri de companii au zece mii de layouturi diferite. Nu există convenție de h1, nu există clasă .about, nu ai pe ce să selectezi — scrierea unui parser pentru fiecare site este tot costul proiectului. Un prompt care descrie câmpurile dorite funcționează pe toate, fără să cunoască markupul niciunuia, exact cazul pentru care există extragerea AI. Cere planul Scale sau mai sus și se facturează cu 2 unități de cerere în plus pe nivelul fast.
Trimite o schema alături de prompt ca să fixezi forma ieșirii, astfel încât fiecare rând să aibă aceleași chei și inserția în baza ta de date să nu fie nevoită să se apere. Pentru profiluri de persoane și companii, extract_type: "profile" aplică un prompt și o schemă gata scrise, fără să le scrii tu. Dacă site-ul unei companii este o aplicație JavaScript — tot mai des toate sunt — adaugă "render": true împreună cu "block_resources": true, ceea ce ține randarea la 1 unitate.
Două limite în jurul cărora să proiectezi. API-ul nu are stare și nu face crawl: preia URL-ul pe care i-l dai și nimic altceva, deci descoperirea paginii „Despre” a unei companii este treaba ta — "links": true returnează fiecare link de pe pagina principală, rezolvat la un URL absolut, ceea ce de obicei e suficient ca să alegi următoarea preluare. Iar datele personale vin cu obligații independente de felul în care le-ai colectat; vezi Politica de utilizare acceptabilă și Termenii înainte să construiești un flux în jurul lor.
Rezultate din căutare
Aflarea paginilor care există pentru o interogare, urmărirea în timp a felului în care se rezolvă un set de termeni sau alimentarea unui pas de descoperire care decide ce se preia mai departe.
POST /v1/search primește o interogare și returnează rezultate organice — titlu, URL-ul real de destinație cu ambalajul de redirect al motorului deja desfăcut, și un fragment. Se facturează ca o singură cerere livrată. Fii conștient de forma lui înainte să îți faci planuri în jurul lui: count e plafonat la 10, pentru că asta înseamnă o pagină de rezultate de la motor, iar dacă ceri mai mult tot 10 primești. Nu există parametru de paginare, iar adăugarea unuia n-ar fi o schimbare mică — ar fi alt produs.
La ce e cu adevărat bun este descoperirea în interiorul unui flux mai mare: rezolvi o interogare în zece URL-uri, apoi preiei și extragi fiecare dintre ele cum trebuie, cu restul API-ului. Ce nu este: un produs de urmărire a pozițiilor. Dacă ai nevoie de pagini de rezultate adânci, de paritate între motoare sau de urmărirea poziției pe sute de cuvinte-cheie, acest endpoint nu e asta, și preferăm să o spunem aici decât să afli după integrare.
Cercetare de piață și de concurență
Baleieri unice sau periodice care răspund la o întrebare, nu umplu un tabel: cum se poziționează un segment, ce funcții își promovează concurenții, cum s-au schimbat paginile de prețuri, ce sugerează anunțurile de angajare dintr-o categorie despre unde se investește.
Trăsătura distinctivă a acestei sarcini este că de multe ori nu cunoști schema dinainte. Nu extragi un câmp cunoscut dintr-o pagină cunoscută — pui o întrebare unei pagini pe care nu ai văzut-o. "structured": false e făcut exact pentru asta: trimiți un prompt de tipul „ce vinde compania asta și cui?” și primești înapoi un answer în limbaj obișnuit, fără cheie data și fără nume de câmpuri inventate pe care să le ghicești. Costă la fel ca extragerea structurată, pentru că e aceeași pagină și același apel de model.
Când știi forma, presetările acoperă cea mai mare parte a terenului fără schemă: article pentru materiale editoriale și de presă, job pentru semnale de angajare, event, real_estate, discussion pentru discuții din forumuri și comunități. "metadata": true este subapreciat la sarcina asta — returnează JSON-LD schema.org parsat, taguri OpenGraph, canonical și date de publicare, fără niciun selector și fără niciun apel de model, la o unitate de cerere. O cantitate surprinzătoare din ceea ce pentru care lumea scrie prompturi stă deja în datele structurate ale paginii.
Baleierile de cercetare sunt prin natura lor în rafale: nimic o săptămână, apoi câteva mii de pagini într-o după-amiază. Reportul se potrivește cu tiparul ăsta — cererile nefolosite trec în perioada următoare în loc să se piardă — iar o rafală care depășește limita ta de rată primește un 429 cu Retry-After, nu o eroare, deci o buclă de backoff e tot ce trebuie.
Date de antrenare și de fundamentare pentru LLM-uri
Construirea unui corpus pentru fine-tuning sau a unui index de căutare din care un model citește în momentul interogării. Volumul e mare, valoarea pe pagină e mică, iar pragul de calitate ține în întregime de cum arată textul după curățare.
"format": "markdown" este varianta pentru asta. Navigația, antetele, subsolurile, coloanele laterale, scripturile și stilurile sunt eliminate; titlurile, listele, linkurile, blocurile de cod și tabelele supraviețuiesc; iar linkurile și imaginile relative sunt rezolvate la URL-uri absolute, detaliul care contează în momentul în care un fragment e separat de pagina din care a venit și nimeni nu mai poate spune la ce se referea /img/3.png. Costă o unitate de cerere — la fel ca HTML-ul brut, fără suprataxă de Markdown sau AI — iar trimiterea tagurilor HTML într-o fereastră de context înseamnă să le plătești ca tokenuri de două ori, o dată la noi și o dată la furnizorul tău de model.
La scară de corpus, trei lucruri încetează să conteze teoretic și încep să conteze pe factură. Faptul că cererile blocate sunt gratuite schimbă economia parcurgerii cozilor lungi de domenii necunoscute, unde rata de blocare este mare și imprevizibilă. block_resources ține paginile randate la 1 unitate, ceea ce la un milion de pagini este diferența dintre o factură și cinci. Iar plafonul de dimensiune a răspunsului este o plasă de siguranță, nu o limită — răspunsul median pe care îl servim are 40,6 KB, deci ce prinde de fapt este preluarea accidentală a unui video sau a unei arhive care oricum n-ar fi contribuit cu nimic la un corpus de text.
Pentru debit, POST /v1/batch ia 20 de URL-uri pe apel, iar POST /v1/jobs rulează munca asincron, cu apel de webhook, deci o baleiere mare nu te obligă să ții mii de conexiuni deschise. Limitele de rată cresc odată cu planul, până la 1.000 de cereri pe secundă. Ce nu face API-ul în mod deliberat este să facă crawl în locul tău: nu are frontieră, nici coadă, nici planificator de politețe, pentru că acelea sunt decizii despre corpusul tău pe care noi doar le-am nimeri greșit. Tu aduci lista de URL-uri; noi transformăm fiecare URL în text curat.
Altceva
Toate cele cinci tipare sunt aceeași primitivă cu parametri diferiți, ceea ce înseamnă că majoritatea sarcinilor care nu sunt pe lista asta sunt tot doar o altă combinație. Nivelul gratuit are 1.000 de cereri fără card, suficient cât să afli dacă a ta este.