| # 提交-輪詢 API 文檔 |
|
|
| ## 概述 |
|
|
| 本地 Playwright 服務現已支持類似 2captcha 的提交-輪詢 API 模式。 |
|
|
| ## API 端點 |
|
|
| ### 1. 提交任務 |
|
|
| **端點:** `POST /api/v1/captcha/submit` |
|
|
| **請求:** |
| ```json |
| { |
| "url": "https://example.com", |
| "sitekey": "0x4AAAAAAAxxxxxx", |
| "timeout": 30000 |
| } |
| ``` |
|
|
| **參數:** |
| - `url` (string, 必需) - 包含 Turnstile 的網頁 URL |
| - `sitekey` (string, 必需) - Turnstile sitekey |
| - `timeout` (integer, 可選) - 超時時間(毫秒),默認 30000 |
|
|
| **響應 (200 OK):** |
| ```json |
| { |
| "task_id": "550e8400-e29b-41d4-a716-446655440000", |
| "status": "pending", |
| "message": "任務已提交,請輪詢查詢結果" |
| } |
| ``` |
|
|
| **示例:** |
| ```bash |
| curl -X POST http://localhost:7860/api/v1/captcha/submit \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "url": "https://example.com", |
| "sitekey": "0x4AAAAAAAxxxxxx", |
| "timeout": 30000 |
| }' |
| ``` |
|
|
| --- |
|
|
| ### 2. 查詢結果 |
|
|
| **端點:** `GET /api/v1/captcha/result/{task_id}` |
|
|
| **參數:** |
| - `task_id` (string, 必需) - 任務 ID |
|
|
| **響應 (200 OK):** |
|
|
| **待處理狀態:** |
| ```json |
| { |
| "task_id": "550e8400-e29b-41d4-a716-446655440000", |
| "status": "pending", |
| "token": null, |
| "error": null, |
| "created_at": "2024-04-04T20:00:00.000000", |
| "updated_at": "2024-04-04T20:00:00.000000" |
| } |
| ``` |
|
|
| **處理中狀態:** |
| ```json |
| { |
| "task_id": "550e8400-e29b-41d4-a716-446655440000", |
| "status": "processing", |
| "token": null, |
| "error": null, |
| "created_at": "2024-04-04T20:00:00.000000", |
| "updated_at": "2024-04-04T20:00:05.000000" |
| } |
| ``` |
|
|
| **完成狀態:** |
| ```json |
| { |
| "task_id": "550e8400-e29b-41d4-a716-446655440000", |
| "status": "ready", |
| "token": "0.xxxxxxxxxxxxx", |
| "error": null, |
| "created_at": "2024-04-04T20:00:00.000000", |
| "updated_at": "2024-04-04T20:00:30.000000" |
| } |
| ``` |
|
|
| **失敗狀態:** |
| ```json |
| { |
| "task_id": "550e8400-e29b-41d4-a716-446655440000", |
| "status": "failed", |
| "token": null, |
| "error": "無法獲取 Turnstile token", |
| "created_at": "2024-04-04T20:00:00.000000", |
| "updated_at": "2024-04-04T20:00:30.000000" |
| } |
| ``` |
|
|
| **示例:** |
| ```bash |
| curl http://localhost:7860/api/v1/captcha/result/550e8400-e29b-41d4-a716-446655440000 |
| ``` |
|
|
| --- |
|
|
| ### 3. 查詢狀態 |
|
|
| **端點:** `GET /api/v1/captcha/status/{task_id}` |
|
|
| **參數:** |
| - `task_id` (string, 必需) - 任務 ID |
|
|
| **響應 (200 OK):** |
| ```json |
| { |
| "task_id": "550e8400-e29b-41d4-a716-446655440000", |
| "status": "processing", |
| "message": "處理中" |
| } |
| ``` |
|
|
| **示例:** |
| ```bash |
| curl http://localhost:7860/api/v1/captcha/status/550e8400-e29b-41d4-a716-446655440000 |
| ``` |
|
|
| --- |
|
|
| ### 4. 獲取統計信息 |
|
|
| **端點:** `GET /api/v1/captcha/stats` |
|
|
| **響應 (200 OK):** |
| ```json |
| { |
| "total": 10, |
| "pending": 2, |
| "processing": 1, |
| "ready": 5, |
| "failed": 2 |
| } |
| ``` |
|
|
| **示例:** |
| ```bash |
| curl http://localhost:7860/api/v1/captcha/stats |
| ``` |
|
|
| --- |
|
|
| ### 5. 刪除任務 |
|
|
| **端點:** `DELETE /api/v1/captcha/{task_id}` |
|
|
| **參數:** |
| - `task_id` (string, 必需) - 任務 ID |
|
|
| **響應 (200 OK):** |
| ```json |
| { |
| "success": true, |
| "message": "任務已刪除" |
| } |
| ``` |
|
|
| **示例:** |
| ```bash |
| curl -X DELETE http://localhost:7860/api/v1/captcha/550e8400-e29b-41d4-a716-446655440000 |
| ``` |
|
|
| --- |
|
|
| ## 任務生命週期 |
|
|
| ``` |
| 提交 → pending → processing → ready/failed |
| ↓ ↓ |
| submit poll poll poll |
| ↓ |
| 返回 token |
| ``` |
|
|
| ### 狀態說明 |
|
|
| | 狀態 | 說明 | 可操作 | |
| |------|------|--------| |
| | `pending` | 等待處理 | 輪詢 | |
| | `processing` | 處理中 | 輪詢 | |
| | `ready` | 已完成 | 獲取 token | |
| | `failed` | 已失敗 | 查看錯誤 | |
|
|
| --- |
|
|
| ## 使用示例 |
|
|
| ### Python |
|
|
| ```python |
| import requests |
| import time |
| |
| # 1. 提交任務 |
| response = requests.post( |
| 'http://localhost:7860/api/v1/captcha/submit', |
| json={ |
| 'url': 'https://example.com', |
| 'sitekey': '0x4AAAAAAAxxxxxx', |
| 'timeout': 30000 |
| } |
| ) |
| |
| data = response.json() |
| task_id = data['task_id'] |
| print(f"任務已提交: {task_id}") |
| |
| # 2. 輪詢結果 |
| while True: |
| response = requests.get( |
| f'http://localhost:7860/api/v1/captcha/result/{task_id}' |
| ) |
| |
| result = response.json() |
| status = result['status'] |
| |
| print(f"狀態: {status}") |
| |
| if status == 'ready': |
| print(f"Token: {result['token']}") |
| break |
| elif status == 'failed': |
| print(f"錯誤: {result['error']}") |
| break |
| |
| time.sleep(2) # 等待 2 秒後重新輪詢 |
| ``` |
|
|
| ### JavaScript |
|
|
| ```javascript |
| // 1. 提交任務 |
| const response = await fetch('http://localhost:7860/api/v1/captcha/submit', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| url: 'https://example.com', |
| sitekey: '0x4AAAAAAAxxxxxx', |
| timeout: 30000 |
| }) |
| }); |
| |
| const data = await response.json(); |
| const taskId = data.task_id; |
| console.log(`任務已提交: ${taskId}`); |
| |
| // 2. 輪詢結果 |
| const pollResult = async () => { |
| while (true) { |
| const response = await fetch( |
| `http://localhost:7860/api/v1/captcha/result/${taskId}` |
| ); |
| |
| const result = await response.json(); |
| const status = result.status; |
| |
| console.log(`狀態: ${status}`); |
| |
| if (status === 'ready') { |
| console.log(`Token: ${result.token}`); |
| break; |
| } else if (status === 'failed') { |
| console.log(`錯誤: ${result.error}`); |
| break; |
| } |
| |
| await new Promise(resolve => setTimeout(resolve, 2000)); |
| } |
| }; |
| |
| pollResult(); |
| ``` |
|
|
| ### cURL |
|
|
| ```bash |
| # 1. 提交任務 |
| TASK_ID=$(curl -s -X POST http://localhost:7860/api/v1/captcha/submit \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "url": "https://example.com", |
| "sitekey": "0x4AAAAAAAxxxxxx", |
| "timeout": 30000 |
| }' | jq -r '.task_id') |
| |
| echo "任務已提交: $TASK_ID" |
| |
| # 2. 輪詢結果 |
| while true; do |
| RESULT=$(curl -s http://localhost:7860/api/v1/captcha/result/$TASK_ID) |
| STATUS=$(echo $RESULT | jq -r '.status') |
| |
| echo "狀態: $STATUS" |
| |
| if [ "$STATUS" = "ready" ]; then |
| TOKEN=$(echo $RESULT | jq -r '.token') |
| echo "Token: $TOKEN" |
| break |
| elif [ "$STATUS" = "failed" ]; then |
| ERROR=$(echo $RESULT | jq -r '.error') |
| echo "錯誤: $ERROR" |
| break |
| fi |
| |
| sleep 2 |
| done |
| ``` |
|
|
| --- |
|
|
| ## 最佳實踐 |
|
|
| ### 1. 輪詢間隔 |
|
|
| 建議輪詢間隔: |
| - 第一次:立即查詢 |
| - 後續:每 2-5 秒查詢一次 |
|
|
| ```python |
| import time |
| |
| # 指數退避策略 |
| poll_interval = 1 |
| max_interval = 10 |
| |
| while True: |
| result = query_result(task_id) |
| |
| if result['status'] in ['ready', 'failed']: |
| break |
| |
| time.sleep(poll_interval) |
| poll_interval = min(poll_interval * 1.5, max_interval) |
| ``` |
|
|
| ### 2. 超時處理 |
|
|
| ```python |
| import time |
| |
| start_time = time.time() |
| timeout = 60 # 60 秒超時 |
| |
| while time.time() - start_time < timeout: |
| result = query_result(task_id) |
| |
| if result['status'] in ['ready', 'failed']: |
| return result |
| |
| time.sleep(2) |
| |
| raise TimeoutError("任務超時") |
| ``` |
|
|
| ### 3. 錯誤處理 |
|
|
| ```python |
| try: |
| # 提交任務 |
| response = requests.post(url, json=payload) |
| response.raise_for_status() |
| |
| task_id = response.json()['task_id'] |
| |
| # 輪詢結果 |
| while True: |
| response = requests.get(f'{url}/{task_id}') |
| response.raise_for_status() |
| |
| result = response.json() |
| |
| if result['status'] == 'ready': |
| return result['token'] |
| elif result['status'] == 'failed': |
| raise Exception(result['error']) |
| |
| time.sleep(2) |
| |
| except requests.RequestException as e: |
| print(f"請求失敗: {e}") |
| except Exception as e: |
| print(f"錯誤: {e}") |
| ``` |
|
|
| --- |
|
|
| ## 與 2captcha 的相似性 |
|
|
| | 功能 | 2captcha | 本地服務 | |
| |------|----------|---------| |
| | 提交任務 | POST /api/captcha | POST /api/v1/captcha/submit | |
| | 查詢結果 | GET /api/result | GET /api/v1/captcha/result/{id} | |
| | 任務 ID | captcha_id | task_id | |
| | 狀態 | 0=pending, 1=ready | pending/processing/ready/failed | |
| | 費用 | 按次計費 | 免費 | |
|
|
| --- |
|
|
| ## 常見問題 |
|
|
| **Q: 任務多久過期?** |
| A: 默認 30 分鐘後自動過期並刪除。 |
|
|
| **Q: 可以同時提交多少個任務?** |
| A: 無限制,但受系統資源限制。 |
|
|
| **Q: 輪詢頻率有限制嗎?** |
| A: 無限制,但建議每 2-5 秒輪詢一次。 |
|
|
| **Q: 任務失敗後可以重新提交嗎?** |
| A: 可以,直接提交新任務即可。 |
|
|
| --- |
|
|
| ## 支持 |
|
|
| - 文檔:查看本文件 |
| - 問題:提交 Issue |
| - 討論:加入社區 |
|
|