Web scraping a valóságban: headerek, TLS-ujjlenyomat és a DataDome

Miért bukik el a Python-szkripted az első percben, még mielőtt egyetlen sort parsolt volna — és mit látsz erről a hálózaton.

Rövid válasz: A legtöbb web scraper nem a HTML-parsolásnál bukik el, hanem már az első kérésnél 403-at kap, miközben a böngészőben ugyanaz az oldal betöltődik. A szerveroldali botvédelem (pl. DataDome) három rétegben azonosít: (1) HTTP-headerek, (2) TLS-ujjlenyomat (JA3/JA4), (3) aktív JavaScript-challenge. A requests és a httpx már a TLS-rétegen lebukik; a curl_cffi impersonate átjut a passzív ellenőrzésen; az aktív challenge-hez headless böngésző vagy érvényes session-cookie kell. A valódi szűk keresztmetszet azonban gyakran nem a kód, hanem az IP-reputáció.

Bevezetés: a scraper nem a parsolásnál hal meg

Amikor valaki először ír web scrapert, általában azt gondolja: a nehéz rész a HTML kibogarászása lesz. Aztán jön az első komolyabb céloldal, és a szkript már az első kérésnél 403-at kap — miközben a böngészőben ugyanaz az oldal tökéletesen betöltődik. A probléma nem a parsolás, hanem hogy a szerver még mielőtt bármilyen HTML-t küldene, eldönti, hogy gép vagy-e.

Ebben a posztban végigmegyünk a szerveroldali azonosítás három rétegén, mindegyikhez valódi, tcpdump-pal rögzített bizonyítékkal:

  1. HTTP fejlécek — amit elsőre mindenki meglát,
  2. TLS-ujjlenyomat — ami a fejléc-réteg alatt már nem hazudhat,
  3. aktív JavaScript-challenge — amire a TLS-másolás sem elég.

A tesztek egy Ubuntu szervert futottak Python 3.12-vel, httpx, requests és curl-cffi csomagokkal, a forgalmat tcpdump + tshark párossal rögzítettük. A célpont a példákban egy DataDome botvédelmet használó, nagy forgalmú e-kereskedelmi oldal (example-webshop.hu) — a tanulságok bármilyen hasonlóan védett oldalra ugyanúgy érvényesek.

SSR vs SPA — hol lakik az adat?

Mielőtt bármibe belefognál, érdemes tisztázni, hogyan épül fel a céloldal. Két szélső eset van:

  • SSR (server-side rendering): a teljes tartalom ott van a letöltött HTML-ben. Egy requests.get() + BeautifulSoup kombináció sokszor elegendő.
  • SPA (single-page app): a szerver csak üres konténereket (<div id="root">) küld, az adat külön XHR/fetch hívásokból, jellemzően JSON-ból érkezik. A HTML-parsolás ilyenkor nem visz semmire — a Network fülön kell keresned a valódi adatforrást (DevTools → Network → Fetch/XHR, és figyeld, melyik kérés viszi a tartalmat).

Tipp SPA-nál: mielőtt elkezdenéd parsolni a HTML-t, nézd meg a DevTools Network fülén, hogy az alkalmazás milyen API-hívásokat csinál. Ha a szerver kiszolgál egy JSON- vagy MessagePack-választ adó REST/GraphQL/gRPC API-t, azt közvetlenül is hívhatod — sokkal kényelmesebb, mint a DOM-ból kiszedni az adatokat. Egy jól strukturált JSON-válaszban a terméknév, ár, kép URL-je és elérhetőség is ott van, méghozzá strukturáltan, parsing nélkül. Mindig nézd meg, van-e ilyen lehetőség, mielőtt a HTML-parsolás felé fordulnál.

A valóság a kettő keveréke. Egy nagy magyar webshop kezdőlapja például SSR: ha átjutunk a botellenőrzésen, ~1,45 MB-nyi kész HTML-t kapunk, benne több tucat <script type="application/json"> „adatszigettel", amelyekből a React alkalmazás betölti magát a DOM-ba. A terméklistát tartó konténert pedig így találod meg benne:

<div data-role="offers-list">

Ha egy SPA-s listázó oldalt kérsz le sima HTTP-klienssel, pont ezek a div-ek üresek — ez a leggyorsabb praktikus jelzés arra, hogy az adat nem a HTML-ben lakik.

De mindez csak akkor érdekes, ha egyáltalán le tudod tölteni az oldalt. Nézzük, miért nem tudod.

