Использование Go SDK с chromedp

Автор: solvethree3 мин на чтение

Сначала получите решение, затем запустите Chrome с тем же прокси и возвращёнными идентификационными данными браузера. Вставьте токен в форму приложения, отправьте её и дождитесь результата приложения. Готовая задача означает, что у вас есть решение, а не то, что сайт его принял.

На этой странице

Настройте модули, профиль и прокси

Установите solvethree.com/[email protected] и github.com/chromedp/[email protected] с помощью go get. Для SDK требуется Go 1.22+, но для этой версии chromedp требуется Go 1.25+, поэтому используйте эту версию для совместного примера. Её типизированный API отличается от API в более старых примерах chromedp.

Перед запуском программы создайте в рабочем пространстве активный профиль сайта для целевого имени хоста. Отправка имени хоста и действия не создаёт профиль.

  • TARGET_URL: полный HTTPS URL страницы с вашей формой. Задача использует его имя хоста как Site; Chrome переходит по полному URL.
  • SOLVETHREE_PROXY: URL одного HTTP- или HTTPS-прокси. Для этого примера нужен прокси с доступом по списку разрешённых IP-адресов, без учётных данных в URL.
  • SOLVETHREE_API_KEY: API-ключ вашего рабочего пространства. Храните его в процессе Go, а не в JavaScript страницы.

Пример запрашивает действие login и использует стандартный режим reCAPTCHA Enterprise v3. Замените действие на значение, которое ожидает ваше приложение.

Получите решение, запустите Chrome и отправьте форму

Программа дожидается решения от SDK, прежде чем создавать аллокатор 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.

Адаптируйте пример к своей форме

При вставке токена перед построением JavaScript-выражения используется json.Marshal. Сохраните этот этап экранирования: он передаёт токен как строковое значение JavaScript, а не подставляет неэкранированный текст в исполняемый код.

  • Замените [name="g-recaptcha-response"] на поле, которое фактически отправляет ваше приложение. Пример задаёт его значение и генерирует событие input; адаптируйте это поведение, если приложению нужен другой способ отправки.
  • Замените button[type="submit"] на нужный элемент отправки формы. Перед нажатием добавьте все необходимые значения формы, поля CSRF и состояние сессии.
  • Замените #success на признак, который действительно подтверждает принятие запроса вашим приложением. Завершённое нажатие или заполненное поле токена таким признаком не является.

SDK не отправляет токен целевому сайту. Если сайт отклоняет запрос, сначала проверьте отправляемую форму: убедитесь, что она содержит result.Solution.Token, а не ID задачи или старое значение поля, и проверьте, что прокси и идентификационные данные браузера по-прежнему совпадают.

Обрабатывайте предельные сроки выполнения без дублирования решений

Для решения установлен предельный срок контекста в 180 секунд. После решения Chrome получает отдельный срок в 60 секунд. Функция run возвращает ошибки, поэтому отложенная отмена контекста и освобождение ресурсов браузера выполняются до того, как main запишет ошибку в журнал.

Отмена контекста решения прекращает ожидание, но не удалённую работу. SDK создаёт задачу один раз и повторяет только опрос при временных сбоях; POST-запрос создания он никогда не повторяет. Поэтому истечение времени ожидания не повод сразу отправлять новую задачу.

  • При отмене или истечении предельного срока используйте errors.Is, чтобы определить ошибку контекста. Извлеките *solvethree.Error с помощью errors.As и сохраните её TaskID и RequestID.
  • Если у вас есть ID задачи, получите её с помощью GetTask. Иначе восстановите её, повторно отправив в точности исходные входные данные задачи с тем же ID запроса. Повторный вызов без этого ID создаёт новый логический запрос; изменение входных данных при повторном использовании ID вызывает конфликт.
  • Для *TaskFailedError проверьте ErrorCode и получите завершившуюся с ошибкой задачу, чтобы прочитать текст ошибки. Эта задача окончательно завершена: прекратите её опрос и устраните причину вместо повторных попыток с неизменёнными входными данными.
  • При ошибке HTTP API извлеките вложенную *APIError, чтобы проверить StatusCode и RetryAfter. Не путайте HTTP-сбой с окончательным завершением задачи с ошибкой.

Запустите первую задачу.

Начните с $5 на балансе рабочего пространства.

Создать рабочее пространство
Использование Go SDK с chromedp · solvethree