asyncioから同期Python SDKを使用する
asyncio.to_thread を使って、イベントループをブロックせずに同期メソッド Solvethree.solve を実行します。オフロードする処理をセマフォで囲み、各ワーカー呼び出しの内部で個別のクライアントを作成してください。SDK にネイティブの非同期クライアントはありません。リクエストとポーリングは、引き続きスレッド内で同期的に実行されます。
このページの内容
処理数を制限し、SDK の期限を設定する
この例には Python 3.10+ と公式 Python SDK が必要です。サーバー環境に SOLVETHREE_API_KEY と SOLVETHREE_PROXY を設定してください。accounts.example.com と login を対象のホスト名とアクションに置き換えます。対象サイトには、ワークスペースで利用できる有効なプロファイルがあらかじめ必要です。サイトプロファイルの設定を参照してください。
import asyncio
import os
import uuid
from solvethree import Solvethree
async def main():
limit = asyncio.Semaphore(4)
async def solve_one(number):
task = {"site": "accounts.example.com", "action": "login", "proxy": os.environ["SOLVETHREE_PROXY"], "requestId": str(uuid.uuid4())}
def blocking_solve():
return Solvethree(os.environ["SOLVETHREE_API_KEY"]).solve(task, timeout=180)
async with limit:
try:
result = await asyncio.to_thread(blocking_solve)
print("Ready task:", result["id"])
# Submit the solution now with its proxy and browser identity.
except Exception as error:
print("Job", number, "requestId", task["requestId"], "error class", type(error).__name__)
# Retain task and inspect SDK recovery IDs before restarting it.
await asyncio.gather(*(solve_one(number) for number in range(8)))
asyncio.run(main())
# Canceling an await does not stop an already-running thread or remote task.- セマフォは、
gatherに渡した 8 件のジョブのうち、同時に 4 件を実行可能にします。空きを待っているジョブは、まだ SDK 呼び出しを開始していません。 - 各ワーカーは、スレッド間でクライアントを共有せず、それぞれ独自の
Solvethreeクライアントを作成します。 - SDK の
timeout=180が解決処理の期限を設定します。セマフォの待機時間は含まれません。await の外側に asyncio のタイムアウトを設定しても、この SDK の期限の代わりにはなりません。 - タスクとその
requestIdは、ワーカーに入る前に作成されます。エラーが発生するたびに代わりの ID を生成するのではなく、復旧に使えるよう両方を保持してください。
SDK はタスクを 1 回だけ作成し、準備完了になるかエラーが発生するまでポーリングします。solve の外側に別のポーリングループを書く必要はありません。
キャンセルしてもワーカーやリモートタスクは停止しない
to_thread を待機しているコルーチンをキャンセルしても、実行中のスレッドは停止せず、リモートの解決処理もキャンセルされません。サンプルの通常の例外ハンドラーは、キャンセル時のクリーンアップを行うものではありません。
これは同時実行数の制限にも影響します。キャンセルによりセマフォが解放されても、ワーカーは処理を続けることがあります。すぐに代わりのジョブを実行すると、4 件を超える SDK 呼び出しがアクティブなままになる可能性があります。キャンセル時にも厳密な制限が必要な場合は、未完了のワーカーを追跡し、完了するまでその分の実行枠を確保し続けてください。
本番環境では、コルーチンの終了後も復旧処理からアクセスできる場所に、正確なタスク入力とリクエスト ID を保持してください。タイムアウトは結果の受信を待つのをやめたことを意味し、必ずしもタスク作成が失敗したことを意味しません。
- 1.
既知のタスクを復旧する
Python SDK のエラーの
task_idとrequest_idを確認してください。タスク ID がある場合は、別のタスクを作成せずにget_taskで取得を再開します。 - 2.
中断された作成処理を復旧する
タスク ID がない場合は、元の
requestIdと完全に同じタスク入力を使って再送信します。作成が成功していれば、既存のタスクが返されます。同じ ID を再利用しながら入力を変更すると、競合が発生します。 - 3.
失敗と中断を区別する
TaskFailedErrorは、タスクが終端の失敗状態に達したことを意味します。errorCodeとerrorを確認してください。ポーリングを続けたり、無効な入力を変更せずに無条件で再試行したりしないでください。HTTP エラーは、これとは別の層の失敗です。
ワーカーが結果を返したらソリューションを送信する
サンプルは準備完了になったタスクの ID を出力するだけで、対象には何も送信しません。その出力の後にあるコメントをアプリケーション固有の送信処理に置き換え、gather のすべてのジョブの完了を待たずに、ソリューションを速やかに使用してください。
返されたソリューションを、その userAgent、secChUa、解決時と同じプロキシとともに送信してください。SDK の successful_proxy ヘルパーは、元のタスクと準備完了の結果からプロキシを選択します。準備完了の結果はソリューションが生成されたことを意味するだけで、アプリケーションのリクエストが成功したことを意味しません。
参照元とAPIリファレンス
最初のタスクを実行しましょう。
$5のワークスペースクレジットで始められます。