Web scraping: ce este, când e justificat, cum o faci legal și curat
Web scraping înseamnă extragerea automată de date din site-uri publice. Cum o faci legal, capcane reale și cum o folosim noi la 24M de înregistrări.
Cuprins
Web scraping înseamnă extragerea programatică de date din site-uri web prin analizarea HTML-ului direct, fără a folosi un API oficial. Tehnica este la fel de veche ca web-ul însuși și acoperă tot, de la o foaie de calcul simplă cu date dintr-un singur tabel până la pipeline-uri cu milioane de URL-uri pe zi. Pentru proiecte unde sursa primară de date este un site web fără API, scraping-ul este singura cale; pentru orice altceva, este de obicei alegerea greșită.
Confuzia tipică este între scraping și crawling: crawling înseamnă descoperirea de URL-uri (motor Googlebot urmărind link-uri), iar scraping înseamnă extragerea de date din pagini cunoscute. În practică, cele două se combină: un crawler care descoperă pagini noi alimentează un scraper care extrage informațiile relevante.
Ce este scraping mai exact?
Un proces în doi pași: fetch (obținerea HTML-ului prin HTTP) și parse (extragerea datelor relevante din HTML). Fiecare pas are alegeri tehnice cu impact direct asupra costului, vitezei și fiabilității.
Pentru fetch, două categorii de unelte:
- HTTP clients pure (Scrapy, requests, axios). Trimit cereri HTTP, primesc răspunsuri raw. Rapide, ieftine (10-100 cereri/secundă per worker), dar nu pot vedea conținut randat de JavaScript.
- Headless browsers (Playwright, Selenium, Puppeteer). Lansează un browser fără UI, execută JavaScript-ul paginii, returnează DOM-ul randat. Lente (1-5 cereri/secundă per browser) și consumatoare de RAM (300-500 MB per instanță), dar singurele care văd conținut dinamic.
Pentru parse, două categorii de unelte:
- HTML parsers cu CSS/XPath selectors (BeautifulSoup, Cheerio, lxml). Citești HTML-ul, selectezi elementele prin
.product-card .pricesau//div[@class="price"]. Simplu pentru pagini cu structură stabilă. - Regex pe HTML raw. Tentant pentru cazuri simple, dar fragil; folosește doar pentru extragerea unui ID din URL sau pattern-uri text simple.
Cum funcționează un scraper bun?
Un scraper de producție are cinci straturi care îl deosebesc de un script rapid în Python:
- Rate limiting: niciodată mai mult de 1-2 cereri pe secundă către același domeniu, plus jitter aleatoriu. Se respectă
robots.txt; dacăCrawl-delay: 5e setat, treci la 0,2 cereri/secundă. - Retry inteligent: failover-uri grațioase pentru erori temporare (5xx, timeout, connection reset). Backoff exponențial, nu retry imediat. Pentru 429 (rate limit hit), respectă header-ul
Retry-Afterdacă există. - Caching: dacă un URL a fost descărcat recent și conținutul nu se schimbă (ETags, Last-Modified), folosește versiunea cached. Reduce sarcina pe sursă și viteza ta de extragere.
- Identitate transparentă: user-agent care identifică explicit cine ești (
"CrawlerraBot/1.0 (+https://crawlerra.com/bot)") și o pagină de contact dacă cineva are obiecții. Mai bun decât să mimezi un browser și să fii descoperit ulterior. - Monitorizare: rata de succes per domeniu, latența medie, frecvența de blocare. Când rata de succes scade sub 90%, ceva s-a schimbat (structură HTML, nou rate limit, IP blocat) și trebuie investigat înainte ca date stale să se acumuleze.
Care sunt capcanele frecvente?
- Scraping fără rate limit. Loop simplu care trimite cereri cât de repede poate procesa. Site-ul te detectează, IP-ul tău e blocat pentru ore sau zile. Pentru proiecte serioase, blocarea înseamnă pierdere de date pentru un întreg ciclu de refresh.
- User-agent default. Folosești
requestssauScrapycu user-agent-ul implicit (python-requests/2.31.0). Majoritatea WAF-urilor moderne îl blochează automat. Setează un user-agent realist sau identifică-te explicit. - Regex pe HTML brut. „Numai pentru cazuri simple", după care cazurile devin tot mai puțin simple. Folosește un parser HTML real chiar și pentru extragere minimă; recuperarea din HTML malformat este o problemă deja rezolvată.
- Nu testezi schimbări de structură. Site-ul schimbă
<div class="price">în<span class="amount">. Scraper-ul tău returneazănullsau valori greșite, dar nu cade. Datele tale devin gunoi tăcut. Verifică validitatea datelor extrase (e numeric? e între 0 și o limită rezonabilă?) la fiecare run; alertează pe drift. - Ignori robots.txt. Considerat de mulți ca politică, nu lege; în realitate, ignorarea lui produce dovezi clare în instanță că ai acționat cu rea-credință. Citește-l, respectă-l, documentează că o faci. Costul tehnic este minim, costul legal de a nu-l face este real.
Cum folosim scraping la crawlerra?
Avem două proiecte mari unde scraping-ul este sursa primară de date: PromoAzi (comparator de prețuri pentru produse de tip grocery, 9 retaileri, 95,6% rata de captură a ofertelor) și RestoInsights (date despre restaurante, peste 24 de milioane de înregistrări la nivelul ultimei revizii). Plus zeci de scrapere mai mici pentru clienți și proiecte interne.
Stack-ul nostru de bază este Python + Scrapy pentru fetch + parse pe pagini statice, și Playwright pentru pagini cu JavaScript modern. Selenium îl folosim doar acolo unde infrastructura a fost moștenită. Datele extrase ajung în Postgres direct (pentru date relaționale curate) sau în tabele JSONB intermediare (pentru date care cer transformări multiple). Orchestrarea workflow-urilor periodice (nightly refresh, recovery după eșecuri) merge prin n8n; pentru date care necesită extragere imediată (webhook-uri, evenimente), folosim handler-uri Spring Boot dedicate.
Latura legală nu este opțională: înainte să rulăm un scraper nou pe un site nou, citim robots.txt, citim ToS-ul, evaluăm dacă datele sunt factuale (OK) sau protejate (nu). Pentru detaliile complete despre GDPR, copyright și politici interne de bună-credință, vezi ghidul nostru despre web scraping legal în România1; pentru principiul „deținem tot stack-ul" care ne face responsabili de calitatea acestor decizii, vezi articolul editorial.
Cum verifici că un scraper merge curat și legal?
Patru verificări pentru fiecare scraper nou înainte să-l declari de producție. Conformitate cu robots.txt: rulează un test care descarcă robots.txt și verifică explicit fiecare URL pe care vrei să faci scraping. Crawler-ul trebuie să refuze pe cele care nu sunt permise; documentează asta într-un test automat.
Verificare de date: după fiecare run, o suită de assertions automate. Prețurile sunt numerice și pozitive? Stocurile sunt 0, „in stock" sau valori cunoscute? Numărul de înregistrări extrase este în limita așteptată (peste 80% din run-ul anterior, sub 120%)? Drift în oricare direcție produce o alertă.
Test cu blocare provocată: introdu intenționat o eroare 429 sau un timeout. Scraper-ul trebuie să facă retry cu backoff, nu să se prăbușească. Pentru workflow-uri critice, simulează un domeniu offline complet și verifică că fallback-urile funcționează.
Monitorizare continuă: prin stack-ul nostru de observabilitate, fiecare scraper expune metrici de succes/eșec/durată. Alertăm pe rata de succes sub 90% pe un domeniu pentru mai mult de două ore consecutive (semn de schimbare structurală) și pe creșteri bruște în latență (semn de blocare iminentă). Asta înseamnă că diagnosticăm probleme înainte ca datele să devină gunoi.
- PromoAzi a atins 95,6% rata de captură pentru promoțiile săptămânale, măsurată pe 14 lanțuri de retail în luna ianuarie 2026.
[promoazi.offer_capture]
Întrebări frecvente
Scraping vs API public, care e alegerea corectă?
API public mereu când există, scraping doar când nu există. Un API este un contract: site-ul îți garantează formatul și disponibilitatea. Scraping-ul depinde de structura HTML care se poate schimba. Pentru date care vin de la un site care nu are API (sau care are unul rate-limited strâns), scraping-ul rămâne singura cale; pentru date din site-uri cu API documentat, scraping-ul e auto-sabotaj.
Este scraping-ul legal în România?
Pe scurt: date factuale public-disponibile, da; date personale, intră sub GDPR; conținut protejat de copyright, nu. Prețuri din vitrine, disponibilități de stoc, descrieri de produs sunt date factuale și publice, e OK să faci scraping dacă respecți robots.txt și rate limit-uri rezonabile. Adrese de email, profiluri personale, recenzii cu nume sunt date personale și cer bază legală GDPR. Articole de presă, fotografii, design layout-uri sunt protejate de copyright. Detaliile complete în ghidul nostru despre web scraping legal.
Selenium, Playwright sau Scrapy, care e cel mai bun?
Scrapy pentru pagini statice și viteză; Playwright pentru pagini cu JavaScript modern; Selenium doar pentru integrarea cu sisteme legacy. Scrapy este de 10-20x mai rapid decât oricare dintre celelalte două și consumă mult mai puțin RAM, dar nu poate vedea conținut randat de JavaScript. Playwright este alternativa modernă la Selenium, cu API mai curat și execuție paralelă mai bună. Selenium rămâne pentru proiecte vechi sau integrări cu Selenium Grid existent.
Cum evit să fiu blocat de site-uri?
Rate limit conservativ, user-agent realist, sesiuni stabile și respect pentru robots.txt. Rate limit de 1-2 cereri pe secundă către același domeniu, plus jitter aleatoriu, evită majoritatea detecțiilor naive. User-agent care arată ca un browser real (cu version string actual). Sesiuni cu cookie persistente (browserii reali nu deschid o sesiune nouă la fiecare request). Și niciodată mai rapid decât ce permite robots.txt. Dacă încalci aceste reguli, te aștepți să fii blocat și ai meritat-o.
Cât de des să rulezi un scraper?
Cât de des contează datele, nu mai des. Pentru prețuri grocery, refresh nocturn este suficient (datele se schimbă la deschiderea magazinului). Pentru rezultate sportive live, refresh la minut. Pentru date statice (descrieri produs), o dată pe săptămână. Rulând mai des decât e nevoie, încarci servere străine fără justificare și crești riscul de blocare; rulând mai rar, datele tale degenerează.