Cloudflare "Just a Moment" w Pythonie: Dlaczego requests zawodzi
Strona 'Just a moment...' Cloudflare'a to wyzwanie JavaScript, a Python requests nie może uruchomić JavaScript. Oto jak je wykryć, dlaczego serwery są bardziej obciążone niż laptopy, dlaczego sztuczki w stylu cloudscraper przestały wystarczać i co przeszło w naszym teście z 29 września 2026.
'Just a moment...' to strona wyzwania Cloudflare'a. Prosi klienta o uruchomienie JavaScript i udowodnienie, że jest przeglądarką, zanim strona odpowie. Python requests nie może uruchomić JavaScript, a z serwera w centrum danych jego IP i handshake TLS już wyglądają na zautomatyzowane. Co przechodzi, to prawdziwa przeglądarka na czystym IP lub web unlocker, który ją uruchamia.
Ten przewodnik pokazuje, jak wykryć wyzwanie w Pythonie, dlaczego ten sam skrypt przechodzi na laptopie, a zawodzi na serwerze, dlaczego sztuczki z ery cloudscraper przestały wystarczać i co faktycznie przeszło, gdy testowaliśmy to 29 września 2026. Zawiera to stronę, na której nic, co próbowaliśmy, nie przeszło 'Just a moment'.
- To wyzwanie, nie błąd. Cloudflare oznacza odpowiedzi z wyzwaniem nagłówkiem
cf-mitigated: challenge. W naszym teście przychodziły jako HTTP 403. - Nagłówki tego nie naprawią. 'Enable JavaScript and cookies to continue' oznacza, że strona chce silnika JavaScript. Żaden ciąg User-Agent go nie dostarcza.
- Serwer kontra laptop to głównie reputacja IP plus odcisk palca TLS. Samo IP rezydencyjne nie przeszło Indeed ani Glassdoor w naszym teście.
- Co przeszło: Web Unlocker uzyskał dostęp do Glassdoor (po przeglądarce bezgłowej, 17,2 s) i Indeed (przy pierwszej próbie, 2,6 s). StockX nadal zwracał 'Just a moment...' i został zgłoszony jako
js_challenge. - Przypnij sesję. Ponowne użycie clearance przeglądarki skróciło drugie żądanie do Glassdoor z 14,0 s do 1,4 s w naszym teście.
Czym właściwie jest strona "Just a moment..."
Cloudflare nazywa to stroną wyzwania przejściowego: pełnostronicową bramą między odwiedzającym a stroną, podczas gdy Cloudflare decyduje, czy odwiedzający jest zautomatyzowany. Jej dokumentacja dotycząca stron wyzwań opisuje trzy rodzaje. Wyzwanie nieinteraktywne wymaga jedynie, aby przeglądarka uruchomiła wstrzyknięty JavaScript. Zarządzane wyzwanie pozwala Cloudflare wybrać typ wyzwania na podstawie sygnałów z żądania. Wyzwanie interaktywne prosi odwiedzającego o wykonanie czegoś. Gdy kontrola przejdzie, oryginalne żądanie kontynuuje do strony, a przeglądarka otrzymuje ciasteczko cf_clearance, które pozwala ominąć bramę na jakiś czas.
Klient HTTP w Pythonie nigdy nie dochodzi tak daleko. Pobiera stronę wyzwania, nie ma silnika do uruchomienia skryptu i zamiast strony, o którą prosiłeś, zwraca ci HTML bramy.
Dlaczego wyzwanie wraca jako 403 i jak je wykryć
W naszym teście każda strona wyzwania przychodziła ze statusem 403, co jest powodem, dla którego raise_for_status() wyrzuca 403 Client Error: Forbidden i ludzie szukają 'cloudflare just a moment 403'. Sam status nie może odróżnić wyzwania od jakiejkolwiek innej odmowy. Wiarygodnym sygnałem jest nagłówek: Cloudflare dokumentuje, że odpowiedź z wyzwaniem zawiera cf-mitigated: challenge, niezależnie od zasobu, o który prosiłeś. Sprawdź to najpierw, a potem tytuł jako rezerwę:
import re
import requests
URL = "https://www.glassdoor.com/Reviews/Google-Reviews-E9079.htm"
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}
def title_of(html: str) -> str:
m = re.search(r"<title[^>]*>(.*?)</title>", html, re.I | re.S)
return m.group(1).strip() if m else ""
def is_cloudflare_challenge(r: requests.Response) -> bool:
if r.headers.get("cf-mitigated") == "challenge": # documented by Cloudflare
return True
if "just a moment" in title_of(r.text).lower():
return True
return "enable javascript and cookies to continue" in r.text[:20000].lower()
r = requests.get(URL, headers=HEADERS, timeout=30)
print(r.status_code, is_cloudflare_challenge(r), title_of(r.text))
# 29 Sep 2026, plain request through a residential proxy (US exit):
# 403, body asking to 'Enable JavaScript and cookies to continue'
Nie próbuj ponownie żądania z wyzwaniem w ciasnej pętli z tym samym klientem. Każda próba pobiera tę samą bramę, a niektóre strony odpowiadają na powtarzające się niepowodzenia surowszymi zasadami. Tak właśnie scraper kończy w pętli 'Just a moment'.
"Enable JavaScript and cookies to continue" i "your browser does not support JavaScript"
Te ciągi to tekst rezerwowy strony wyzwania, pokazywany klientom, którzy nie wykonują jej skryptu. Gdy Python requests pokazuje ci jeden z nich, strona nie narzeka na twoje nagłówki. Mówi ci dosłownie, że potrzebuje silnika JavaScript i słoika na ciasteczka, który zachowa to, co ustawia skrypt.
To dokładnie to, co nasz zwykły rezydencyjny bieg otrzymał od Indeed i Glassdoor: 403 z 'Enable JavaScript and cookies to continue', z czystego domowego IP. Dodanie User-Agent Chrome, nagłówka Accept-Language lub Referer nic nie zmienia, ponieważ strona ich nie czyta, aby zdecydować. Czeka, aż skrypt się uruchomi.
Dlaczego twój scraper działa lokalnie, ale jest blokowany przez Cloudflare na serwerze
To najczęstsza wersja skargi: skrypt działa na laptopie, a potem otrzymuje 'Just a moment', gdy tylko uruchomi się na chmurze VM. Dwie rzeczy zmieniają się między tymi maszynami.
- IP. Na twoim laptopie skrypt wychodzi z adresu ISP domowego. Na VM wychodzi z zakresu dostawcy hostingu, który obrony przed botami oceniają jako prawdopodobnie zautomatyzowany, zanim cokolwiek innego przeczytają. Nasz serwer w centrum danych we Frankfurcie został odrzucony przez Glassdoor, Indeed, Zillow i Leboncoin, między innymi.
- Odcisk palca TLS. Handshake Pythona wygląda jak Python na obu maszynach, ale na domowym IP strona może go tolerować, a na IP hostingu rzadko to zrobi. Nasze wyjaśnienie na temat curl_cffi vs requests pokazuje, jak wiele z handshake kontroluje biblioteka.
Kolumna rezydencyjna naszego testu pokazuje limit naprawy tylko IP. Zillow i Leboncoin obsłużyły prawdziwą stronę na zwykłe żądanie przez rezydencyjny proxy. Indeed i Glassdoor nadal odpowiadały wyzwaniem JavaScript. Twój laptop może trafić na tę samą ścianę na bardziej rygorystycznej stronie. Po prostu trafia na nią rzadziej.

