استخدام Guzzle مع API مهام CAPTCHA

بقلم solvethreeمدة القراءة: 3 دقيقة

استخدم Guzzle 7 لإنشاء مهمة، ثم استعلم دوريًا عن معرّفها حتى تصبح حالتها ready أو failed. احتفظ بقيمة requestId إذا حدث أي خطأ: انتهاء مهلة الاتصال لا يثبت فشل إنشاء المهمة.

هذا تكامل REST، وليس حزمة SDK رسمية لـ PHP.

في هذه الصفحة

تحضير طلب reCAPTCHA

  • ثبّت Guzzle 7 باستخدام Composer. يظهر أمر التثبيت في بداية المثال.
  • اضبط SOLVETHREE_API_KEY على مفتاح API لمساحة عملك، وSOLVETHREE_PROXY على عنوان URL للخادم الوكيل. احتفظ بالمفتاح على الخادم.
  • أنشئ ملف تعريف نشطًا لموقعك المستهدف في لوحة التحكم قبل طلب حل. راجع إعداد ملف تعريف الموقع.
  • استبدل accounts.example.com وlogin باسم المضيف والإجراء المستهدفين. يقبل الحقل site اسم مضيف، لا عنوان URL كاملًا.

يطلب المثال reCAPTCHA Enterprise v3، وهو القيمة الافتراضية لـ recaptchaType. لا تضف الحقلين type أو url. يوفّر المثال proxy واحدًا؛ وإذا استخدمت proxyCandidates بدلًا منه، فاحذف proxy تمامًا.

الإنشاء مرة واحدة، ثم الاستعلام الدوري ضمن مدة زمنية ثابتة

Guzzle · طلب POST واحد واستعلام دوري ضمن حدود
<?php
// composer require guzzlehttp/guzzle:^7
require __DIR__.'/vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://api.solvethree.com/v1/',
    'headers' => ['Authorization' => 'Bearer '.getenv('SOLVETHREE_API_KEY'), 'Accept' => 'application/json'],
    'allow_redirects' => false,
    'http_errors' => false,
]);
$input = ['site' => 'accounts.example.com', 'action' => 'login',
    'proxy' => getenv('SOLVETHREE_PROXY'), 'requestId' => bin2hex(random_bytes(16))];
$deadline = microtime(true) + 180;
$call = function (string $method, string $path, array $options = []) use ($client, $deadline): array {
    $remaining = $deadline - microtime(true);
    if ($remaining <= 0) throw new RuntimeException('Local deadline reached');
    $response = $client->request($method, $path, $options + ['timeout' => min(25, $remaining), 'connect_timeout' => min(10, $remaining)]);
    if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
        throw new RuntimeException('API HTTP '.$response->getStatusCode());
    }
    return json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR);
};
$id = null;
try {
    $result = $call('POST', 'tasks?waitMs=0', ['json' => $input]);
    $id = $result['id'];
    while (in_array($result['status'], ['pending', 'solving'], true)) {
        $result = $call('GET', 'tasks/'.rawurlencode($id).'?waitMs=15000');
        if (in_array($result['status'], ['pending', 'solving'], true)) usleep(1000000);
    }
    if ($result['status'] !== 'ready') {
        throw new RuntimeException('Task failed: '.($result['errorCode'] ?? 'unknown'));
    }
    echo 'Ready task: '.$id.PHP_EOL;
    // Submit $result['solution']['token'] with userAgent/secChUa and the same proxy.
} catch (Throwable $error) {
    fwrite(STDERR, 'Recovery requestId='.$input['requestId'].' taskId='.($id ?? 'unknown').PHP_EOL);
    throw $error;
}

يصادق العميل باستخدام مفتاح Bearer. تنتهي قيمة base_uri لديه بـ /v1/، ومسارات الطلبات نسبية، لذا تحتفظ ببادئة API تلك. يرسل الخيار json في Guzzle مدخلات المهمة بصيغة JSON.

