PythonでのCloudflare「Just a Moment」:requestsが失敗する理由

Cloudflareの「Just a moment...」ページはJavaScriptチャレンジであり、Python requestsはJavaScriptを実行できません。これを検出する方法、なぜサーバーではラップトップよりも厳しいのか、cloudscraperスタイルのトリックがなぜ十分でなくなったのか、そして2026年9月29日のテストで何が通過したのかを説明します。

「Just a moment...」はCloudflareのチャレンジページです。クライアントにJavaScriptを実行してブラウザであることを証明するよう求め、サイトが応答する前に行います。Python requestsはJavaScriptを実行できず、データセンターサーバーからのIPとTLSハンドシェイクはすでに自動化されたものに見えます。通過するのはクリーンなIPの実際のブラウザか、それを実行するウェブアンロッカーです。

このガイドは、Pythonでチャレンジを検出する方法、同じスクリプトがラップトップでは通過しサーバーでは失敗する理由、cloudscraper時代のトリックがなぜ十分でなくなったのか、そして2026年9月29日にテストした際に実際に通過したものを示します。試したすべてが「Just a moment」を超えられなかったサイトも含まれています。

「Just a moment...」ページの実際の内容

Cloudflareはこれをインタースティシャルチャレンジページと呼びます:訪問者とサイトの間にあるフルページのゲートで、Cloudflareが訪問者が自動化されているかどうかを判断する間にあります。チャレンジページに関するドキュメントでは3種類を説明しています。非インタラクティブチャレンジはブラウザが注入されたJavaScriptを実行するだけで済みます。管理されたチャレンジはリクエストのシグナルからCloudflareがチャレンジタイプを選びます。インタラクティブチャレンジは訪問者に何かをさせます。チェックが通過すると、元のリクエストがサイトに進み、ブラウザはしばらくの間ゲートをスキップできるcf_clearanceクッキーを取得します。

Python HTTPクライアントはそこまで進みません。チャレンジページをダウンロードし、スクリプトを実行するエンジンがなく、要求したページの代わりにゲートのHTMLを渡します。

なぜチャレンジが403として返されるのか、そしてそれを検出する方法

我々のテストでは、すべてのチャレンジページがステータス403で到着し、raise_for_status()が403 Client Error: Forbiddenを投げる理由です。ステータスだけでは他の拒否とチャレンジを区別できません。信頼できるシグナルはヘッダーです:Cloudflareはチャレンジされた応答がcf-mitigated: challengeを持つとドキュメント化しています、要求したリソースに関わらず。まずそれを確認し、次にタイトルをフォールバックとして使用します:

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'

同じクライアントでチャレンジされたリクエストをタイトなループで再試行しないでください。各再試行は同じゲートを取得し、一部のサイトは繰り返しの失敗に対してより厳しいルールで応答します。これがスクレイパーが「Just a moment」ループにはまる方法です。

「Enable JavaScript and cookies to continue」と「your browser does not support JavaScript」

これらの文字列はチャレンジページのフォールバックテキストであり、そのスクリプトを実行しないクライアントに表示されます。Python requestsがこれらのいずれかを表示するとき、サイトはあなたのヘッダーについて不満を言っているのではなく、文字通りJavaScriptエンジンとスクリプトが設定するものを保持するクッキージャーが必要であることを伝えています。

これはまさに我々のプレーンな住宅実行がIndeedとGlassdoorから受け取ったものです:クリーンなホームIPからの403で「Enable JavaScript and cookies to continue」。Chrome User-Agent、Accept-Languageヘッダー、またはRefererを追加しても何も変わりません。ページはそれらを読んで決定しているのではなく、スクリプトが実行されるのを待っています。

なぜあなたのスクレイパーがローカルでは動作するのにサーバーではCloudflareにブロックされるのか

これは最も一般的な不満のバージョンです:スクリプトはラップトップで動作し、クラウドVMで実行するとすぐに「Just a moment」を受け取ります。これらのマシン間で2つのことが変わります。

我々のテストの住宅列は、IPだけを修正する限界を示しています。ZillowとLeboncoinは住宅プロキシを通じたプレーンなリクエストに実際のページを提供しました。IndeedとGlassdoorは依然としてJavaScriptチャレンジで応答しました。あなたのラップトップも厳しいサイトで同じ壁にぶつかることがあります。ただし、それが起こる頻度は少ないです。

なぜPythonスクレイパーがラップトップからCloudflareを通過し、クラウドサーバーから「Just a moment」チャレンジを受けるのか:ホームIP対ホスティングIP、同じPython TLSフィンガープリント、どちらの場合もJavaScriptエンジンがない
同じスクリプト、2つのマシン。IPが変わりますが、Python TLSハンドシェイクと欠けているJavaScriptエンジンは変わりません。

