curl_cffi vs requests: Wat Het Oplost en Wat Niet

Het vervangen van requests door curl_cffi verandert veel 403's in 200's. Het doet echter niets voor een verbrande IP. Hier is de grens tussen de twee, en een vijftien minuten durende test die je vertelt aan welke kant je probleem zich bevindt.

De vraag curl_cffi vs requests komt meestal midden in een incident op: een scraper die maandenlang draaide begint 403 te retourneren bij de eerste oproep, iemand op Reddit zegt de client te wisselen, en het werkt. Dat is een echt effect met een echte verklaring — maar de manier waarop het wordt herhaald ("gebruik gewoon curl_cffi") verbergt zowel wat er gebeurt als waar het ophoudt te helpen. requests is niet traag of slecht geschreven. Het heeft precies één nadeel in een scraping-context, het is een grote, en het heeft niets te maken met de API die je typt. Dit is waar de twee clients echt verschillen, wat er verandert als je wisselt, en het ene ding dat imitatie nooit zal oplossen, ongeacht welke bibliotheek je kiest.

Het enige verschil dat ertoe doet

Beide bibliotheken sturen dezelfde headers. Het verschil zit een laag dieper, in de TLS-handshake die de verbinding opent voordat er een enkele HTTP-byte beweegt. requests zit op urllib3 en OpenSSL, die een cipherlijst, extensieset en volgorde adverteren die bij Python horen en niets anders. curl_cffi is een binding naar een gepatchte fork van curl die een browser's ClientHello byte voor byte reproduceert, samen met zijn HTTP/2 SETTINGS-frame — zodat zijn JA3, JA3N en Akamai hashes overeenkomen met echte Chrome in plaats van een scriptingbibliotheek. Anti-bot leveranciers houden databases bij van deze handtekeningen; een mismatch tussen een Chrome User-Agent header en een Python-handshake is een contradictie waar je niet onderuit kunt praten. We hebben de mechanica uitgepakt in JA3 en JA4 fingerprinting, en hetzelfde effect verklaart waarom curl een 403 krijgt waar je browser een 200 krijgt op een identieke URL.

Alles in de vergelijking volgt uit de implementatie. Omdat curl_cffi libcurl omwikkelt, erft het HTTP/2, HTTP/3, websockets en asyncio, die requests nooit heeft ondersteund. Omdat requests puur Python is, installeert het op alles en heeft het een decennium aan ecosysteem achter zich. Beide uitspraken zijn tegelijk waar, en welke domineert hangt volledig af van je doel.

Een reproduceerbare test die je in vijf minuten kunt uitvoeren

Neem iemands pass-rate tabel niet op geloof aan, inclusief de onze. Het verschil in vingerafdrukken is direct waarneembaar: richt beide clients op een TLS-echo eindpunt en vergelijk de hashes die ze terug rapporteren. Als de twee lijnen overeenkomen, imiteert je build niets.

# pip install requests curl_cffi
import requests
import curl_cffi

URL = "https://tls.browserleaks.com/json"

a = requests.get(URL, timeout=30).json()
b = curl_cffi.get(URL, impersonate="chrome", timeout=30).json()

print("requests   ja3n:", a["ja3n_hash"], "| akamai:", a.get("akamai_hash", "-"))
print("curl_cffi  ja3n:", b["ja3n_hash"], "| akamai:", b.get("akamai_hash", "-"))

# Two different ja3n hashes = impersonation is working. The curl_cffi README
# documents aa56c057ad164ec4fdcb7a5a283be9fc as a Chrome-matched ja3n value.
# The akamai (HTTP/2) fingerprint is usually empty for requests: it is
# HTTP/1.1 only, so there is no SETTINGS frame to fingerprint in the first place.

Sinds v0.15 is er een one-liner versie van dezelfde controle: curl-cffi get tls.browserleaks.com/json --impersonate chrome. Voer het uit voordat je iets anders debugt — het scheidt "mijn imitatie is verkeerd geconfigureerd" van "mijn imitatie is in orde en iets anders blokkeert me", wat twee totaal verschillende middagen zijn.

Zij-aan-zij vergelijking van requests en curl_cffi op het gebied van protocolondersteuning, gelijktijdigheid, vingerafdrukken en draagbaarheid
requests wint op draagbaarheid en ecosysteem; curl_cffi wint op protocollen en vingerafdrukken. Geen van beide wint op IP-kwaliteit — dat is geen clientfunctie.

