chromedpでGo SDKを使用する
まず解決処理を行い、同じプロキシと返されたブラウザー識別情報を使って Chrome を起動します。アプリケーションのフォームにトークンを挿入して送信し、アプリケーションの結果を待ちます。タスクが ready になったことは、解決結果が得られたことを意味し、ウェブサイトがそれを受け入れたことを意味するわけではありません。
このページの内容
モジュール、プロファイル、プロキシを設定する
go get で solvethree.com/[email protected] と github.com/chromedp/[email protected] をインストールします。SDK は Go 1.22+ を必要としますが、このバージョンの chromedp は Go 1.25+ を必要とするため、両方を使うこの例ではそちらを使用してください。このバージョンの型付き API は、古い chromedp のサンプルとは異なります。
プログラムを実行する前に、ワークスペースで対象ホスト名の有効なサイトプロファイルを作成してください。ホスト名とアクションを送信しても、プロファイルは作成されません。
TARGET_URL: フォームを含むページの完全な HTTPS URL。タスクはそのホスト名をSiteとして使用し、Chrome は完全な URL に移動します。SOLVETHREE_PROXY: 単一の HTTP または HTTPS プロキシ URL。この例では、URL に認証情報を含まない、IP 許可リスト方式のプロキシが必要です。SOLVETHREE_API_KEY: ワークスペースの API キー。ページの JavaScript ではなく、Go プロセス内に保持してください。
この例は login アクションを要求し、デフォルトの Enterprise v3 reCAPTCHA モードを使用します。アクションは、アプリケーションが期待する値に変更してください。
解決して Chrome を起動し、送信する
プログラムは SDK の解決処理が完了するのを待ってから、Chrome のアロケーターを作成します。同じ単一のプロキシと result.Solution.UserAgent を Chrome に設定し、移動前に、返された SecChUA を sec-ch-ua ヘッダーに設定します。無関係なブラウザー識別情報に置き換えたり、解決から送信までの間にプロキシを切り替えたりしないでください。
// 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
}この例では意図的にプロキシを 1 つだけ使用しているため、候補の選択結果を確認する必要はありません。後で ProxyCandidates を使用する場合は、SuccessfulProxy(originalTask, readyResult) で成功したプロキシを取得し、そのプロキシを Chrome に使用してください。
デモフォームの前提を置き換える
トークンの挿入では、JavaScript 式を構築する前に json.Marshal を使用しています。このエスケープ処理は残してください。エスケープされていないテキストを実行可能なコードに埋め込むのではなく、トークンを JavaScript の文字列値として渡すための処理です。
[name="g-recaptcha-response"]を、アプリケーションが実際に送信するフィールドに置き換えてください。サンプルでは、その値を設定して input イベントを発火させます。アプリケーションが別の送信経路を必要とする場合は、それに合わせて変更してください。button[type="submit"]を、使用する送信コントロールに置き換えてください。クリックする前に、必要なフォーム値、CSRF フィールド、セッション状態をすべて設定してください。#successを、アプリケーションで受け入れを確実に示す結果に置き換えてください。クリックの完了やトークンフィールドへの値の設定は、その結果ではありません。
SDK はトークンを対象サイトに送信しません。対象サイトがリクエストを拒否した場合は、まず送信するフォームを確認してください。タスク ID や以前のフィールド値ではなく result.Solution.Token が含まれていることと、プロキシとブラウザー識別情報が引き続き一致していることを確認します。
解決処理を重複させずに期限に対処する
解決処理のコンテキストには 180 秒の期限があります。Chrome には解決後、別途 60 秒の期限が設定されます。run 関数はエラーを返すため、main がエラーをログに記録する前に、遅延実行されるコンテキストのキャンセルとブラウザーのクリーンアップが行われます。
解決処理のコンテキストをキャンセルすると待機は停止しますが、リモートの処理は停止しません。SDK は作成を 1 回だけ行い、一時的なポーリング失敗のみを再試行します。作成用 POST を再試行することはありません。したがって、タイムアウトしたからといって、すぐに新しいタスクを送信すべきではありません。
- キャンセルや期限切れの場合は、
errors.Isでコンテキストのエラーを識別します。errors.Asで*solvethree.Errorを取り出し、そのTaskIDとRequestIDを保持してください。 - タスク ID がある場合は、
GetTaskでタスクを取得します。ない場合は、同じリクエスト ID で元のタスク入力を完全に同じ内容で再送して復元します。その ID を指定せずに再度呼び出すと、新しい論理リクエストが作成されます。同じ ID を再利用しながら入力を変更すると、競合が発生します。 *TaskFailedErrorの場合は、ErrorCodeを確認し、失敗したタスクを取得してエラーテキストを確認してください。そのタスクは終端状態です。ポーリングを停止し、入力を変えずに再試行するのではなく、原因を修正してください。- HTTP API エラーの場合は、ネストされた
*APIErrorを取り出し、StatusCodeとRetryAfterを確認してください。HTTP の失敗と、終端状態となったタスクの失敗を混同しないでください。
参照元とAPIリファレンス
最初のタスクを実行しましょう。
$5のワークスペースクレジットで始められます。