FastAPIバックエンドからsolvethreeを呼び出す

著者:solvethree読了時間:1分

解答生成全体の完了後ではなく、作成後にタスクを返します。呼び出し元はステータスが ready または failed になるまで取得を繰り返し、解答生成に成功した場合はトークンを対象サイトに送信します。

このページの内容

サーバーと対象を設定する

Python 3.10 以降を使用してください。python -m pip install "fastapi[standard]" で FastAPI をインストールし、続いて python -m pip install https://solvethree.com/downloads/solvethree_sdk-0.1.0-py3-none-any.whl で公式 SDK をインストールしてください。

アプリを起動する前に、サーバープロセスで次の環境変数を設定してください。

  • SOLVETHREE_API_KEY: ワークスペースの API キーです。サーバーで保持してください。SDK はこのキーを使用して solvethree に対して認証します。
  • INTEGRATION_KEY: 呼び出し元が FastAPI ルートに対して認証するために、X-Integration-Key ヘッダーで送信する別個のシークレットです。
  • CAPTCHA_SITE: 完全な URL ではなく、対象のホスト名です。
  • SOLVETHREE_PROXY: このタスクで使用する HTTP または HTTPS プロキシの URL です。パス、クエリ、フラグメントは含めません。

ルートを呼び出す前に、ワークスペースで対象の有効なプロファイルを作成してください。この例ではアクションを login に固定しているため、プロファイルと意図した対象リクエストがその用途に一致している必要があります。ホスト名を指定してもプロファイルは作成されません。サイトプロファイルの設定を参照してください。

FastAPI ルートを追加して実行する

この例を main.py として保存してください。どちらのルートにも X-Integration-Key が必要です。作成ルートはサーバー設定だけでタスクを構築します。呼び出し元はリクエスト本文でサイト、アクション、プロキシを指定しません。

FastAPI · 同期SDKをスレッドプールで実行
# pip install "fastapi[standard]" and the official solvethree SDK wheel.
import hmac
import os
from fastapi import FastAPI, HTTPException, Request
from fastapi.concurrency import run_in_threadpool
from solvethree import Solvethree, SolvethreeError

app = FastAPI()

def authorize(request):
    if not hmac.compare_digest(request.headers.get("x-integration-key", ""), os.environ["INTEGRATION_KEY"]):
        raise HTTPException(status_code=401, detail="Unauthorized")

@app.post("/captcha/tasks")
async def create(request: Request):
    authorize(request)
    task = {"site": os.environ["CAPTCHA_SITE"], "action": "login", "proxy": os.environ["SOLVETHREE_PROXY"]}
    if request.headers.get("x-request-id"):
        task["requestId"] = request.headers["x-request-id"]
    def submit():
        return Solvethree(os.environ["SOLVETHREE_API_KEY"]).create_task(task, wait_ms=0, timeout=20)
    try:
        return await run_in_threadpool(submit)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid task input")
    except SolvethreeError as error:
        raise HTTPException(status_code=502, detail={"requestId": error.request_id, "taskId": error.task_id})

@app.get("/captcha/tasks/{task_id}")
async def retrieve(task_id: str, request: Request):
    authorize(request)
    def fetch():
        return Solvethree(os.environ["SOLVETHREE_API_KEY"]).get_task(task_id, wait_ms=15000, timeout=25)
    try:
        return await run_in_threadpool(fetch)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid task ID")
    except SolvethreeError as error:
        raise HTTPException(status_code=502, detail={"requestId": error.request_id, "taskId": error.task_id})

タスクはデフォルトで reCAPTCHA Enterprise v3 を使用します。site、action、1つの proxy を送信し、type フィールドと url フィールドは含めません。

Python SDK は同期式です。async def 内で直接呼び出すとイベントループをブロックするため、両方のハンドラーで run_in_threadpool を使用します。作成では wait_ms=0 と20秒の SDK リクエストタイムアウトを使用し、取得では15秒の待機と25秒の SDK リクエストタイムアウトを設定します。どちらのハンドラーも、client.solve を使って解答生成全体の完了を待つことはありません。

ローカル開発では、ファイルがあるディレクトリから fastapi dev main.py を実行してください。ルートは POST http://127.0.0.1:8000/captcha/tasks と GET http://127.0.0.1:8000/captcha/tasks/{task_id} です。FastAPI が app オブジェクトを検出するため、別途登録する URLconf はありません。

タスクを作成・取得し、中断されたリクエストを復旧する

  1. 1.

    固定のリクエスト ID で1回だけ作成する

    X-Integration-Key と、この論理的なタスク用に保持する X-Request-ID を付けて POST /captcha/tasks を送信してください。リクエスト ID は、1–64文字の ASCII 英数字、アンダースコア、ハイフンで構成する必要があります。ルートはこれを requestId として solvethree に渡します。返されたタスク ID を保存してください。

  2. 2.

    タスクのステータスを読む前に HTTP ステータスを確認する

    ラッパーは、認証失敗時に401、ValueError による入力エラー時に400、捕捉した SolvethreeError 例外に対して502を返します。502の detail には SDK エラーの requestId と taskId が含まれます。レスポンスをタスクとして扱う前に、その HTTP エラーを処理してください。

  3. 3.

    既存のタスクを取得する

    X-Integration-Key を付けて GET /captcha/tasks/{task_id} を呼び出してください。HTTP レスポンスが成功するたびに status を確認します。取得時の待機が終わっても、タスクが終了状態に達していない場合があります。別のタスクを作成せず、同じタスクを再度取得してください。

  4. 4.

    作成結果が不明な場合に復旧する

    作成がタイムアウトし、タスク ID がある場合は、そのタスクを取得してください。ない場合は、元の X-Request-ID を使い、サーバー側のタスク入力値を変更せずに POST を繰り返してください。同じタスクに同じ ID を再利用すると既存のタスクが返されます。入力値を変更すると競合が発生します。元の ID は別途保持し、エラーレスポンスの requestId で置き換えないでください。

HTTP レスポンス成功後の処理
HTTP レスポンス成功後の処理
タスクのステータス呼び出し元の処理
pendingまたはsolving同じタスクの取得を続けてください。利用可能な解答はまだありません。
readysolution を読み取り、対象サイトへのリクエストを準備してください。
failedポーリングを停止してください。新しいタスクを作成すべきか判断する前に、errorCode と error を確認してください。解答生成に失敗した場合は返金されます。

元のプロキシと返された識別情報を使ってトークンを送信する

これらのルートはタスクデータを返すだけで、対象のウェブサイトには何も送信しません。status が ready になったら、対象サイトへのリクエストを行うコンポーネントは、タスク ID ではなく solution.token を使用する必要があります。

タスク作成時と同じプロキシを経由し、返された solution.userAgent と solution.secChUa のブラウザー識別情報を使って送信してください。現在の環境にも同じプロキシが設定されていると想定せず、元のタスク設定を保持してください。SDK の successful_proxy(original_task, ready_result) ヘルパーは、その元のタスクからプロキシを選択します。

送信内容は、意図した対象サイトとアクションに紐付けたままにしてください。タスクが ready であることは、solvethree が解答を生成したことを意味します。アプリケーションのリクエストが受け入れられたかどうかは、対象サイトのレスポンスでのみ判断できます。

最初のタスクを実行しましょう。

$5のワークスペースクレジットで始められます。

ワークスペースを作成
FastAPIバックエンドからsolvethreeを呼び出す · solvethree