なぜcloudscraper時代のトリックが効かなくなるのか

cloudscraperはCloudflareの古い「I'm Under Attack」ページ用に作られました。そのページは主にクライアントがJavaScript計算を実行できるかどうかを確認していたので、ライブラリはチャレンジを取得し、埋め込まれたインタープリタでスクリプトを解決し、答えを送り返し、クッキーを保持しました。長い間それで十分でしたが、それが「cloudscraper alternative」が今検索される理由です。

それ以来、いくつかのことが変わり、それらは特定のライブラリバージョンに依存しません:

2026年9月29日現在、cloudscraperリポジトリの最後のプッシュは2025年6月でした。これは特定のサイトに対する判決ではなく、この記事のためにベンチマークを行っていません。それは、動作することを前提とせずに自分のターゲットに対してテストするべきであることを意味します。各代替案は問題の一部を修正します。curl_cffiはブラウザのTLSハンドシェイクを再現しますが、JavaScriptを実行しません。Playwrightや他の実際のブラウザはチャレンジを実行しますが、依然としてクリーンなIPが必要で、大量に操作するには重いです。アンロッカーはそれらを組み合わせ、どちらも機能しなかったときに知らせます。

通過するもの:ウェブアンロッカーの道

QuantumProxies.io Web UnlockerはGETを段階的に処理します。まず、異なるブラウザフィンガープリントの下で新鮮な住宅出口で最大3回のTLS試行を行います。すべての試行がチャレンジされると、チャレンジを実行するヘッドレスブラウザにエスカレートします。それでもページがブロックされている場合、x-qp-unlocker-blockedとブロッククラスのエラーステータスが返され、200として装ったチャレンジページは返されません。Pythonからはプロキシ設定です:

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

x-qp-*ヘッダーはアンロッカーによって読み取られ、サイトには転送されません。証明書をまったく扱いたくない場合、RESTエンドポイントPOST https://app.quantumproxies.io/api/v1/scraper/unlockはAPIキー付きのJSONボディでURLを受け取り、blocked、blockClass、vendorをレスポンスで返します。我々のウェブアンロッカーAPIチュートリアルは両方を説明します。

測定済み:通過した2つのCloudflareスタイルの壁

2026年9月29日に測定。プレーンリクエストはChrome User-Agentを持つcurlで、フランクフルトのデータセンターサーバーと米国出口の住宅プロキシを通じて行われました。アンロッカーは米国出口の住宅ティアを使用しました。時間は1つのリクエストの壁時計です。

サイトプレーン、データセンターサーバープレーン、住宅プロキシWeb Unlocker
Glassdoor(企業レビュー)403、「セキュリティ」ページ403、「Enable JavaScript and cookies to continue」200、2回の試行+ヘッドレスブラウザ、17.2秒
Indeed(求人検索)403、「セキュリティチェック」ページ403、「Enable JavaScript and cookies to continue」200、1回の試行、ブラウザなし、2.6秒

Glassdoorはフルパスです。2回のTLS試行がチャレンジされ、その後ヘッドレスブラウザがチャレンジを実行し、レビューのページを返しました。Indeedは興味深いものです:プレーンな住宅リクエストが受け取ったチャレンジはアンロッカーの最初の試行では現れませんでした。新鮮な住宅出口でのブラウザTLSフィンガープリントだけで十分で、ブラウザは必要ありませんでした。

Web UnlockerでJavaScriptチャレンジを突破する

クリアランスを再利用する:なぜ2回目のリクエストが速いのか

ブラウザのエスカレーションが遅い部分であり、すべてのページでそれを支払いたくありません。ヘッドレスブラウザがチャレンジを通過すると、クリアランスを獲得します。同じx-qp-session値を送信するか、プロキシユーザー名に同じ-session-<id>を保持し、次のリクエストで同じ出口から出発し、再びブラウザを実行する代わりにそのクリアランスを再利用します。x-qp-clearanceヘッダーは各呼び出しで何が起こったかを教えてくれます:新しいクリアランスが獲得されたときはminted、保存されたものが再利用されたときはhit、再利用するものがなかったときはmiss。

2026年9月29日のGlassdoorテストでは、プロキシユーザー名にセッションを固定し、2つの別々の接続で、最初のリクエストは14.0秒でブラウザを通過し(minted)、2回目は1.4秒で戻ってきました(hit)。固定が重要なのは、Cloudflareがクリアランスをどのように結びつけるかによります:それはそれを獲得した訪問者とデバイスに結びついており、サイトが設定する期間有効であり、Cloudflareがセッションが疑わしいと判断しない限り有効です。出口やフィンガープリントを変更すると、再び新しい訪問者になります。