Mit küld valójában a Python HTTP-kliensed? (Demo A: headerek)

Rövid válasz: alapértelmezetten egy leleplező User-Agent-et (pl. python-requests/2.34.2), hiányzó sec-ch-ua* és Sec-Fetch-* headereket, és böngészőre egyáltalán nem jellemző header-sorrendet — a botvédelem ezekből azonnal gépet lát.

Először nézzük meg, pontosan mit küld a szkripted a hálózaton — három népszerű Python HTTP-kliens ugyanarra a címre. Futtattunk egy pici echo szervert a 127.0.0.1:8899-es porton, ami a beérkező kérés-fejléceket változatlan formában nyomtatja ki, közben tcpdump -i lo -s0 -A 'tcp port 8899'-pal rögzítettük a nyers bájtokat.

import requests, httpx
from curl_cffi import requests as creq

URL = "http://127.0.0.1:8899/proba"
requests.get(URL)                          # 1. urllib3 motor
httpx.get(URL)                             # 2. httpx alapértelmezés
creq.get(URL, impersonate="chrome150")     # 3. curl_cffi Chrome-impersonációval

A rögzített, valós kérésfejek (a tcpdump-kimenettel bájtról bájtra megegyeznek):

=== 1. requests ===
GET /proba HTTP/1.1
Host: 127.0.0.1:8899
User-Agent: python-requests/2.34.2
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

=== 2. httpx ===
GET /proba HTTP/1.1
Host: 127.0.0.1:8899
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
User-Agent: python-httpx/0.28.1

