حل مهام CAPTCHA بالتزامن بلغة Go

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

استخدم عميل Go SDK واحدًا مشتركًا ومجموعة ثابتة من وحدات التنفيذ. خصّص سياقًا مستقلًا لكل مهمة، ثم أرسل كل حل جاهز فورًا بدلًا من تجميع الرموز لإرسالها لاحقًا على دفعة واحدة.

في هذه الصفحة

إعداد العميل والهدف

  • استخدم Go 1.22 أو أحدث والوحدة الرسمية solvethree.com/sdk بالإصدار v0.1.0. تتوفر تفاصيل التثبيت في توثيق التكامل مع Go.
  • اضبط SOLVETHREE_API_KEY وSOLVETHREE_PROXY في بيئة الخادم. أبقِ مفتاح API خارج كود المتصفح.
  • استبدل accounts.example.com وlogin باسم المضيف والإجراء المستهدفين. تحتاج مساحة العمل إلى ملف تعريف نشط لذلك الموقع؛ إرسال اسم المضيف لا ينشئ ملفًا. راجع إعداد ملف تعريف الموقع.

يطلب المثال reCAPTCHA Enterprise، وهو الخيار الافتراضي. ويوفّر وكيلًا واحدًا ويترك Type وURL دون تعيين عمدًا: تستخدم طلبات reCAPTCHA اسم مضيف الموقع والإجراء، وليس هذين الحقلين.

ضع حدًا للعمل، لا لعدد العملاء

العميل آمن للاستخدام المتزامن. يبدأ هذا المثال أربع وحدات تنفيذ ويمرّر إليها ثماني مهام عبر قناة غير مخزّنة مؤقتًا. ينتظر المُنتِج عندما تكون جميع وحدات التنفيذ مشغولة، بدلًا من تشغيل goroutine لكل مهمة. العدد أربعة هو إعداد توضيحي للتطبيق، وليس حدًا لخدمة solvethree.

Go · أربعة عوامل تنفيذ ومعالجة فورية لكل مهمة
package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "sync"
    "time"
    solvethree "solvethree.com/sdk"
)

func main() {
    client, err := solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"), nil)
    if err != nil { log.Fatal(err) }
    jobs := make(chan int)
    var workers sync.WaitGroup
    for worker := 0; worker < 4; worker++ {
        workers.Add(1)
        go func() {
            defer workers.Done()
            for job := range jobs {
                ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
                input := solvethree.TaskRequest{Site: "accounts.example.com", Action: "login", Proxy: os.Getenv("SOLVETHREE_PROXY")}
                result, err := client.Solve(ctx, input, solvethree.SolveOptions{})
                if err != nil { cancel(); log.Printf("Job %d: %v", job, err); continue }
                fmt.Println("Ready task:", result.ID)
                // Submit this solution now, with its proxy and browser identity.
                cancel()
            }
        }()
    }
    for job := 0; job < 8; job++ { jobs <- job }
    close(jobs)
    workers.Wait()
}

تنشئ كل وحدة تنفيذ سياقًا مستقلًا مدته 180 ثانية لكل مهمة. تنشئ Solve المهمة مرة واحدة وتنتظر نتيجتها. ولأن المثال لا يحدد RequestID، تنشئ SDK معرّفًا لعملية الإنشاء تلك. يُسجَّل الاستدعاء الفاشل، وتنقل continue وحدة التنفيذ المعنية فقط إلى مهمتها التالية؛ ولا توقف الدفعة.

أبقِ الإلغاء داخل الحلقة كما هو موضح، حتى تحرّر كل مهمة موارد سياقها سريعًا. يوقف الإلغاء الانتظار المحلي؛ ولا يلغي العمل البعيد.

عالج الحل قبل تولّي مهمة أخرى

يطبع المثال معرّف المهمة الجاهزة؛ ولا يرسل أي شيء إلى الهدف. استبدل التعليق بطلبك إلى الهدف، ثم أبلغ عن نتيجة إرسال تلك المهمة قبل أن تتولّى وحدة التنفيذ مهمة أخرى. لا تُضف الرموز إلى شريحة لاستخدامها بعد workers.Wait.

  • أرسل result.Solution.Token، وليس result.ID.
  • استخدم SuccessfulProxy مع المهمة الأصلية والنتيجة الجاهزة لتحديد الوكيل الناجح. أرسل عبر ذلك الوكيل باستخدام هوية المتصفح المُعادة في UserAgent وSecChUA.
  • أبقِ الإرسال ضمن سياق المهمة، قبل cancel(). عندئذ تشمل المهلة البالغة 180 ثانية الحل والإرسال معًا؛ استخدم سياق إرسال مستقلًا إذا احتجت إلى مهلة زمنية مستقلة.

أبلغ عن الحل والإرسال إلى الهدف كلًّا على حدة. توفّر المهمة الجاهزة حلًا قابلًا للاستخدام، لكن استجابة الهدف وحدها تخبرك ما إذا كان قد قبل طلب التطبيق.

استعادة مهمة انقطعت دون إعادة تشغيل الدفعة

يكفي تسجيل الخطأ في هذا المثال الصغير، لكن التكامل القابل للاستعادة ينبغي أن يحتفظ بمدخلات كل مهمة كما هي تمامًا، وبمعرّفات الطلب والمهمة المتاحة. انتهاء المهلة لا يثبت فشل إنشاء المهمة.

  • استخرج *solvethree.Error للحصول على RequestID وTaskID. إذا كان معرّف المهمة معروفًا، فاستأنف الاسترجاع باستخدام GetTask ضمن سياق جديد.
  • إذا لم يكن نجاح الإنشاء مؤكدًا وكان لديك معرّف الطلب، فأعِد إرسال المدخلات الأصلية باستخدام RequestID نفسه. يعيد الطلب نفسه المهمة الموجودة؛ أما تغيير المدخلات مع إعادة استخدام المعرّف فيؤدي إلى تعارض.
  • في حالة *TaskFailedError، سجّل ErrorCode واسترجع المهمة الفاشلة إذا احتجت إلى نص Error الخاص بها. أوقف الاستقصاء عن تلك المهمة وعالج السبب بدلًا من إعادة إرسال المدخلات غير الصالحة نفسها دون تحقق.

ميّز أخطاء HTTP عن حالات الفشل النهائي للمهام. يمكن لـ errors.As استخراج *APIError متداخل يتضمن StatusCode وRetryAfter. تعيد Solve بالفعل محاولة الاستقصاء عند الاستجابات المؤقتة ذات رموز HTTP 429 أو 502 أو 503 أو 504، مع مراعاة Retry-After ضمن المهلة النهائية للسياق. لا تُعاد محاولة إنشاء المهمة تلقائيًا مطلقًا.

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

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

إنشاء مساحة عمل
حل مهام CAPTCHA بالتزامن بلغة Go · solvethree