Scarnox · Blog

Solver captcha di Java dengan HttpClient: contoh kerja (2026)

Solver captcha di Java hanya membutuhkan satu panggilan HTTP. Java 11 dan yang lebih baru sudah menyertakan java.net.http.HttpClient, jadi satu-satunya dependensi yang dibutuhkan panduan ini adalah Gson, untuk JSON. Anda mengirim sitekey dan URL halaman ke API Scarnox, membaca solution.token, lalu mengirim token ke form situs. Panduan ini membahas PopularCaptcha, yang saat ini di-solve Scarnox secara proxyless.

Mulai dengan kredit uji coba gratis, tanpa kartu dan coba kodenya di halaman yang Anda kontrol sebelum memakainya di tempat lain.

Kebutuhan

Class solver

Class ini membungkus satu HttpClient yang menyimpan cookie, sehingga sesi bisa terbawa sampai pengiriman form. Metode solve mengatur header Bearer, mengirim body JSON, dan memeriksa errorId:

// ScarnoxSolver.java
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;

public class ScarnoxSolver {
    private static final String API_URL = "https://api.scarnox.com/api/tasks/create";
    private static final Gson GSON = new Gson();

    private final String apiKey;
    private final HttpClient http = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ALL))
            .build();

    public ScarnoxSolver(String apiKey) {
        this.apiKey = apiKey;
    }

    /** Mengembalikan token. Panggilan ini menunggu sampai token siap. */
    public String solve(String sitekey, String pageUrl, boolean invisible) throws Exception {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("captcha_type", "PopularCaptchaTaskProxyless");
        body.put("site_key", sitekey);
        body.put("site_url", pageUrl);
        if (invisible) {
            body.put("is_invisible", true);
        }

        HttpRequest request = HttpRequest.newBuilder(URI.create(API_URL))
                .timeout(Duration.ofSeconds(185))
                .header("Authorization", "Bearer " + apiKey)
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(GSON.toJson(body)))
                .build();

        HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());

        JsonObject data;
        try {
            data = JsonParser.parseString(response.body()).getAsJsonObject();
        } catch (RuntimeException e) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": not JSON");
        }

        if (data.get("errorId").getAsInt() != 0) {
            throw new IllegalStateException(
                    data.get("errorCode").getAsString() + ": "
                    + data.get("errorDescription").getAsString());
        }

        return data.getAsJsonObject("solution").get("token").getAsString();
    }

    /** Mengirim token ke form situs dan mengembalikan status HTTP. */
    public int submit(String pageUrl, String email, String token) throws Exception {
        // Muat halaman dulu agar cookie sesi ter-set.
        http.send(HttpRequest.newBuilder(URI.create(pageUrl)).GET().build(),
                HttpResponse.BodyHandlers.discarding());

        String form = "email=" + enc(email)
                + "&h-captcha-response=" + enc(token)
                + "&g-recaptcha-response=" + enc(token);

        HttpRequest request = HttpRequest.newBuilder(URI.create(pageUrl))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(form))
                .build();

        return http.send(request, HttpResponse.BodyHandlers.discarding()).statusCode();
    }

    private static String enc(String value) {
        return URLEncoder.encode(value, StandardCharsets.UTF_8);
    }
}

Header Authorization memakai awalan Bearer. Timeout request adalah 185 detik, karena API memblokir sampai token siap. Tidak ada langkah polling.

Program utama

Method main melakukan solve dengan beberapa retry, lalu mengirim token:

// Main.java
public class Main {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("SCARNOX_API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalStateException("Set SCARNOX_API_KEY");
        }

        String pageUrl = "https://example.com/signup";
        String sitekey = "YOUR-SITEKEY-UUID";
        ScarnoxSolver solver = new ScarnoxSolver(apiKey);

        String token = null;
        for (int attempt = 1; attempt <= 3 && token == null; attempt++) {
            try {
                token = solver.solve(sitekey, pageUrl, false);
            } catch (IllegalStateException e) {
                System.err.println("Attempt " + attempt + " failed: " + e.getMessage());
                Thread.sleep(attempt * 1000L);
            }
        }
        if (token == null) {
            throw new IllegalStateException("No token after 3 attempts");
        }

        int status = solver.submit(pageUrl, "you@example.com", token);
        System.out.println("Status: " + status);
    }
}

Jalankan:

export SCARNOX_API_KEY=scarnox_YOURAPIKEY
javac -cp gson-2.10.1.jar *.java
java -cp .:gson-2.10.1.jar Main

Mengapa strukturnya seperti ini

Satu client per solver. HttpClient aman dipakai lintas thread dan memang dirancang untuk dipakai ulang. Membuatnya untuk setiap request hanya membuang koneksi.

Cookie aktif. CookieManager pada client menyimpan sesi yang memuat form, sehingga token dikirim bersama cookie yang diharapkan situs.

Pengecekan error yang eksplisit. API mengembalikan errorId bernilai 0 saat berhasil. Nilai lain menyertakan errorCode dan errorDescription, dan solver melempar exception dengan teks tersebut. Token ada di solution.token.

Kedua field captcha. Form menerima h-captcha-response dan g-recaptcha-response. Kebanyakan situs membaca salah satunya.

Waktu

Token kedaluwarsa dalam sekitar dua menit. Lakukan solve tepat sebelum pengiriman, jangan di awal job yang panjang. Masa berlaku token captcha menjelaskan cara menentukan ukuran jendela waktunya dan membedakan token yang kedaluwarsa dari token yang ditolak.

Widget invisible

Berikan true sebagai argumen ketiga ke solve saat widgetnya invisible. Request kemudian menyertakan is_invisible: true. Jika halaman memerlukan callback JavaScript dengan token, client sisi server tidak bisa memicunya, dan alat browser automation adalah pilihan yang lebih baik. Panduan captcha invisible mencantumkan polanya.

Penanganan error

Retry menangani kegagalan sementara. Hentikan retry untuk error yang tidak akan berubah, seperti sitekey yang salah atau URL halaman yang tidak menjalankan widget, dan catat untuk ditinjau. Kebijakan lengkapnya, termasuk kode mana yang harus dihentikan, ada di penanganan error solver captcha.

Scraping secara bertanggung jawab

Jika program ini melakukan scraping pada halaman publik, cek robots.txt dan terms situs terlebih dahulu. Tambahkan jeda di antara request, dan simpan hanya data pribadi yang Anda butuhkan. Untuk latar belakang hukumnya, lihat apakah web scraping legal.

Harga

Anda membayar per solve yang berhasil, dan solve yang gagal tidak dikenai biaya. Lihat harga terkini untuk tarifnya. Untuk memulai, mulai dengan kredit uji coba gratis, tanpa kartu.

Langkah selanjutnya