=== 3. curl_cffi, impersonate="chrome150" ===
GET /proba HTTP/1.1
Host: 127.0.0.1:8899
sec-ch-ua: "Not;A=Brand";v="8", "Chromium";v="150", "Google Chrome";v="150"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
  (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,
  image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
Priority: u=0, i

A különbség látványos, ha egy botvédelem szemével nézzük:

Szignál requests httpx curl\_cffi (chrome150)
User-Agentpython-requests/2.xpython-httpx/x.yvalódi Chrome UA
sec-ch-ua klienshinteknincsnincsteljes
Sec-Fetch-* headereknincsnincsteljes
Accept-Languagenincsnincsvan
Header-sorrendurllib3-féleeltérőChrome-pontos

Fontos tanulságok:

  • A python-requests/2.34.2 és a python-httpx/0.28.1 User-Agent önmagában azonnali kizárást jelent — de nem elég ezt átírni. Ha csak a UA-t cseréled, a többi mező elárul: hiányzó sec-ch-ua* klienshintek, hiányzó Sec-Fetch-* navigációs headerek, hiányzó Accept-Language.
  • A header-sorrend is jel. Böngészők determinisztikus sorrendben küldik a fejléceket; az urllib3 sorrendje (User-Agent második, Accept negyedik) egyik böngészőre sem hasonlít.
  • A cookie-visszaadás hiánya szintén árulkodó: a valódi böngésző minden kapott Set-Cookie cookie-t vissza is küld a következő kérésben, a frissen írt szkript ezt rendszerint elfelejti.

Persze mindez kézzel is összerakható — a kérdés, hogy ennek van-e értelme, amikor a TLS-réteg úgyis elárul.

Miért nem elég a HTTP-headereket lemásolni? (Demo B: TLS-ujjlenyomat)

Rövid válasz: mert a TLS ClientHello már a HTTP-headerek előtt elárul — a Python-OpenSSL más cipher- és kiterjesztéssorrendet (JA3/JA4-ujjlenyomat) küld, mint a Chrome, GREASE és ECH nélkül —, és ezt fejléccserével nem tudod eltüntetni.

Amikor a Python kliensed HTTPS-kapcsolatot nyit, a TLS ClientHello üzenetében elküldi a támogatott titkosítókat, kiterjesztéseket és elliptic curve paramétereket (lásd ECDHE/ECDSA) — mindet abban a sorrendben, ahogy a mögöttes SSL-csomag (Python esetén OpenSSL) összerakta. Ez a halmaz csomagonként és verzióként jellegzetes: belőle számolják a JA3/JA4 ujjlenyomatot. A szerver tehát még azelőtt tudja, hogy Python-OpenSSL vagy, hogy az első HTTP bájtot is látná.

A lényeg röviden: a python-requests és a sima httpx OpenSSL-helloja teljesen más kiterjesztéssorrendet és cipherlistát küld, mint egy valódi Chrome — GREASE értékek nélkül, széles legacy-cipherlistával. A Chrome-helloban 0x9a9a GREASE, 65037 = encrypted_client_hello (ECH), 17613 = ALPS — ezek egyiken sem szerepelnek a Python-kliensben. Részletes méréseket és a tshark-parancsokat a JA3/JA4 és az ECDHE/ECDSA oldalakon találsz.

UGYANAZOKAT a HTTP-headereket küldted, de a TLS-bájtjaid elárultak. Ezért nem elég a headereket lemásolni.

Átjut a curl_cffi a DataDome-on? (esettanulmány)

Rövid válasz: a passzív TLS-ellenőrzésen igen — a curl_cffi impersonate a valódi Chrome ClientHello-ját küldi —, de az aktív JavaScript-challenge-en önmagában nem, mert azt egy statikus HTTP-kliens nem tudja lefuttatni.

A curl_cffi a trükk lényege: nem a Python SSL-stackjét használja, hanem a curl-impersonate fordított BoringSSL-jét, és a impersonate="chromeXXX" kapcsolóval pontosan azt a ClientHello-t, HTTP/2 SETTINGS-frame-et és header-sorrendet küldi, mint a valódi böngészők. A curl_cffi impersonate TLS-ujjlenyomata a vezetéken gyakorlatilag megkülönböztethetetlen a valódi Chrome-étól.

De meddig visz ez? Nézzük ezt egy élő, DataDome által védett oldalon. Először a klasszikus kísérlet — tökéletesen kimásolt Chrome-headerekkel, sima kliensekkel:

CHROME_HEADERS = {
    "User-Agent": "Mozilla/5.0 ... Chrome/150.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,...",
    "Accept-Language": "hu-HU,hu;q=0.9,en;q=0.8",
    "sec-ch-ua": '"Not;A=Brand";v="8", "Chromium";v="150", ...',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"macOS"',
    "Sec-Fetch-Site": "none", "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-User": "?1", "Sec-Fetch-Dest": "document",
    "Upgrade-Insecure-Requests": "1",
}

httpx.get("https://www.example-webshop.hu/", headers=CHROME_HEADERS)

Valós eredmények (2026. augusztus, saját mérés alapján):

[plain requests, csak Chrome UA]      status=403  len=781   datadome=True
[httpx, tökéletes Chrome-headerek]    status=403  len=781   datadome=True
[curl_cffi impersonate="chrome150"]   status=403  len=780   datadome=True

[robots.txt sima requests-szel]       status=200  len=550
[favicon.ico sima requests-szel]      status=200  len=15086

Mért tények (example-webshop.hu, 2026. augusztus):

  • Sima requests csak Chrome User-Agenttel: 403, 781 bájtos DataDome blokklap.
  • httpx tökéletesen kimásolt Chrome-headerekkel: 403, 781 bájt.
  • curl_cffi impersonate="chrome150": 403, 780 bájt — a passzív TLS-ellenőrzésen átjut, az aktív challenge-en nem.
  • robots.txt és favicon.ico: 200 — a védelem csak a HTML-navigációkra céloz.
  • Érvényes böngésző-cookie + curl_cffi: 200, ~1,45 MB teljes SSR-HTML.

A 403-as válasz 781 bájtos blokklap, amelyben ott a DataDome jellemző aláírása:

<p id="cmsg">Please enable JS and disable any ad blocker</p>
<script>var dd={'rt':'i','cid':'AHrlqAAAAAMA...','host':'geo.captcha-delivery.com',
'cookie':'yRB5ha1z7_Ai8F...'}</script>

Mit tanulunk ebből?

  1. A passzív réteget a TLS-impersonáció kezeli — a sima requests és a header-másolgatós httpx azonnal lepleződik.
  2. Az aktív réteg ennél többet kér: a curl_cffi is 403-as challenge-oldalt kap, mert a DataDome JavaScriptet akar futtatni a böngésződben (rt:'i' = JS-challenge egy iframe-ben). Ezt statikus HTTP-kliens nem tudja végrehajtani.
  3. A védelem a dokumentum-navigációkra céloz: a robots.txt és a favicon.ico önállóan is 200-at ad, csak a HTML-oldalak mennek challengen.

És mi történik, ha van egy valós böngészős session-cookie-d? Headless Chrome-ban meglátogattuk az oldalt (a challenge automatikusan lefutott, a datadome cookie-t kiolvassuk DevTools-on keresztül), majd ugyanarról az IP-ről visszajátszottuk a cookie-t curl_cffi chrome-impersonációval:

from curl_cffi import requests as creq

cookie = open("dd_cookie.txt").read().strip()   # valódi böngészőből
r = creq.get("https://www.example-webshop.hu/",
             cookies={"datadome": cookie},
             impersonate="chrome131", timeout=20)
# -> status: 200 | bytes: 1452851

Eredmény: 200 OK, ~1,45 MB teljes SSR-HTML ("Webshop – Remek árak – Kezdőlap"), tele tartalommal. Ugyanez a kombó — érvényes cookie + hiteles TLS — átmegy a passzív ellenőrzésen.

De vigyázz, ez nem stabil állapot: ugyanezzel a cookie-val egy második navigáció (egy listázó aloldal) pillanatok alatt friss 403-as challenge-et kapott. A DataDome-cookie-t a böngészőben futó JavaScript folyamatosan újragenerálja és a sessionhez, az IP-hez köti — egy kimásolt, statikus cookie hamar lejár vagy új kérésnél újra challenge-et vált ki.

Hol a határ?

Technika Passzív fingerprint Aktív JS-challenge Viselkedéselemzés
requests/httpx defaultelbukikelbukikelbukik
Tökéletes header-másoláselbukik (TLS!)elbukikelbukik
curl\_cffi impersonationátmegyelbukikelbukik
curl\_cffi + érvényes cookieátmegyátmenetileg átmegyelbukik
Headless Chromiumrészlegesengyakran elbukikelbukik
Headless, valódi böngészőátmegyátmegyátmegy

Összegzés: a TLS-impersonáció szükséges, de nem elégséges feltétel. Aktív challenge-eknél vagy headless böngészővel dolgozol (Playwright headless=False), vagy valódi böngészőből szerzett session-cookie-t használsz — különben feladod.

Miért kapok néha 403-at, néha 200-at ugyanazzal a kóddal? (IP-reputáció)

Rövid válasz: mert a DataDome a kliens IP-címe és TLS-ujjlenyomata alapján, szerveroldalon dönti el, hogy megoldható challenge-et, „szürke" kemény tiltást vagy átengedést ad — így ugyanaz a kód tiszta IP-ről 200-at, rossz reputációjú IP-ről 403-at kap.

A fenti „hol a határ?" táblázat azt sugallja, hogy az aktív challenge a végállomás. A gyakorlatban viszont a kép árnyaltabb — és a különbség sokszor nem a kódon múlik, hanem azon, honnan indul a kérés.

Vegyük ismét az example-webshop.hu-t, ezúttal egy curl_cffi-alapú klienssel, változatlan headerekkel és impersonate="chrome150"-nel. Ugyanaz a szkript egyik nap tiszta 403-at kap, a következő futáson viszont 200-at, tele találattal — pedig egyetlen sort sem írtunk át közben. A magyarázat, hogy a DataDome nem egy, hanem három különböző választ ad ugyanarra a kliensre, és szerveroldalon dönti el, melyiket — még mielőtt bármilyen JavaScript lefutna:

Állapot HTTP Mit kapsz Jelentés
JS-challenge (rt:'i')403var dd={'rt':'i',...} blokk + i.jsaz egyetlen megoldható változat: valódi challenge fut le egy iframe-ben
Szürke tiltás403csupasz, szürke oldal: Please enable JS..., var dd nélkülnincs felkínált challenge — nincs mit „megoldani" rajta
Átengedés200teljes SSR-HTMLa TLS-print és az IP is rendben volt

A „szürke tiltás" a legfélrevezetőbb a három közül: nem is challenge-oldal, hanem egy üres, dísztelen hibalap — ugyanaz a Please enable JS and disable any ad blocker felirat, mint a challenge-nél, de a mögötte lévő var dd={...} blokk és az i.js hiányzik. Nincs mit futtatni, nincs mit megoldani: a DataDome ilyenkor egyszerűen közli, hogy ezt a klienst most egyáltalán nem szolgálja ki.

És itt a kulcs: hogy challenge-oldalt vagy szürke tiltást kapsz-e, az szerveroldalon, még a JavaScript előtt eldől — és nem a klienskódhoz, hanem az IP-cím + TLS-ujjlenyomat reputációjához kötött. Ha az IP-d egyszer a „szürke" kategóriába csúszott, a megoldható challenge-et meg sem kapod, hiába lenne tökéletes a megoldód. Fordítva viszont: egy tiszta IP-ről a curl_cffi passzív rétege (hiteles TLS + Chrome-pontos header-sorrend) sokszor magától átjut, bárminemű challenge-megoldás nélkül. Ezt élőben is megfogtuk: a 403-at adó kérés után — a kliens egyetlen bájtját sem érintve — másik kimenő IP-ről próbáltuk újra, és rögtön 200-at kaptunk, tele valódi termékadattal.

Ebből két gyakorlati tanulság adódik, amit a „hol a határ?" táblázat önmagában elrejt:

  • Az aktív challenge megoldása sokszor nem is a feladat. A curl_cffi passzív rétege elég, ha az IP-reputáció rendben van. A valódi szűk keresztmetszet nem a JavaScript, hanem az IP — ide fektesd az energiát, ne egy böngésző nélküli JS-solver visszafejtésébe.
  • A sűrű kopogtatás melegen tartja a tiltást. Aki percenként pingeli a szürke oldalt, azzal csak késlelteti a feloldást. Az exponenciális backoff-minta (lásd a poszt záró szakaszát) és az IP-rotáció a nyerő — pont az ellentéte a türelmetlen újrapróbálkozásnak.

A gyakorlati recept ezért egyszerűbb, mint elsőre tűnik: detektáld, milyen állapotot kaptál (a datadome cookie értéke a missing / allocation~ jelet tartalmaz — / ok skálán mozog), és amikor szürkét vagy allokációt látsz, ne a challenge-et próbáld erőltetni, hanem válts IP-t (residential vagy mobil proxy), a régit pedig hagyd kihűlni. A hiteles TLS-t adja a curl_cffi, a tiszta identitást a proxy — a kettő együtt jut át.

Összefoglaló checklist

Mielőtt legközelebb 403-at kapsz, futtasd végig:

  1. SSR vagy SPA? Ha a data-role="offers-list"-szerű konténerek üresek a letöltött HTML-ben, az adat XHR/JSON-ból jön — DevTools Network fülön keresd a forráskérést. SPA-nál mindig nézd meg, van-e közvetlen API-hívás (JSON, MessagePack, GraphQL) — ha igen, azt használd a HTML-parsolás helyett.
  2. Headerek: valódi böngészőből másolj teljes header-készletet (UA, Accept, Accept-Language, sec-ch-ua*, Sec-Fetch-*, Referer-lánc), és vidd vissza a cookie-kat.
  3. TLS: használj curl_cffi impersonate=-et; ellenőrizd tcpdump + tshark segítségével, hogy a ClientHello tényleg Chrome-ot mutat-e (GREASE, ECH, kiterjesztés-sorrend).
  4. Aktív védelem: ha JS-challenge jön, HTTP-klienssel nincs tovább — headless böngésző vagy cookie-seeding, etikus keretek között.
  5. Teszteld helyben: egy echo szerver + tcpdump percben elárulja, pontosan mit küld a szkripted a hálózaton.

Etika és jog gyorsan

A technika önmagában semmi — a használat számít. Mielőtt scrapelsz: olvasd el a robots.txt-et (szinte minden oldal közzéteszi, mit tilt), tarts mértékkel a kérésszámmal (ne DDoS-olj véletlenül), és nézd meg az oldal ÁSZF-ét — sok oldal explicit tilalmat ír elő az automatizált elérésre. Nyilvános termékadatok esetén GDPR tipikusan nem merül fel, mert nem személyes adatot kezelsz, de a szerződési feltételek (ToU) ettől függetlenül kötnek. Soha ne scrappelj bejelentkezést igénylő, személyes adatokat tartalmazó felületeket engedély nélkül.

Hogyan kezeljem a 429-et scraping közben? (exponenciális backoff)

Rövid válasz: ne adj fel és ne is ronts vissza azonnal — várj egyre hosszabb, véletlen jitterrel megtoldott szünetekkel (2, 4, 8, 16… másodperc), és ha a szerver Retry-After headert küld, azt mindig tartsd be.

A 403 nem az egyetlen kód, amivel találkozhatsz. A 429 Too Many Requests és az 5xx szerverhiba esetén az udvarias scraper nem adja fel azonnal, de nem is rohan vissza: exponenciális backoff-fal vár – egyre hosszabb szünetekkel (2, 4, 8, 16… másodperc), és jitterrel, azaz véletlen plusz késleltetéssel. A jitter nélkül ugyanis minden párhuzamos kliens pontosan ugyanabban a másodpercben csapna rá újra a szerverre, és a backoff pontosan azt a thundering-herd-helyzetet termelné újra, amitől a szerver épp próbál megszabadulni.

import random, time
from curl_cffi import requests as creq

def get_slowly(url: str, *, max_tries: int = 6,
               base_delay: float = 2.0, cap: float = 60.0):
    """429/50x-re lassú retry – csak idempotens GET-et adj be neki!"""
    for attempt in range(max_tries):
        r = creq.get(url, impersonate="chrome150", timeout=30)
        if r.status_code < 400:
            return r
        if r.status_code != 429 and r.status_code < 500:
            r.raise_for_status()            # 4xx: újrapróbálni sem éri meg
        retry_after = r.headers.get("Retry-After")
        if retry_after and retry_after.isdigit():
            delay = float(retry_after)      # ha a szerver mondja, ő tudja pontosan
        else:
            delay = min(cap, base_delay * 2 ** attempt)
        delay += random.uniform(0, min(cap, delay))   # full jitter
        time.sleep(delay)
    raise RuntimeError(f"{url}: {max_tries} próbálkozás után is hibás")

A szabályok röviden:

  • A várakozási sor exponenciálisan nő, cap-pel lefedve — lassan engedd vissza a klienst, ne pörgesse a szervert.
  • Ha jön Retry-After header, azt mindig tartsd be, akkor is, ha rövidebb a saját számításodnál.
  • Csak 429 és 5xx esetén próbálkozz újra; a többi 4xx (403, 404) nem múlik el várakozással.
  • Idempotens GET-re ez biztonságos; POST-nál gondold át, mi történik kétszeres végrehajtásnál.

Gyakori kérdések

Miért kap a Python requests 403-at, a böngésző meg nem?

Mert a szerveroldali botvédelem még a HTML elküldése előtt azonosítja a klienst. A requests és a httpx alapértelmezett User-Agent-je, hiányzó sec-ch-ua* / Sec-Fetch-* headerei és nem böngészőszerű TLS-ujjlenyomata együtt azonnal elárulja, hogy nem valódi böngészőről van szó — ezért kapsz 403-at, miközben ugyanaz az URL a Chrome-ban betöltődik.

Mi az a JA3/JA4 TLS-ujjlenyomat?

A JA3 és az újabb JA4 egy tömör aláírás, amelyet a szerver a TLS ClientHello üzenetéből számol: a támogatott titkosítók, kiterjesztések és paraméterek halmazából és sorrendjéből. Mivel ezt a mögöttes SSL-könyvtár (Python esetén OpenSSL) rakja össze, egy Python-kliens ujjlenyomata jól elkülönül a valódi Chrome-étól — függetlenül attól, milyen HTTP-headereket küldesz.

Meg lehet kerülni a DataDome-ot curl_cffi-vel?

Részben. A curl_cffi impersonate a valódi böngésző TLS- és HTTP/2-ujjlenyomatát küldi, ezért a passzív ellenőrzésen átjut. Az aktív JavaScript-challenge-et (rt:'i') azonban egy statikus HTTP-kliens nem tudja lefuttatni, ahhoz headless böngésző vagy érvényes session-cookie kell. A gyakorlatban a legfontosabb tényező nem is a challenge, hanem az IP-reputáció.

Miért kapok néha 403-at, néha 200-at ugyanazzal a kóddal?

Mert a DataDome a kliens IP-címe és TLS-ujjlenyomata alapján, szerveroldalon dönti el a választ: megoldható challenge-et, „szürke" kemény tiltást (üres hibaoldal, challenge nélkül) vagy átengedést ad. Ha az IP jó reputációjú, a curl_cffi passzív rétege magától átjut; ha a „szürke" bucketbe került, még a challenge-et sem kapod meg. A megoldás az IP-rotáció, nem a challenge erőltetése.

Meddig él egy datadome cookie?

Nem stabil. A datadome cookie-t a böngészőben futó JavaScript folyamatosan újragenerálja, és a sessionhez meg az IP-hez köti. Egy kimásolt, statikus cookie hamar lejár, vagy egy következő navigációnál új challenge-et vált ki — ezért nem megbízható hosszú távú megoldás a cookie kézi átemelése.

Legális a web scraping?

A technika önmagában nem tilos, de a használat számít. Olvasd el a robots.txt-et és az oldal ÁSZF-ét (sok oldal explicit tilalmat ír elő az automatizált elérésre), tarts mértéket a kérésszámmal, és soha ne gyűjts személyes adatot vagy bejelentkezés mögötti tartalmat engedély nélkül. Nyilvános termékadatoknál a GDPR tipikusan nem merül fel, a szerződési feltételek viszont akkor is kötnek.

Previous Post Next Post