curl_cffi vs requests: Co naprawia, a czego nie może

Zamiana requests na curl_cffi zamienia wiele 403 na 200. Nie robi to jednak nic dla zablokowanego IP. Oto granica między nimi i piętnastominutowy test, który pokaże, po której stronie leży Twój problem.

Pytanie curl_cffi vs requests zazwyczaj pojawia się w trakcie incydentu: scraper, który działał przez miesiące, zaczyna zwracać 403 przy pierwszym wywołaniu, ktoś na Reddit mówi, żeby zamienić klienta, i to działa. To jest prawdziwy efekt z prawdziwym wyjaśnieniem — ale sposób, w jaki jest powtarzany ("po prostu użyj curl_cffi") ukrywa zarówno to, co się dzieje, jak i gdzie przestaje pomagać. requests nie jest wolne ani źle napisane. Ma dokładnie jedną wadę w kontekście scrapingu, jest ona duża i nie ma nic wspólnego z API, które wpisujesz. To jest miejsce, gdzie te dwa klienty naprawdę się różnią, co się zmienia, gdy przełączysz, i jedna rzecz, której impersonation nigdy nie naprawi, niezależnie od wybranej biblioteki.

Jedyna różnica, która ma znaczenie

Obie biblioteki wysyłają te same nagłówki. Różnica jest o poziom niżej, w handshake TLS, który otwiera połączenie, zanim przesunie się choćby jeden bajt HTTP. requests opiera się na urllib3 i OpenSSL, które reklamują listę szyfrów, zestaw rozszerzeń i kolejność, które należą do Pythona i niczego innego. curl_cffi jest powiązaniem z poprawioną wersją curl, która odtwarza ClientHello przeglądarki bajt po bajcie, wraz z jej ramką HTTP/2 SETTINGS — więc jej hasze JA3, JA3N i Akamai pasują do prawdziwego Chrome, a nie do biblioteki skryptów. Dostawcy anty-botów prowadzą bazy danych tych sygnatur; niezgodność między nagłówkiem User-Agent Chrome a handshake Pythona to sprzeczność, której nie można zignorować. Rozpakowaliśmy mechanikę w JA3 i JA4 fingerprinting, a ten sam efekt wyjaśnia, dlaczego curl dostaje 403, gdzie Twoja przeglądarka dostaje 200 na identycznym URL.

Wszystko inne w porównaniu wynika z implementacji. Ponieważ curl_cffi opiera się na libcurl, dziedziczy HTTP/2, HTTP/3, websockets i asyncio, z których żadne requests nigdy nie obsługiwało. Ponieważ requests jest czystym Pythonem, instaluje się na wszystkim i ma za sobą dekadę ekosystemu. Oba stwierdzenia są prawdziwe jednocześnie, a które z nich dominuje, zależy całkowicie od Twojego celu.

Powtarzalny test, który możesz przeprowadzić w pięć minut

Nie ufaj tabeli pass-rate nikogo, w tym naszej. Różnica w fingerprint jest bezpośrednio obserwowalna: skieruj oba klienty na endpoint TLS-echo i porównaj hasze, które zwracają. Jeśli dwie linie się zgadzają, Twoja kompilacja niczego nie impersonuje.

# 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.

Od wersji v0.15 istnieje jednolinijkowa wersja tego samego sprawdzenia: curl-cffi get tls.browserleaks.com/json --impersonate chrome. Uruchom to, zanim zaczniesz debugować cokolwiek innego — oddziela "moja impersonacja jest źle skonfigurowana" od "moja impersonacja jest w porządku, a coś innego mnie blokuje", co są dwie zupełnie różne popołudnia.

Porównanie side-by-side requests i curl_cffi pod względem wsparcia protokołów, współbieżności, fingerprintingu i przenośności
requests wygrywa pod względem przenośności i ekosystemu; curl_cffi wygrywa pod względem protokołów i fingerprintów. Żaden z nich nie wygrywa pod względem jakości IP — to nie jest cecha klienta.

Czego curl_cffi nie naprawia: reputacja IP

Oto część, którą pomija rada o zamianie biblioteki. Fingerprint TLS odpowiada na pytanie "jakie to oprogramowanie?". Nie mówi nic o "skąd to pochodzi?" — a na to drugie pytanie odpowiada osobne wyszukiwanie względem Twojego wyjściowego IP: który ASN je posiada, czy to dostawca hostingu czy konsumencki ISP, czy pojawiło się w kanałach nadużyć, ile innych sesji uderzyło w tę stronę z tego samego adresu w ciągu ostatniej godziny. Nienaganny handshake Chrome przychodzący z VM w chmurze w zakresie centrum danych to przeglądarka Chrome, która najwyraźniej została zainstalowana w szafie serwerowej. To nie jest bardziej przekonujące niż python-requests. W niektórych przypadkach jest mniej, ponieważ sprzeczność jest ostrzejsza.

FAQ projektu stawia jakość IP na pierwszym miejscu na liście czynników, przed szybkością żądań i fingerprintami JavaScript, wyjaśniając, dlaczego samo impersonation może nie wystarczyć. Ta kolejność nie jest przypadkowa: reputacja to najtańszy sygnał do oceny dla obrońcy i najtrudniejszy do podrobienia dla atakującego, ponieważ w przeciwieństwie do nagłówka czy listy szyfrów nie można jej wygenerować lokalnie. Jeśli Twój scraper oparty na requests już działał przez pulę centrum danych i był blokowany, przejście na curl_cffi na tej samej puli zmienia jeden z dwóch nieudanych testów. Zobaczysz częściową poprawę na miękkich celach i żadną poprawę na twardych — co jest dokładnie mylącym wynikiem, o którym ludzie mówią.