Wat curl_cffi niet oplost: IP-reputatie

Hier is het deel dat het advies om van bibliotheek te wisselen weglaat. Een TLS-vingerafdruk beantwoordt de vraag "welke software is dit?". Het zegt niets over "waar komt dit vandaan?" — en die tweede vraag wordt beantwoord door een aparte lookup tegen je exit-IP: welke ASN bezit het, is het een hostingprovider of een consumenten-ISP, is het verschenen in abuse feeds, hoeveel andere sessies hebben deze site geraakt vanaf hetzelfde adres in het afgelopen uur. Een vlekkeloze Chrome-handshake afkomstig van een cloud-VM in een datacenterrange is een Chrome-browser die blijkbaar is geïnstalleerd in een serverrack. Dat is niet overtuigender dan python-requests. In sommige gevallen is het minder, omdat de contradictie scherper is.

De FAQ van het project zet IP-kwaliteit bovenaan in zijn lijst van factoren, vóór aanvraagfrequentie en JavaScript-vingerafdrukken, bij het uitleggen waarom alleen imitatie misschien niet genoeg is. Die volgorde is geen toeval: reputatie is het goedkoopste signaal voor een verdediger om te evalueren en het moeilijkst voor een aanvaller om te vervalsen, omdat je het, in tegenstelling tot een header of een cipherlijst, niet lokaal kunt genereren. Als je op requests gebaseerde scraper al door een datacenterpool liep en werd geblokkeerd, verandert het overschakelen naar curl_cffi op dezelfde pool één van de twee falende controles. Je zult gedeeltelijke verbetering zien op zachte doelen en helemaal geen verbetering op harde — wat precies het verwarde resultaat is dat mensen rapporteren.

De oplossing voor die as is adreskwaliteit, niet code: residential IPs van echte consumenten-ISP-allocaties, wat je 90M+ adressen in meer dan 200 landen oplevert. Als je wilt controleren hoe je huidige exit eruitziet voordat je iets verandert, rapporteert onze gratis IP-kwaliteitschecker de ASN en classificatie die een doelwit zou zien.

De 2x2 die je vertelt welke as kapot is

In plaats van te raden, test beide variabelen onafhankelijk tegen je echte doel. Vier aanvragen, vier regels uitvoer, en het resultaat benoemt je probleem:

import requests
import curl_cffi

TARGET = "https://your-target.example/api/items"
DC  = "http://USER:PASS@your-datacenter-gateway:PORT"
RES = "http://USER:PASS@gate.quantumproxies.io:PORT"

def probe(label, fn):
    try:
        print(f"{label:26} -> {fn().status_code}")
    except Exception as e:
        print(f"{label:26} -> {type(e).__name__}")

probe("requests  + datacenter",
      lambda: requests.get(TARGET, proxies={"https": DC}, timeout=30))
probe("curl_cffi + datacenter",
      lambda: curl_cffi.get(TARGET, proxy=DC, impersonate="chrome", timeout=30))
probe("requests  + residential",
      lambda: requests.get(TARGET, proxies={"https": RES}, timeout=30))
probe("curl_cffi + residential",
      lambda: curl_cffi.get(TARGET, proxy=RES, impersonate="chrome", timeout=30))

Lees de vier resultaten als een waarheidstabel:

Voer het een paar dozijn keer uit in plaats van één keer. Beide blokkeringslagen zijn probabilistisch, en een enkele 200 vertelt je bijna niets.

Test de residential rij met echte huishoudelijke IP's

Diagram dat een browser-gematchte TLS-handshake van een datacenter-IP laat zien die wordt geblokkeerd terwijl dezelfde handshake van een residential IP slaagt
Imitatie en IP-reputatie zijn afzonderlijke poorten. Het ene oplossen en het andere laten is waarom 'ik schakelde over naar curl_cffi en er veranderde niets' zo'n veelvoorkomend rapport is.

Wanneer de extra afhankelijkheid het niet waard is

Openhartigheid is goedkoper dan een herschrijving. Blijf bij requests wanneer:

En er is een middenweg die de meeste mensen missen: je hoeft requests niet te verlaten om de handshake te krijgen. De maintainers wijzen naar curl-adapter, die curl_cffi monteert als een requests transportadapter, en httpx-curl-cffi op PyPI, die hetzelfde doet voor httpx. Je behoudt je bestaande code en ecosysteem, en alleen de bytes op de draad veranderen.