تنفّذ الدالة المساعدة طلب POST واحدًا للإنشاء. ثم تستعلم دوريًا عن معرّف المهمة المُعاد ما دامت الحالة pending أو solving. يتشارك الإنشاء والاستعلام الدوري مدة إجمالية قدرها 180 ثانية؛ ويُقيَّد كل استدعاء HTTP بالوقت المتبقي، مع حد أقصى لمهلتي الطلب والاتصال يبلغ 25 و10 ثوانٍ على الترتيب. لا يبدأ الاستعلام الجديد المدة الإجمالية من جديد.

عمليات إعادة التوجيه معطلة. يتيح ضبط http_errors على false للدالة المساعدة التحقق صراحةً من الاستجابات خارج نطاق 2xx قبل فك ترميز حمولة نجاح؛ ولا يمنع أخطاء الاتصال أو انتهاء المهل.

  • تؤدي استجابة HTTP خارج نطاق 2xx إلى طرح خطأ HTTP خاص بواجهة API. شخّص حالة الاستجابة وجسمها بدلًا من التعامل معها على أنها محاولة حل فاشلة.
  • توقف المهمة ذات الحالة النهائية failed الاستعلام الدوري. يطرح المثال قيمة errorCode الخاصة بها؛ وافحص أيضًا حقل error في المهمة للاطلاع على الرسالة التشخيصية. تُرد تكلفة محاولات الحل الفاشلة.
  • وحدها الحالة ready تحتوي على حل قابل للاستخدام. يشير سطر “Ready task” المطبوع إلى جاهزية المهمة، لا إلى قبول الموقع المستهدف لها.

استعادة طلب انتهت مهلته دون الدفع مقابل مهمة أخرى

احتفظ بقيمة requestId المُولَّدة ومدخلات المهمة الدقيقة قبل إرسال POST. تطبع كتلة catch قيمة requestId، وكذلك taskId إذا أعادته عملية الإنشاء. تساعد هذه المخرجات في الاسترداد، لكنها لا تغني عن حفظ الطلب قبل إرساله.

  1. 1.

    إذا كان لديك معرّف مهمة، فاسترجع تلك المهمة

    استخدم GET /v1/tasks/:id للتحقق من حالتها. بلوغ المهلة المحلية للدالة المساعدة لا يثبت فشل المهمة البعيدة.

  2. 2.

    إذا انتهت مهلة الإنشاء دون إعادة معرّف، فاسترد المهمة باستخدام requestId

    أرسل طلب المهمة نفسه باستخدام requestId المحفوظ. تعيد المدخلات المتطابقة المهمة الموجودة؛ أما المدخلات المختلفة مع ذلك المعرّف فتؤدي إلى تعارض.

  3. 3.

    لا تُعد تشغيل البرنامج النصي كاختصار للاسترداد

    يولّد كل تشغيل قيمة requestId جديدة. لا تضف إعادة محاولة تلقائية لـ POST تولّد معرّفًا آخر، ولا تُعد المحاولة عشوائيًا بمدخلات غير صالحة دون تغيير.

إرسال الرمز عبر الخادم الوكيل نفسه

يتوقف المثال قبل الاتصال بالجهة المستهدفة. أرسل solution.token، لا معرّف المهمة، واستخدم solution.userAgent وsolution.secChUa المُعادَين كترويستي User-Agent وSec-CH-UA في الطلب المستهدف.

تخبر قيمة proxy داخل JSON الخاص بالمهمة solvethree بالخادم الوكيل الذي يجب استخدامه للحل. وهي لا تضبط اتصال Guzzle بواجهة API أو طلبك اللاحق إلى الجهة المستهدفة. اضبط الطلب المستهدف بشكل منفصل ليستخدم الخادم الوكيل نفسه.

استخدم عميلًا منفصلًا للطلب المستهدف حتى لا تُرسَل ترويسة Authorization الافتراضية لعميل API إلى الموقع. احتفظ بالرمز وهوية المتصفح المُعادة والخادم الوكيل والجهة المستهدفة المقصودة معًا. قد يُرفض الرمز الجاهز إذا لم يتطابق الطلب اللاحق معها.

شغّل مهمتك الأولى.

ابدأ برصيد قدره $5 في مساحة العمل.

إنشاء مساحة عمل
استخدام Guzzle مع API مهام CAPTCHA · solvethree