פתרון משימות CAPTCHA במקביל ב-Go

מאת solvethree3 דקות קריאה

השתמשו בלקוח Go SDK משותף אחד ובמאגר קבוע של workers. הקצו לכל עבודה context משלה, ושלחו כל פתרון מיד כשהוא מוכן במקום לאסוף טוקנים לאצווה מאוחרת יותר.

בדף הזה

הכנת הלקוח והיעד

  • השתמשו ב-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 משתמשות בשם המארח של האתר ובפעולה, ולא בשדות האלה.

הגבילו את העבודה, לא את מספר הלקוחות

הלקוח בטוח לשימוש מקבילי. הדוגמה מפעילה ארבעה workers ומעבירה להם שמונה עבודות דרך ערוץ ללא חוצץ. כשכל ה-workers עסוקים, היצרן ממתין במקום להפעיל 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()
}

כל worker יוצר context נפרד של 180 שניות לכל עבודה. Solve יוצר את המשימה פעם אחת וממתין לתוצאה שלה. מכיוון שהדוגמה אינה מציינת RequestID, ה-SDK מייצר מזהה עבור היצירה הזו. קריאה שנכשלה נרשמת ביומן, ו-continue מעביר רק את אותו worker לעבודה הבאה שלו; הוא אינו עוצר את האצווה.

השאירו את הביטול בתוך הלולאה, כפי שמוצג, כדי שכל עבודה תשחרר בהקדם את משאבי ה-context שלה. הביטול מפסיק את ההמתנה המקומית; הוא אינו מבטל עבודה מרוחקת.

טפלו בפתרון לפני קבלת עבודה נוספת

הדוגמה מדפיסה את מזהה המשימה המוכנה; היא אינה שולחת דבר ליעד. החליפו את ההערה בבקשה ליעד שלכם, ואז דווחו על תוצאת השליחה של אותה עבודה לפני שה-worker מקבל עבודה נוספת. אל תוסיפו טוקנים ל-slice לשימוש לאחר workers.Wait.

  • שלחו את result.Solution.Token, לא את result.ID.
  • השתמשו ב-SuccessfulProxy עם המשימה המקורית ועם התוצאה המוכנה כדי לזהות את הפרוקסי שהצליח. שלחו דרך הפרוקסי הזה עם זהות הדפדפן שהוחזרה ב-UserAgent וב-SecChUA.
  • בצעו את השליחה במסגרת ה-context של העבודה, לפני cancel(). כך מגבלת הזמן של 180 שניות מכסה גם את הפתרון וגם את השליחה; השתמשו ב-context נפרד לשליחה אם דרוש לכם תקציב זמן נפרד.

דווחו בנפרד על הפתרון ועל השליחה ליעד. משימה מוכנה מספקת פתרון שמיש, אבל רק תגובת היעד מעידה אם הוא קיבל את בקשת היישום.

שחזור עבודה שנקטעה בלי להפעיל מחדש את האצווה

רישום שגיאה ביומן מספיק לדוגמה הקטנה הזו, אבל אינטגרציה שתומכת בשחזור צריכה לשמור את קלטי המשימה המדויקים של כל עבודה ואת מזהי הבקשה והמשימה הזמינים. חריגה ממגבלת הזמן אינה מוכיחה שיצירת המשימה נכשלה.

  • חלצו את *solvethree.Error כדי לקבל את RequestID ואת TaskID. אם מזהה המשימה ידוע, חדשו את אחזור המשימה באמצעות GetTask עם context חדש.
  • אם לא ברור אם המשימה נוצרה ויש לכם את מזהה הבקשה, שלחו שוב את הקלטים המקוריים עם אותו RequestID. אותה בקשה מחזירה את המשימה הקיימת; שינוי הקלטים תוך שימוש חוזר במזהה גורם להתנגשות.
  • במקרה של *TaskFailedError, שמרו את ErrorCode ואחזרו את המשימה שנכשלה אם אתם זקוקים לטקסט שב-Error שלה. הפסיקו לתשאל את המשימה הזו וטפלו בגורם לכישלון במקום לשלוח שוב באופן עיוור את אותם קלטים לא תקינים.

הבחינו בין שגיאות HTTP לבין כישלונות סופיים של משימות. errors.As יכול לחלץ *APIError מקונן עם StatusCode ו-RetryAfter. Solve כבר מנסה מחדש תשאולים שהחזירו תגובות זמניות עם HTTP 429, 502, 503 או 504, תוך כיבוד Retry-After במסגרת מגבלת הזמן של ה-context. יצירת משימה לעולם אינה מתבצעת שוב אוטומטית.

הפעלת המשימה הראשונה שלך.

התחילו עם $5 בקרדיט לסביבת העבודה.

יצירת סביבת עבודה
פתרון משימות CAPTCHA במקביל ב-Go · solvethree