Migratieproblemen die het waard zijn om eerst te weten

De API is dichtbij genoeg dat de meeste scripts draaien na het wijzigen van de import, maar de compatibiliteitspagina vermeldt echte verschillen en het loont om ze te lezen voordat je een grote port uitvoert. Redirect response bodies worden niet behouden in Response.history. Cookies met lege domeinen kunnen verloren gaan bij redirects. Streaming response objecten kunnen niet worden gepickeld, hoewel normale responses dat wel kunnen. En er zijn helemaal geen transports of adapters, omdat de bibliotheek bewust is gelast aan libcurl-impersonate. Proxyconfiguratie verschilt ook op een kleine manier die mensen in de war brengt:

# requests: dict, and retries mounted on an adapter
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

s = requests.Session()
s.mount("https://", HTTPAdapter(max_retries=Retry(total=4, status_forcelist=[429, 503])))
r = s.get(url, proxies={"https": "http://USER:PASS@gate.quantumproxies.io:PORT"}, timeout=30)

# curl_cffi: single proxy string preferred, retries are a session parameter
from curl_cffi import Session
from curl_cffi.requests import RetryStrategy

s = Session(
    impersonate="chrome",
    proxy="http://USER:PASS@gate.quantumproxies.io:PORT",
    retry=RetryStrategy(count=4, delay=1.0, backoff="exponential"),
    timeout=30,
)
r = s.get(url)  # note: retry fires on transport errors, not on 429/503

Het volledige proxyooppervlak — dict keys, proxy_auth, per-aanvraag rotatie, async en het https:// voorvoegsel dat een onbehulpzaam WRONG_VERSION_NUMBER fout oplevert — wordt stap voor stap behandeld in onze curl_cffi proxy gids. Als je blijft, is de equivalente referentie voor de andere kant onze Python Requests proxy gids.

Veelgestelde vragen

Is curl_cffi sneller dan requests?

Ja, en de benchmarks van het project plaatsen het op gelijke voet met aiohttp en pycurl in plaats van met requests. De winst komt van libcurl die het werk in C doet plus HTTP/2 multiplexing, niet van slimme Python. Voor een handvol opeenvolgende oproepen is het verschil onzichtbaar; bij hoge gelijktijdigheid, vooral met async, is het aanzienlijk.

Is curl_cffi veilig in gebruik?

Het is MIT-gelicentieerd, wijdverbreid ingezet en levert vooraf gecompileerde wielen, dus er is geen build-stap om te auditen. Eén waarschuwing is de moeite waard om op te letten: een v0.15.0 advies dekt redirect-gebaseerde SSRF. Als je URL's ophaalt die door anderen worden geleverd, stel allow_redirects="safe" in of schakel redirects uit. Het imiteren van een browser is een technische maatregel, geen toestemming om de voorwaarden van een site te negeren.

Omzeilt curl_cffi Cloudflare?

Het verwijdert de TLS en HTTP/2 vingerafdrukken, wat basisbeschermingsniveaus vrijmaakt. Het kan geen JavaScript-uitdaging uitvoeren, Turnstile oplossen, of een gemarkeerde exit-IP repareren. De maintainers zeggen dit ook in hun FAQ, en raden een betere proxypool plus browserautomatisering aan voor de hogere niveaus.

curl_cffi vs httpx of tls_client — welke moet ik gebruiken?

httpx geeft je HTTP/2 en async maar geen vingerafdrukimitatie, dus het landt tussen requests en curl_cffi op stealth. tls_client spoofs ook TLS-profielen en benchmarks vergelijkbaar; curl_cffi heeft de grotere gemeenschap en voegt HTTP/3 en websockets toe. Als httpx al in je stack zit, krijg je met de httpx-curl-cffi transport de imitatie zonder een herschrijving.

De korte versie: schakel over naar curl_cffi wanneer je doel handshakes leest, blijf bij requests wanneer dat niet het geval is, en verwacht nooit dat een van beide keuzes een datacenter-IP zal witwassen. De clients verschillen op één as, de proxies op een andere, en geblokkeerde scrapers zijn bijna altijd een verhaal over beide. Voer de vier probes uit, lees de waarheidstabel, en repareer de as waar de gegevens op wijzen in plaats van degene waar het internet over schreeuwde.

Repareer de as die imitatie niet kan bereiken