Dlaczego sztuczki z ery cloudscraper przestają działać
cloudscraper został zbudowany dla starszej strony 'I'm Under Attack' Cloudflare'a. Ta strona głównie sprawdzała, czy klient może wykonać obliczenie JavaScript, więc biblioteka pobierała wyzwanie, rozwiązywała skrypt w wbudowanym interpreterze, wysyłała odpowiedź z powrotem i zachowywała ciasteczko, wszystko na wierzchu requests. Przez długi czas to wystarczało i to jest powód, dla którego 'cloudscraper alternative' jest teraz wyszukiwaniem, które ludzie wykonują.
Od tego czasu wiele się zmieniło i nie zależy to od żadnej wersji biblioteki:
- Handshake nadal mówi Python. Biblioteka zbudowana na requests łączy się przez stos TLS Pythona, więc ClientHello nie pasuje do przeglądarki, którą twierdzi, że jest jej User-Agent. Ta niezgodność jest widoczna, zanim jakikolwiek JavaScript się uruchomi.
- Wyzwanie to już nie tylko arytmetyka. Zarządzane wyzwania wybierają kontrolę z sygnałów żądania, a detekcje JavaScript Cloudflare'a czytają środowisko przeglądarki. Interpreter, który ocenia skrypt, nie jest przeglądarką, która renderuje stronę.
- Clearance jest przypisane do klienta. Dokumentacja clearance Cloudflare'a mówi, że ciasteczko cf_clearance jest związane z odwiedzającym i urządzeniem, na które zostało wydane, aby zapobiec ponownemu użyciu na różnych maszynach. Skopiowanie go z przeglądarki do skryptu na innej maszynie jest zaprojektowane, aby się nie powieść.
- IP nadal jest twoje. Rozwiązanie strony nie zmienia IP z zakresu hostingu, a niektóre strony odrzucają je, zanim jakiekolwiek wyzwanie zostanie podane.
Na dzień 29 września 2026, ostatnie przesłanie do repozytorium cloudscraper miało miejsce w czerwcu 2025. To nie jest werdykt na temat żadnej konkretnej strony, i nie przeprowadziliśmy benchmarku dla tego artykułu. Oznacza to jednak, że powinieneś testować go na swoich własnych celach, zamiast zakładać, że działa. Alternatywy rozwiązują każdą część problemu. curl_cffi odtwarza handshake TLS przeglądarki, ale nie uruchamia JavaScript. Playwright lub inna prawdziwa przeglądarka uruchamia wyzwanie, ale nadal potrzebuje czystego IP i jest ciężka do obsługi na dużą skalę. Unlocker łączy oba i informuje cię, gdy żadne z nich nie działało.
Co przechodzi: ścieżka web unlocker
QuantumProxies.io Web Unlocker obsługuje GET w etapach. Najpierw do trzech prób TLS, każda na świeżym wyjściu rezydencyjnym pod innym odciskiem palca przeglądarki. Jeśli każda próba wraca z wyzwaniem, eskaluje do przeglądarki bezgłowej, która uruchamia wyzwanie. Jeśli strona jest nadal zablokowana po tym, otrzymujesz status błędu z x-qp-unlocker-blocked i klasą blokady, nigdy stronę wyzwania przebrane za 200. Z Pythona to ustawienie proxy:
import os
import time
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
user = os.environ["QP_UNLOCK_USER"] + "-country-us" # unlock-XXXXXXXX-country-us
PROXY = f"http://{user}:{os.environ['QP_UNLOCK_PASSWORD']}@unlock.quantumproxies.io:9000"
s = requests.Session()
s.proxies = {"http": PROXY, "https": PROXY}
s.verify = False # or the unlocker CA file from /api/v1/scraper/unlock/ca
s.headers["x-qp-session"] = "glassdoor-01" # same value on every call: same exit, reusable clearance
for attempt in (1, 2): # same page twice here; in practice, the next page
t0 = time.monotonic()
r = s.get(URL, timeout=120)
print(attempt, r.status_code, f"{time.monotonic() - t0:.1f}s",
"rendered:", r.headers.get("x-qp-unlocker-rendered"),
"clearance:", r.headers.get("x-qp-clearance"),
"block:", r.headers.get("x-qp-block-class"))
Nagłówki x-qp-* są odczytywane przez unlocker i nigdy nie są przekazywane do strony. Jeśli wolisz w ogóle nie zajmować się certyfikatem, punkt końcowy REST POST https://app.quantumproxies.io/api/v1/scraper/unlock przyjmuje URL w ciele JSON z twoim kluczem API i zwraca blocked, blockClass i vendor w odpowiedzi. Nasz samouczek API web unlocker przeprowadza przez oba.
Zmierzono: dwie ściany w stylu Cloudflare, które przeszły, jedna, która nie
Zmierzono 29 września 2026. Zwykłe żądania były curl z User-Agent Chrome, z serwera w centrum danych we Frankfurcie i przez proxy rezydencyjne z wyjściem w USA. Unlocker używał poziomu rezydencyjnego z wyjściem w USA. Czas to czas ścienny dla jednego żądania.
| Strona | Zwykłe, serwer w centrum danych | Zwykłe, proxy rezydencyjne | Web Unlocker |
|---|---|---|---|
| Glassdoor (recenzje firm) | 403, strona 'Security' | 403, 'Enable JavaScript and cookies to continue' | 200, 2 próby + przeglądarka bezgłowa, 17,2 s |
| Indeed (wyszukiwanie pracy) | 403, strona 'Security Check' | 403, 'Enable JavaScript and cookies to continue' | 200, 1 próba, bez przeglądarki, 2,6 s |
| StockX (strona produktu) | 403 | 403 | 403 'Just a moment...', x-qp-block-class js_challenge, vendor cloudflare, 47,8 s |
| DoorDash (strona miasta) | 403 | 403 | 403, x-qp-block-class ip_reputation, vendor cloudflare |
Glassdoor to pełna ścieżka. Dwie próby TLS były wyzwaniem, potem przeglądarka bezgłowa uruchomiła wyzwanie i zwróciła stronę z recenzjami. Indeed to interesujący przypadek: wyzwanie, które otrzymało zwykłe rezydencyjne żądanie, nigdy nie pojawiło się przy pierwszej próbie unlockera. Odcisk palca TLS przeglądarki na świeżym wyjściu rezydencyjnym wystarczył, bez przeglądarki.
StockX to uczciwa porażka. Unlocker eskalował, spędził 47,8 sekundy, a strona nadal była 'Just a moment...'. Wróciła jako 403 z x-qp-block-class: js_challenge i x-qp-vendor: cloudflare, a nie jako 200, które twój parser próbowałby odczytać. DoorDash, również za Cloudflare, został odrzucony na IP (ip_reputation) zamiast wyzwany. Niektóre konfiguracje Cloudflare nie otwierają się na żadne z narzędzi w tym artykule i lepiej wiedzieć to z nagłówka niż z pustego zestawu danych.
Przejdź przez wyzwania JavaScript z Web Unlocker
Ponowne użycie clearance: dlaczego drugie żądanie jest szybkie
Eskalacja przeglądarki to wolna część i nie chcesz za nią płacić na każdej stronie. Gdy przeglądarka bezgłowa przechodzi wyzwanie, zdobywa clearance. Wyślij tę samą wartość x-qp-session, lub zachowaj ten sam -session-<id> w nazwie użytkownika proxy, przy kolejnych żądaniach i wychodzą one z tego samego wyjścia, ponownie używając tego clearance zamiast ponownie uruchamiać przeglądarkę. Nagłówek x-qp-clearance mówi ci, co się stało przy każdym wywołaniu: minted, gdy zdobyto nowy clearance, hit, gdy ponownie użyto przechowywanego, miss, gdy nie było nic do ponownego użycia.
W teście Glassdoor 29 września 2026, z sesją przypiętą w nazwie użytkownika proxy i dwoma oddzielnymi połączeniami, pierwsze żądanie przeszło przez przeglądarkę w 14,0 sekund (minted), a drugie wróciło w 1,4 sekundy (hit). Przypięcie ma znaczenie ze względu na to, jak Cloudflare wiąże clearance: jest związane z odwiedzającym i urządzeniem, które je zdobyło, i pozostaje ważne przez czas, który strona konfiguruje, chyba że Cloudflare uzna, że sesja wygląda podejrzanie. Zmień wyjście lub odcisk palca, a jesteś nowym odwiedzającym.

