حل reCAPTCHA وإرساله باستخدام aiohttp
استخدم aiohttp مباشرةً مع واجهة REST API: أنشئ مهمة واحدة، واستعلم عنها دوريًا حتى تصبح جاهزة، ثم أرسل solution.token إلى الموقع المستهدف. افصل طلبات API وطلبات الموقع المستهدف في جلستين حتى لا يصل رمز Bearer لمساحة عملك إلى الموقع المستهدف مطلقًا.
يتطلب المثال Python 3.11+، ويضع الإنشاء والاستعلام الدوري والإرسال إلى الموقع المستهدف ضمن مهلة واحدة مدتها 180 ثانية. هذه مهلة للتطبيق، وليست وقت حل مضمونًا.
في هذه الصفحة
اضبط الموقع والوكيل والطلب المستهدف
- اضبط SOLVETHREE_API_KEY على مفتاح API لمساحة عملك. احتفظ به على الخادم.
- اضبط SOLVETHREE_PROXY على عنوان URL للوكيل الذي ينبغي أن تستخدمه الخدمة للحل. يستخدم الإرسال إلى الموقع المستهدف الوكيل نفسه.
- استبدل accounts.example.com والإجراء login باسم المضيف والإجراء المستخدمَين في تكاملك. يحتاج الموقع إلى ملف تعريف نشط متاح لمساحة عملك؛ أعدّ ملف التعريف قبل إنشاء المهام.
- استبدل عنوان URL لتسجيل الدخول المستهدف وبيانات النموذج بطلب التطبيق الفعلي. يرسل المثال g-recaptcha-response فقط؛ أضف الحقول الأخرى التي يتطلبها الموقع المستهدف، واستخدم حقل الرمز الذي يتوقعه.
ترسل المهمة site وaction وproxy واحدًا. وتستخدم افتراضيًا reCAPTCHA Enterprise v3؛ لا تضف حقول type أو url. إذا كان مسار الموقع المستهدف يحتاج إلى ملفات تعريف ارتباط من طلب سابق، فأرسل تلك الطلبات عبر جلسة الموقع المستهدف، إذ يبدأ المثال بجلسة جديدة.
أنشئ مرة واحدة، واستعلم دوريًا، ثم أرسل
# pip install aiohttp
import asyncio
import os
import uuid
from urllib.parse import urlsplit, unquote
import aiohttp
async def main():
api = "https://api.solvethree.com/v1"
proxy = os.environ["SOLVETHREE_PROXY"]
task = {"site": "accounts.example.com", "action": "login", "proxy": proxy, "requestId": str(uuid.uuid4())}
task_id = None
try:
async with asyncio.timeout(180):
async with aiohttp.ClientSession(headers={"Authorization": "Bearer " + os.environ["SOLVETHREE_API_KEY"]}) as client:
async with client.post(api + "/tasks", params={"waitMs": 0}, json=task, allow_redirects=False) as response:
response.raise_for_status()
if response.status >= 300: raise RuntimeError("Unexpected API redirect")
result = await response.json()
task_id = result["id"]
while result["status"] in ("pending", "solving"):
async with client.get(api + "/tasks/" + task_id, params={"waitMs": 15000}, allow_redirects=False) as response:
response.raise_for_status()
if response.status >= 300: raise RuntimeError("Unexpected API redirect")
result = await response.json()
if result["status"] in ("pending", "solving"): await asyncio.sleep(1)
if result["status"] != "ready":
raise RuntimeError("Task failed: " + str(result.get("errorCode", "unknown")))
solution = result["solution"]
# A separate session prevents the API Bearer header reaching the target.
proxy_url = urlsplit(proxy)
netloc = proxy_url.netloc.rsplit("@", 1)[-1]
server = f"{proxy_url.scheme}://{netloc}"
auth = aiohttp.BasicAuth(unquote(proxy_url.username), unquote(proxy_url.password or "")) if proxy_url.username else None
async with aiohttp.ClientSession() as target:
async with target.post("https://accounts.example.com/login", proxy=server, proxy_auth=auth,
headers={"User-Agent": solution["userAgent"], "sec-ch-ua": solution["secChUa"]},
data={"g-recaptcha-response": solution["token"]}, allow_redirects=False) as response:
print("Application HTTP status:", response.status)
except TimeoutError:
print("Timed out; recover requestId", task["requestId"], "taskId", task_id)
raise
asyncio.run(main())هذا استخدام مباشر وغير متزامن لـ REST، وليس أسلوبًا غير متزامن في حزمة SDK الرسمية لـ Python. يرسل طلب POST واحدًا للإنشاء إلى /v1/tasks، ويحفظ id المُعاد، ويستخدم طلبات GET للاستعلام الدوري عن تلك المهمة. تُبقي pending وsolving الحلقة مستمرة؛ وتسمح ready بالإرسال؛ وتوقف المهمة بحالة failed الحلقة.
يحدد proxy في متن المهمة الوكيل الذي يجب أن تستخدمه solvethree. ولا يظهر معاملا aiohttp، وهما proxy وproxy_auth، إلا في استدعاء HTTP للموقع المستهدف، وليس في استدعاءات API. يفصل المثال بيانات اعتماد الوكيل عن عنوان URL لخادم الوكيل، ويرسل الرمز مع userAgent وsecChUa المُعادين.
الجلسات منفصلة عمدًا: جلسة API وحدها تحتوي على ترويسة Authorization. عمليات إعادة التوجيه معطّلة، وتُعامل عمليات إعادة توجيه API كأخطاء بدلًا من اتباعها.
- يُرجع طلب API خطأ HTTP.
يفحص المثال حالة HTTP قبل قراءة بيانات استجابة النجاح.
جرّب هذا: تعامل مع خطأ HTTP بمعزل عن فشل المهمة. صحّح المدخلات غير الصالحة أو مشكلات التفويض قبل إعادة المحاولة.
- تصل المهمة إلى الحالة failed.
افحص errorCode وerror في استجابة المهمة. يطرح المثال استثناءً يتضمن errorCode فقط.
جرّب هذا: توقف عن الاستعلام الدوري عن تلك المهمة وعالج السبب المُبلّغ عنه. أصلح السبب المُبلّغ عنه قبل المحاولة مجددًا.
- المهمة بحالة ready، لكن التطبيق يرفض الطلب.
افحص حقل الرمز المُرسل وعنوان URL المستهدف وهوية المتصفح والوكيل المُعادين، إضافةً إلى أي حقول نموذج أو ملفات تعريف ارتباط مطلوبة.
جرّب هذا: أرسل solution.token مع الهوية المُعادة عبر الوكيل نفسه. اقرأ استجابة التطبيق وفق معايير النجاح لدى الموقع المستهدف؛ فحالة HTTP المطبوعة وحدها لا تثبت القبول.
استردّ العمل بعد انتهاء المهلة دون شراء حل آخر
انتهاء مهلة الإنشاء لا يثبت فشل الإنشاء. يحتفظ المثال بالمدخلات الأصلية في task، ويطبع requestId الخاص بها مع task_id عند انتهاء المهلة. تبقى هذه المتغيرات فقط ما دامت العملية قيد التشغيل؛ والمعرّفات المطبوعة ليست سجل استرداد كاملًا.
- 1.
احفظ الطلب الأصلي قبل الإنشاء
للاسترداد بعد إعادة التشغيل، احتفظ بأمان بمتن المهمة كاملًا، بما في ذلك requestId وproxy. يولّد المثال UUID جديدًا في كل تشغيل، لذا فإن مجرد إعادة تشغيله ينشئ طلبًا منطقيًا جديدًا.
- 2.
استأنف المهمة الحالية
إذا كان لديك task_id، فاسترجع تلك المهمة. إذا انتهت مهلة الإنشاء قبل تلقي معرّف، فكرر الإنشاء باستخدام متن المهمة وrequestId المحفوظين دون تغيير. تُعيد واجهة API المهمة الحالية؛ أما تغيير المدخلات مع إعادة استخدام المعرّف فيؤدي إلى تعارض.
- 3.
تحقق مما إذا كان الإرسال قد حدث بالفعل
تشمل المهلة أيضًا طلب POST إلى الموقع المستهدف. إذا انتهت مهلة ذلك الطلب، فافحص النتيجة في التطبيق قبل الإرسال مجددًا: ربما تلقى الطلب رغم أن عميلك لم يتلقَّ استجابة. استرداد مهمة solvethree لا يحدد ما حدث لدى الموقع المستهدف.
المصادر ومرجع API
شغّل مهمتك الأولى.
ابدأ برصيد قدره $5 في مساحة العمل.