1つの固定されたアンロッカーセッションでの2つのリクエストのタイムライン:最初はヘッドレスブラウザにエスカレートし、14.0秒でCloudflareクリアランスを獲得し、2回目はそれを再利用して1.4秒で戻ります。
最初のリクエストはブラウザの費用を支払い、同じ固定されたセッション内の後続のリクエストはクリアランスを再利用します。

プレーンリクエストがCloudflareサイトでまだ有効な場合

多くのサイトはCloudflareの背後でCDNとしてのみ動作しているか、通常のトラフィックを通過させるボットルールを持っており、プレーンリクエストがそれらで機能します。「Cloudflare上にある」ことはブラウザを使用する理由ではありません。シグナルは応答です。cf-mitigated: challengeヘッダーがなく、「Just a moment」タイトルがなく、403がない場合、プレーンリクエストを維持し、実際にチャレンジされたURLのみをエスカレートします。我々のチュートリアルのプレーンファーストヘルパーはまさにそれを行います。

2つの隣接する問題にはそれぞれのガイドがあります。「Error 1020 Access denied」と表示されるページは、ファイアウォールルールがあなたを拒否しているものであり、チャレンジではなく、JavaScriptでそれを修正することはできません;Cloudflareエラー1020を引き起こすものとそれを修正するものを参照してください。Pythonを超えたCloudflareの防御の全体像については、2026年にCloudflareチャレンジを回避する方法を読んでください。壁の後ろのページがGlassdoorの場合、我々のGlassdoorの給与とレビューをスクレイピングするガイドはデータ側をカバーしています。

よくある質問

なぜ私のスクレイパーはローカルで動作するのにクラウドでCloudflareにブロックされるのですか?

IPが変わるからです。自宅ではスクリプトは住宅ISPアドレスから出発し、クラウドVMではホスティング範囲から出発し、ボット防御が一目で信頼しません。PythonのTLSハンドシェイクは両方で同じであり、厳しいサイトでは自宅からでもチャレンジされます。我々のテストでは、住宅IPだけではIndeedやGlassdoorを通過しませんでした。

なぜCloudflareの「Just a moment」ページが403を返すのですか?

チャレンジページは要求したページの代わりに提供され、我々のテストではステータス403で到着しましたので、requestsは「403 Client Error: Forbidden」を発生させます。ステータスはチャレンジに特有ではありません。Cloudflareがすべてのチャレンジページ応答でチャレンジに設定されているとドキュメント化しているcf-mitigated応答ヘッダー、または「Just a moment」タイトルを確認してください。

cloudscraperはまだ動作していますか、良い代替は何ですか?

寛容な設定のサイトではまだ動作するかもしれないので、ターゲットに対してテストしてください。それはPythonのTLSスタックで動作し、インタープリタでスクリプトを解決しますが、現在のチャレンジはハンドシェイクとブラウザ環境も見ています。代替案:ブラウザハンドシェイクのためのcurl_cffi、JavaScriptのための実際のブラウザ、またはその両方を行うウェブアンロッカー。

Python requestsは単独でCloudflareチャレンジを通過できますか?

いいえ。requestsはJavaScriptを実行しないため、ページのスクリプトの実行に依存するチャレンジを完了できません。あなたをチャレンジしていないCloudflareサイトからページを取得できます。cf-mitigated: challengeが現れると、実際のブラウザが必要であり、直接またはそれを実行するサービスを通じて、理想的にはクリーンな住宅IPが必要です。

Cloudflareクリアランスはどのくらい持続しますか?

サイトがそれを設定する限りです。Cloudflareはこの設定をChallenge Passageと呼び、Cloudflareがセッションが疑わしいと判断した場合、クリアランスは早期に終了することがあります。cf_clearanceクッキーはそれを獲得した訪問者とデバイスに結びついているため、後続のリクエストが同じ出口とフィンガープリントを保持する場合にのみ役立ちます。これはx-qp-sessionを固定することが行うことです。

短いバージョン:「Just a moment...」はブラウザを要求しているので、requestsでのヘッダートリックでは満足させられません。cf-mitigatedでそれを検出し、チャレンジされていないものにはプレーンリクエストを維持し、チャレンジされたURLをクリーンなIPから実際のブラウザを実行するものを通じて送信し、セッションを固定してそのブラウザの費用を1回だけ支払うようにします。

住宅出口でWeb Unlockerを試してみてください