Naprawa dla tej osi to jakość adresu, a nie kod: IP rezydencyjne z prawdziwych alokacji konsumenckich ISP, co daje ponad 90 milionów adresów w ponad 200 krajach. Jeśli chcesz sprawdzić, jak wygląda Twoje obecne wyjście przed zmianą czegokolwiek, nasz darmowy sprawdzacz jakości IP raportuje ASN i klasyfikację, którą zobaczy cel.

2x2, które mówi, która oś jest zepsuta

Zamiast zgadywać, przetestuj obie zmienne niezależnie na swoim prawdziwym celu. Cztery żądania, cztery linie wyjściowe, a wynik nazwie Twój problem:

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

Odczytaj cztery wyniki jak tabelę prawdy:

Uruchom to kilkadziesiąt razy zamiast raz. Obie warstwy blokujące są probabilistyczne, a pojedyncze 200 mówi Ci prawie nic.

Przetestuj wiersz rezydencyjny z prawdziwymi IP domowymi

Diagram pokazujący handshake TLS dopasowany do przeglądarki z IP centrum danych, który jest blokowany, podczas gdy ten sam handshake z IP rezydencyjnego przechodzi
Impersonation i reputacja IP to oddzielne bramy. Naprawienie jednej i pozostawienie drugiej to powód, dla którego "przeszedłem na curl_cffi i nic się nie zmieniło" jest tak powszechnym raportem.

Kiedy dodatkowa zależność nie jest tego warta

Szczerość jest tańsza niż przepisanie. Pozostań przy requests, gdy:

I jest środkowa ścieżka, którą większość ludzi pomija: nie musisz porzucać requests, aby uzyskać handshake. Utrzymujący wskazują na curl-adapter, który montuje curl_cffi jako adapter transportowy requests, i httpx-curl-cffi na PyPI, który robi to samo dla httpx. Zachowujesz swój istniejący kod i ekosystem, a zmieniają się tylko bajty na przewodzie.

Pułapki migracji, które warto znać najpierw

API jest na tyle podobne, że większość skryptów działa po zmianie importu, ale strona zgodności wymienia rzeczywiste różnice i warto je przeczytać przed dużym portem. Ciała odpowiedzi przekierowania nie są zachowywane w Response.history. Ciasteczka z pustymi domenami mogą zostać utracone przy przekierowaniach. Obiekty odpowiedzi strumieniowej nie mogą być serializowane, chociaż normalne odpowiedzi mogą. API plików różni się nieznacznie. I nie ma żadnych transportów ani adapterów, ponieważ biblioteka jest celowo zespawana z libcurl-impersonate. Konfiguracja proxy również różni się w mały sposób, który sprawia, że ludzie się potykają:

# 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

Pełna powierzchnia proxy — klucze słownika, proxy_auth, rotacja na żądanie, async i prefiks https://, który powoduje niepomocny błąd WRONG_VERSION_NUMBER — jest omówiona krok po kroku w naszym przewodniku po proxy curl_cffi. Jeśli pozostajesz na miejscu, równoważne odniesienie dla drugiej strony to nasz przewodnik po proxy Python Requests.

Często zadawane pytania

Czy curl_cffi jest szybsze niż requests?

Tak, a benchmarki projektu stawiają go na równi z aiohttp i pycurl, a nie z requests. Zysk pochodzi z tego, że libcurl wykonuje pracę w C plus multipleksowanie HTTP/2, a nie z pomysłowego Pythona. Dla kilku sekwencyjnych wywołań różnica jest niewidoczna; przy wysokiej współbieżności, zwłaszcza z async, jest znacząca.

Czy curl_cffi jest bezpieczne do użycia?

Jest licencjonowane na MIT, szeroko stosowane i dostarcza wstępnie skompilowane koła, więc nie ma etapu budowania do audytu. Jedna uwaga jest warta uwzględnienia: doradztwo v0.15.0 obejmuje SSRF oparte na przekierowaniach. Jeśli pobierasz URL-e dostarczone przez inne osoby, ustaw allow_redirects="safe" lub wyłącz przekierowania. Impersonowanie przeglądarki to środek techniczny, a nie pozwolenie na ignorowanie warunków strony.

Czy curl_cffi omija Cloudflare?

Usuwa sygnał fingerprint TLS i HTTP/2, co usuwa podstawowe poziomy ochrony. Nie może wykonać wyzwania JavaScript, rozwiązać Turnstile ani naprawić oznaczonego wyjścia IP. Utrzymujący mówią o tym w swoim FAQ i zalecają lepszą pulę proxy plus automatyzację przeglądarki dla wyższych poziomów.

curl_cffi vs httpx lub tls_client — którego powinienem użyć?

httpx daje Ci HTTP/2 i async, ale bez impersonacji fingerprint, więc plasuje się między requests a curl_cffi pod względem ukrycia. tls_client również fałszuje profile TLS i benchmarkuje podobnie; curl_cffi ma większą społeczność i dodaje HTTP/3 oraz websockets. Jeśli httpx jest już w Twoim stosie, transport httpx-curl-cffi daje Ci impersonację bez przepisywania.

Krótka wersja: przejdź na curl_cffi, gdy Twój cel odczytuje handshakes, pozostań przy requests, gdy tego nie robi, i nigdy nie oczekuj, że którykolwiek wybór wyczyści IP centrum danych. Klienci różnią się na jednej osi, proxy na innej, a zablokowane scrapery to prawie zawsze historia o obu. Uruchom cztery sondy, odczytaj tabelę prawdy i napraw oś, na którą wskazują dane, zamiast tej, o której krzyczał internet.

Napraw oś, której impersonacja nie może dosięgnąć