שימוש ב-SDK של Go עם chromedp

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

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

בדף הזה

הגדרת המודולים, הפרופיל והפרוקסי

התקינו את solvethree.com/[email protected] ואת github.com/chromedp/[email protected] באמצעות go get. ה-SDK דורש Go 1.22+, אבל גרסה זו של chromedp דורשת Go 1.25+, ולכן השתמשו בה עבור הדוגמה המשולבת. ה-API שלה בעל הטיפוסים שונה מזה שבדוגמאות ישנות יותר של chromedp.

צרו בסביבת העבודה שלכם פרופיל אתר פעיל עבור שם המארח של היעד לפני הפעלת התוכנית. שליחת שם מארח ופעולה אינה יוצרת פרופיל.

  • TARGET_URL: כתובת ה-HTTPS המלאה של הדף שמכיל את הטופס שלכם. המשימה משתמשת בשם המארח שלה בתור Site; ‏Chrome מנווט לכתובת המלאה.
  • SOLVETHREE_PROXY: כתובת URL של פרוקסי HTTP או HTTPS יחיד. דוגמה זו דורשת פרוקסי עם רשימת הרשאה לפי כתובות IP, ללא פרטי הזדהות בכתובת ה-URL.
  • SOLVETHREE_API_KEY: מפתח ה-API של סביבת העבודה שלכם. שמרו אותו בתהליך Go, ולא ב-JavaScript של הדף.

הדוגמה מבקשת את הפעולה login ומשתמשת במצב ברירת המחדל Enterprise v3 של reCAPTCHA. שנו את הפעולה לערך שהיישום שלכם מצפה לו.

פתרון, הפעלת Chrome ושליחה

התוכנית ממתינה לפתרון של ה-SDK לפני יצירת ה-allocator של Chrome. היא מגדירה את Chrome עם אותו פרוקסי יחיד ועם result.Solution.UserAgent, ואז מגדירה את SecChUA שהוחזר ככותרת sec-ch-ua לפני הניווט. אל תחליפו את זהות הדפדפן בזהות לא קשורה ואל תחליפו פרוקסי בין הפתרון לשליחה.

chromedp · שליחה דרך פרוקסי המורשה לפי כתובת IP
// go get github.com/chromedp/[email protected] (requires Go 1.25+)
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/url"
    "os"
    "time"
    "github.com/chromedp/cdproto/network"
    "github.com/chromedp/chromedp"
    solvethree "solvethree.com/sdk"
)

func main() { if err := run(); err != nil { log.Fatal(err) } }

func run() error {
    target,err:=url.Parse(os.Getenv("TARGET_URL"));if err!=nil||target.Scheme!="https"||target.Hostname()=="" { return fmt.Errorf("set an HTTPS TARGET_URL") }
    proxy,err:=url.Parse(os.Getenv("SOLVETHREE_PROXY"));if err!=nil||proxy.User!=nil { return fmt.Errorf("use an IP-allowlisted proxy without URL credentials") }
    client,err:=solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"),nil);if err!=nil { return err }
    ctx,cancel:=context.WithTimeout(context.Background(),180*time.Second);defer cancel()
    result,err:=client.Solve(ctx,solvethree.TaskRequest{Site:target.Hostname(),Action:"login",Proxy:proxy.String()},solvethree.SolveOptions{})
    if err!=nil { return err }
    options:=append(chromedp.DefaultExecAllocatorOptions[:],chromedp.ProxyServer(proxy.String()),chromedp.UserAgent(result.Solution.UserAgent))
    allocator,closeAllocator:=chromedp.NewExecAllocator(context.Background(),options...);defer closeAllocator()
    browser,closeBrowser:=chromedp.NewContext(allocator);defer closeBrowser()
    browser,stop:=context.WithTimeout(browser,60*time.Second);defer stop()
    tokenJSON,err:=json.Marshal(result.Solution.Token);if err!=nil { return err }
    script:=`(token => {const field=document.querySelector('[name="g-recaptcha-response"]'); field.value=token; field.dispatchEvent(new Event('input',{bubbles:true}));})(`+string(tokenJSON)+`)`
    _,err=chromedp.Call(browser,network.SetExtraHTTPHeaders,network.SetExtraHTTPHeadersParams{Headers:network.Headers{"sec-ch-ua":result.Solution.SecChUA}})
    if err!=nil { return err }
    err=chromedp.Do(browser,
        chromedp.Navigate(target.String()),chromedp.WaitReady(chromedp.CSS(`[name="g-recaptcha-response"]`)),
        chromedp.Evaluate[chromedp.Void](script),chromedp.Click(chromedp.CSS(`button[type="submit"]`)),
        chromedp.WaitVisible(chromedp.ID("success")))
    if err!=nil { return err }
    // #success is this owned demo form's outcome. Adapt its selectors, token
    // parameter and success check to your application, including CSRF/session fields.
    return nil
}

דוגמה זו משתמשת במכוון בפרוקסי אחד, ולכן אין צורך לברר איזה מועמד נבחר. אם בהמשך תשתמשו ב-ProxyCandidates, קבלו את הפרוקסי שהצליח באמצעות SuccessfulProxy(originalTask, readyResult) והשתמשו בו עבור Chrome.

התאמת הנחות טופס ההדגמה

הכנסת הטוקן משתמשת ב-json.Marshal לפני בניית ביטוי ה-JavaScript. שמרו על שלב ההימלטות הזה: הוא מעביר את הטוקן כערך מחרוזת של JavaScript במקום לשלב טקסט שלא עבר הימלטות בקוד בר-ביצוע.

  • החליפו את [name="g-recaptcha-response"] בשדה שהיישום שלכם שולח בפועל. הדוגמה מגדירה את ערכו ומשגרת אירוע קלט; התאימו זאת אם היישום שלכם דורש מסלול שליחה אחר.
  • החליפו את button[type="submit"] בפקד השליחה הרצוי. כללו את כל ערכי הטופס, שדות ה-CSRF ומצב הסשן הנדרשים לפני הלחיצה.
  • החליפו את #success בתוצאה שבאמת מעידה על קבלה ביישום שלכם. לחיצה שהושלמה או שדה טוקן שמולא אינם תוצאה כזו.

ה-SDK אינו שולח את הטוקן ליעד. אם היעד דוחה את הבקשה, בדקו תחילה את הטופס היוצא: ודאו שהוא מכיל את result.Solution.Token, ולא את מזהה המשימה או ערך שדה ישן, ובדקו שהפרוקסי וזהות הדפדפן עדיין תואמים.

טיפול במועדים סופיים ללא יצירת בקשות פתרון כפולות

לפעולת הפתרון מוגדר מועד סופי של 180 שניות ב-context. ל-Chrome מוקצה מועד סופי נפרד של 60 שניות לאחר הפתרון. הפונקציה run מחזירה שגיאות, כך שביטול ה-context וניקוי משאבי הדפדפן שהוגדרו לביצוע דחוי מתבצעים לפני ש-main רושמת שגיאה ביומן.

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

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

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

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

יצירת סביבת עבודה
שימוש ב-SDK של Go עם chromedp · solvethree