Kiedy zwykłe requests nadal działa na stronie Cloudflare
Wiele stron działa za Cloudflare tylko jako CDN, lub z zasadami botów, które przepuszczają zwykły ruch, i zwykłe requests działa na nich. Bycie 'na Cloudflare' nie jest powodem, aby sięgać po przeglądarkę. Sygnałem jest odpowiedź. Jeśli nie ma nagłówka cf-mitigated: challenge, tytułu 'Just a moment' i 403, zachowaj zwykłe żądanie i eskaluj tylko URL-e, które faktycznie są wyzwaniem. Pomocnik plain-first w naszym samouczku robi dokładnie to.
Dwa sąsiednie problemy mają własne przewodniki. Strona, która mówi 'Error 1020 Access denied', to reguła zapory odmawiająca ci, a nie wyzwanie, i żadna ilość JavaScript tego nie naprawi; zobacz co wywołuje błąd Cloudflare 1020 i co go naprawia. Dla szerszego obrazu obron Cloudflare poza Pythonem, przeczytaj jak ominąć wyzwania Cloudflare w 2026. Jeśli strona za ścianą to Glassdoor, nasz przewodnik po skrobaniu wynagrodzeń i recenzji Glassdoor obejmuje stronę danych.
Często zadawane pytania
Dlaczego mój scraper działa lokalnie, ale jest blokowany przez Cloudflare w chmurze?
Ponieważ IP się zmienia. W domu twój skrypt wychodzi z adresu ISP rezydencyjnego; na chmurze VM wychodzi z zakresu hostingu, który obrony przed botami nie ufają na pierwszy rzut oka. Handshake TLS Pythona jest taki sam na obu, a na bardziej rygorystycznej stronie jest wyzwaniem nawet z domu. W naszym teście samo IP rezydencyjne nie przeszło Indeed ani Glassdoor.
Dlaczego strona "Just a moment" Cloudflare'a zwraca 403?
Strona wyzwania jest serwowana zamiast strony, o którą prosiłeś, i w naszym teście przychodziła ze statusem 403, więc requests podnosi '403 Client Error: Forbidden'. Status nie jest specyficzny dla wyzwań. Sprawdź nagłówek odpowiedzi cf-mitigated, który Cloudflare dokumentuje jako ustawiony na challenge przy każdej odpowiedzi na stronę wyzwania, lub tytuł 'Just a moment'.
Czy cloudscraper nadal działa, i jaka jest dobra alternatywa?
Może nadal działać na stronach z łagodnymi ustawieniami, więc przetestuj go na swoich celach. Działa na stosie TLS Pythona i rozwiązuje skrypty w interpreterze, podczas gdy obecne wyzwania również patrzą na handshake i środowisko przeglądarki. Alternatywy: curl_cffi dla handshake przeglądarki, prawdziwa przeglądarka dla JavaScript, lub web unlocker, który robi oba.
Czy Python requests może samodzielnie przejść wyzwanie Cloudflare?
Nie. requests nie wykonuje JavaScript, więc nie może ukończyć wyzwania, które zależy od uruchomienia skryptu strony. Może pobierać strony z witryn Cloudflare, które cię nie wyzywają. Gdy pojawia się cf-mitigated: challenge, potrzebujesz prawdziwej przeglądarki, bezpośrednio lub przez usługę, która ją uruchamia, i najlepiej czystego rezydencyjnego IP.
Czy web unlocker może rozwiązać Cloudflare Turnstile lub inne captchy?
Nieinteraktywne wyzwania JavaScript są rozwiązywane przez eskalację przeglądarki bezgłowej. Wyzwania interaktywne, które wymagają działania człowieka, takie jak checkbox, suwak, przytrzymanie lub siatka obrazów, nie są rozwiązywane. Żądanie kończy się niepowodzeniem zamiast zwracania wyzwania jako 200, a punkt końcowy REST oznacza je klasą blokady captcha i dostawcą, więc twój kod może zdecydować, czy pominąć URL, zwolnić czy spróbować później.
Jak długo trwa clearance Cloudflare?
Tak długo, jak strona to konfiguruje. Cloudflare nazywa to ustawienie Challenge Passage, a clearance może zakończyć się wcześniej, jeśli Cloudflare uzna, że sesja wygląda podejrzanie. Ciasteczko cf_clearance jest związane z odwiedzającym i urządzeniem, które je zdobyło, więc pomaga tylko, jeśli późniejsze żądania zachowują to samo wyjście i odcisk palca, co robi przypięcie x-qp-session.
Krótka wersja: 'Just a moment...' prosi o przeglądarkę, więc żadna sztuczka z nagłówkami w requests tego nie zadowoli. Wykryj to z cf-mitigated, zachowaj zwykłe requests dla wszystkiego, co nie jest wyzwaniem, wyślij wyzwanie URL-e przez coś, co uruchamia prawdziwą przeglądarkę z czystego IP, i przypnij sesję, aby zapłacić za tę przeglądarkę raz, a nie na każdej stronie.