C# ve HttpClient ile DataDome CAPTCHA Nasıl Çözülür

datadome captcha in c# - How to Solve DataDome CAPTCHA in C# With HttpClient

C#’ta bir DataDome CAPTCHA’sını çözmek için 403 engelleme sayfasını okuyup, ondan challenge adresini oluşturup, bu adresi CapSkip DataDome çözücüsü üzerinden DataDomeAsync ile gönderip, döndürdüğü çerezle isteği HttpClient üzerinden yinelersiniz. Yanıt bir çerezdir, bir form için token değildir ve CapSkip belgeleri, çerezin yalnızca kazanıldığı User-Agent ve IP adresiyle çalıştığını söyler. Bu rehber, bütün döngüyü tek bir konsol projesinde CapSkip NuGet paketiyle ve başka hiçbir şey olmadan çalıştırır. CapSkip, çözüm başına ücret olmadan kendi Windows bilgisayarınızda çalışır; bu yüzden bir yeniden deneme hiçbir şeye mal olmaz.

Neye ihtiyacınız var

  • Bir Windows bilgisayarında çalışan CapSkip 3.2.0 veya üzeri. Bu sürüm, kaydırıcı ve cihaz kontrolü için DataDome çözme desteğini ekledi. İlk başlatmayı kurulum kılavuzu adım adım anlatır.
  • Kurulu bir .NET 8 SDK veya üzeri ve CapSkip NuGet paketinin 1.5.0 veya üzeri sürümü. Daha eski sürümlerde DataDomeAsync metodu yoktur. Aşağıdaki kod .NET SDK 10.0.100 üzerinde, CapSkip 1.5.0 ile net10.0 ve net8.0 hedeflerinde çalıştı.
  • Bir PowerShell istemi. Komutlar PowerShell sözdizimini kullanır.
  • Sahibi olduğunuz ya da otomatikleştirmenize izin verilen, DataDome arkasındaki bir sayfa.
  • Çözücü için bir adres. Local modunda CapSkip 127.0.0.1 üzerinden yalnızca o cihaza yanıt verir. Server modunda ağ adresinizi veya genel IP’nizi dinler, böylece başka bir bilgisayardaki, bir VPS’teki veya barındırılan bir platformdaki program API üzerinden ona erişebilir. İkisi de şurada ayarlanır: bağlantı ayarları.
  • Test aşamasını geçtiğinizde görev başına sabit IP’li (sticky) bir proxy. Çerez, onu kazanan IP adresine bağlıdır; bunu 3. Adım açıklar.

Nasıl test edildi: kod, gerçek bir DataDome sitesine karşı değil, benim yazdığım üç sahte (mock) sunucuya karşı çalıştırıldı. Birincisi CapSkip API belgeleri içindeki DataDome bölümünde listelenen denetimleri kopyalar; ikincisi 403 engelleme sayfası veren ve yalnızca CapSkip’in verdiği bir çerezi, verildiği User-Agent ile birlikte kabul eden korumalı bir sitenin, üçüncüsü ise bir forward proxy’nin yerini tutar. SDK, NuGet’taki gerçek CapSkip 1.5.0 paketiydi. Bilgisayarımda CapSkip uygulaması çalışmıyordu, bu yüzden buradaki hiçbir şey gerçek bir çözümün ne kadar sürdüğünü ya da canlı bir DataDome sitesinin neyi kabul ettiğini göstermez. Çerezle ilgili kurallar CapSkip belgelerinden gelir. DataDome’nin kendi çerez belgeleri datadome çerezinin bir yıl geçerli olduğunu söyler ve Domain ya da Path değerinin değiştirilmesine karşı uyarır. Çerezin neye bağlı olduğunu söylemez.

1. Adım: projeyi oluşturun ve engelleme sayfasını okuyun

Bir konsol projesi oluşturun ve paketi ekleyin. Varsayılan şablon örtük using ifadelerini ve nullable türleri açar ve kod ikisine de dayanır.

dotnet new console -o datadome-demo
cd datadome-demo
dotnet add package CapSkip

DataDome bir isteğe şüpheyle yaklaştığında site, içerik yerine 403 durumuyla (bazen 401) ve küçük bir HTML sayfasıyla yanıt verir. Bu sayfa dd adlı bir script nesnesi barındırır ve challenge’ı geo.captcha-delivery.com üzerinden bir iframe içinde yükler. CapSkip’in ihtiyaç duyduğu şey iframe adresidir ve belgeler ona captcha_url der. Sayfada iframe zaten varsa src değerini olduğu gibi alabilirsiniz. Çoğu engelleme sayfası iframe’i bunun yerine bir script ile oluşturur; bu yüzden ilk blok aynı adresi dd nesnesinden derler. İlk istek sade bir Chrome User-Agent ile gider. Bu dize CapSkip’e hiç gönderilmez, bu yüzden Chrome sürümünün hiçbir şeyle eşleşmesi gerekmez; canlı bir DataDome sitesinin farklı bir User-Agent’a aynı challenge’ı verip vermediğini ise test etmedim. Program.cs dosyasının içeriğini bu rehberin dört bloğuyla değiştirin: art arda, bununla başlayarak.

rt değeri yolu belirler. c, /captcha/ altındaki kaydırıcı; i ise /interstitial/ altındaki cihaz kontrolü anlamına gelir. t değeri de önemlidir: fe bir challenge, bv ise sayfayı çeken IP adresini DataDome’nin yasakladığı anlamına gelir.

using System.Net;
using System.Text.Json;
using System.Text.RegularExpressions;
using CapSkip;

static class BlockPage
{
    // Build the challenge address from a DataDome block page, or return null.
    public static string? CaptchaUrl(HttpStatusCode status, string html, string pageUrl)
    {
        if (status != HttpStatusCode.Forbidden && status != HttpStatusCode.Unauthorized) return null;
        var found = Regex.Match(html, @"var\s+dd\s*=\s*(\{[^}]*\})");
        if (!found.Success) return null;

        using var doc = JsonDocument.Parse(found.Groups[1].Value.Replace('\'', '"'));
        var dd = doc.RootElement;
        string? Get(string name) => dd.TryGetProperty(name, out var v)
            ? (v.ValueKind == JsonValueKind.String ? v.GetString() : v.GetRawText())
            : null;

        var query = new List<(string Key, string? Value)>
        {
            ("initialCid", Get("cid")), ("hash", Get("hsh")), ("cid", Get("cookie")),
        };
        if (!string.IsNullOrEmpty(Get("t"))) query.Add(("t", Get("t")));  // fe challenge, bv banned
        query.Add(("referer", pageUrl));
        query.Add(("s", Get("s")));
        foreach (var key in new[] { "e", "b" })
            if (!string.IsNullOrEmpty(Get(key))) query.Add((key, Get(key)));
        query.Add(("dm", "cd"));

        var path = Get("rt") == "i" ? "interstitial" : "captcha";
        var qs = string.Join("&", query.Select(p => p.Key + "=" + Uri.EscapeDataString(p.Value ?? "")));
        return $"https://{Get("host") ?? "geo.captcha-delivery.com"}/{path}/?{qs}";
    }
}

Metot, engelleme sayfası olmayan her yanıt için null döndürür; böylece çağıran “çözülecek bir şey yok” ile “çözüldü” durumunu ayırt edebilir. Her engelleme sayfası yeni bir challenge getirir. Adresini hemen kullanın ve sonraki görev için eski birini yeniden kullanmak yerine sayfayı yeniden isteyin.

2. Adım: DataDomeAsync ile çözün

SDK metodu DataDomeAsync(captchaUrl, url, options) biçimindedir ve 2Captcha argüman sırasını izler. İki adres konumsaldır; userAgent ve proxy ise options sözlüğüne girer. İkisi de CapSkip’te isteğe bağlıdır. Bir User-Agent göndermezseniz CapSkip kendi tarayıcısınınkini kullanır ve sonuçta UserAgent olarak döndürür. Gönderirseniz Windows için Chrome olmalı ve CapSkip’in tarayıcısıyla aynı ana sürüme sahip olmalıdır; bu yüzden onu hiç vermemek kolay seçimdir. CapSkip challenge’ı kendi çalıştırdığı gerçek bir tarayıcıda geçtiği için metot, reCAPTCHA’nın kullandığı daha uzun 300 saniyelik zaman aşımıyla bekler. API belgeleri bir çözümün genellikle 3 ila 20 saniye sürdüğünü söyler; ben bir tane ölçmedim.

Proxy, sonraki isteklerinizin kullanacağı proxy’dir. Programınız bir proxy üzerinden çıkıyorsa onu verin; istekler CapSkip’i çalıştıran makineden çıkıyorsa hiçbir şey vermeyin. CapSkip bir sunucuda çalışıyorsa o makine sunucudur. Proxy sınıfı türü ve adresi alır; örneğin HTTP ve user:[email protected]:8080.

static class Solve
{
    // Local mode. In Server mode, use your server's address instead.
    static readonly CapSkipClient Solver = new(host: "127.0.0.1", port: 8080);

    public static Task<SolveResult> Run(string captchaUrl, string pageUrl, string? proxyUrl)
    {
        var options = new Dictionary<string, object?>();
        if (proxyUrl != null)
        {
            var i = proxyUrl.IndexOf("://", StringComparison.Ordinal);
            options["proxy"] = new Proxy(proxyUrl[..i].ToUpperInvariant(), proxyUrl[(i + 3)..]);
        }
        // No userAgent option: CapSkip uses its own browser's and reports it.
        return Solver.DataDomeAsync(captchaUrl, pageUrl, options);
    }
}

Cihaz kontrolü geçilemezse DataDome kaydırıcıya geçer. CapSkip belgelerine göre tek çağrı onu oraya kadar izler, bu yüzden iki challenge için ayrı kod yazmanız gerekmez.

3. Adım: çerezi User-Agent ve IP adresiyle birlikte gönderin

Sonuç, önemli olan dört özelliği bulunan bir SolveResult nesnesidir. Sonucun Code özelliği, çerezin DataDome tarafından ayarlandığı hâliyle Max-Age, Domain ve diğer özniteliklerle birlikte tamamıdır; bir çerez deposunun istediği budur. Cookie özelliği yalnızca datadome ad ve değer çiftidir; bir Cookie başlığının istediği budur. UserAgent, onu kazanan User-Agent’tır. Proxy özelliği yalnızca siz proxy göndermediyseniz ve CapSkip kendi DataDome proxy listesi üzerinden çözdüyse ayarlanır. CapSkip belgeleri, DataDome’nin çerezi başka bir IP adresinden ya da başka bir User-Agent ile reddettiğini söyler; bu yüzden her seferinde aynı User-Agent’ı gönderin ve sonuç bir proxy adlandırıyorsa isteği onun üzerinden gönderin. Bu Proxy özelliği yalnızca host ve porttur; şema ve giriş bilgisi yoktur. Program http:// ekler ve bu yüzden girişi gerekmeyen bir liste proxy’si varsayar. Sizinkinde giriş varsa onu ekleyin.

Aşağıdaki yardımcı Cookie başlığını elle ayarlar ve UseCookies özelliğini kapatır; böylece HttpClient onu yazıldığı gibi gönderir. Ayrıca yanıttan yerine geçen bir çerezi de okur, çünkü DataDome sonraki bir yanıtta bir tane gönderebilir ve bir tarayıcı onu tutardı. Kısa kalması için çağrı başına bir HttpClient oluşturur. Çok sayıda istek için bunun yerine proxy başına bir istemci tutun.

static class Web
{
    static HttpClient Client(string? proxyUrl)
    {
        // UseCookies is off, so the Cookie header set by hand goes out as it is.
        var handler = new HttpClientHandler { UseCookies = false };
        if (proxyUrl != null)
        {
            var uri = new Uri(proxyUrl);
            var proxy = new WebProxy($"{uri.Scheme}://{uri.Host}:{uri.Port}");
            if (uri.UserInfo.Length > 0)
            {
                var login = uri.UserInfo.Split(':', 2);
                proxy.Credentials = new NetworkCredential(
                    Uri.UnescapeDataString(login[0]),
                    login.Length > 1 ? Uri.UnescapeDataString(login[1]) : "");
            }
            handler.Proxy = proxy;
        }
        return new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };
    }

    // Returns the status, the body and a replacement datadome cookie if the site sent one.
    public static async Task<(HttpStatusCode Status, string Body, string? Fresh)> Get(
        string url, string userAgent, string? cookie, string? proxyUrl)
    {
        using var http = Client(proxyUrl);
        using var request = new HttpRequestMessage(HttpMethod.Get, url);
        request.Headers.TryAddWithoutValidation("User-Agent", userAgent);
        if (cookie != null) request.Headers.TryAddWithoutValidation("Cookie", cookie);
        using var response = await http.SendAsync(request);

        string? fresh = null;
        if (response.Headers.TryGetValues("Set-Cookie", out var lines))
            fresh = lines.Select(l => Regex.Match(l, "^datadome=[^;]+"))
                         .FirstOrDefault(m => m.Success)?.Value;
        return (response.StatusCode, await response.Content.ReadAsStringAsync(), fresh);
    }
}

4. Adım: tam çalışan örnek

Son blok, C#’ta DataDome CAPTCHA çözme akışını bir araya getirir ve yeniden denemeyi ekler. dotnet run ile çalıştırın. Her deneme sayfayı yeniden ister, bu yüzden her seferinde yeni bir challenge gelir. Başarısız bir çözüm ApiException fırlatır ve döngü yeniden dener. Buna rağmen 403 alan bir çerez de döngüyü yeni engelleme sayfasıyla başa döndürür.

static class Program
{
    const string PageUrl = "https://www.example.com/";  // a page behind DataDome
    const string Browser = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
                           "(KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36";
    // The proxy your requests use, e.g. "http://user:[email protected]:8080". Null means
    // they leave from the PC that runs CapSkip.
    static readonly string? ProxyUrl = null;

    static async Task<int> Main()
    {
        for (var attempt = 1; attempt <= 3; attempt++)
        {
            var (status, body, _) = await Web.Get(PageUrl, Browser, null, ProxyUrl);  // fresh block page
            var captchaUrl = BlockPage.CaptchaUrl(status, body, PageUrl);
            if (captchaUrl is null)
            {
                Console.WriteLine($"{(int)status}: no DataDome block page on this response");
                return 0;
            }

            SolveResult result;
            try { result = await Solve.Run(captchaUrl, PageUrl, ProxyUrl); }
            catch (ApiException ex)
            {
                Console.WriteLine($"attempt {attempt}: {ex.Message}");  // the next try gets a new page
                continue;
            }

            // The cookie works only from the address and User-Agent that earned it.
            var via = ProxyUrl ?? (result.Proxy is null ? null : "http://" + result.Proxy);
            var cookie = result.Cookie;
            var (code, page, fresh) = await Web.Get(PageUrl, result.UserAgent!, cookie, via);
            if (code != HttpStatusCode.OK)
            {
                Console.WriteLine($"attempt {attempt}: HTTP {(int)code} with the cookie");
                continue;
            }
            cookie = fresh ?? cookie;  // DataDome may rotate the cookie; keep the newest
            Console.WriteLine($"{(int)code} {page[..Math.Min(40, page.Length)]}");
            var (again, _, _) = await Web.Get(PageUrl, result.UserAgent!, cookie, via);  // reuse it
            Console.WriteLine((int)again);
            return 0;
        }
        Console.WriteLine("DataDome not passed in 3 attempts");
        return 1;
    }
}

SDK, t=bv içeren bir captchaUrl adresini hiçbir şey göndermeden önce reddeder, çünkü o adresten yapılan hiçbir çözüm geçemez. Bu ValidationException bir ApiException değildir, bu yüzden döngü onu yakalamaz. Görünür kalmasına izin verin: adresi değiştirip baştan başlamanın işaretidir. CapSkip’in bunun yerine ERROR_PROXY_BANNED olarak bildirdiği bir yasak ise ApiException olarak gelir ve döngü onu aynı adreste yeniden dener; bu yüzden çıktıda bu hatayı görürseniz yeniden çalıştırmadan önce proxy’yi değiştirin. Proxy döndürme kılavuzu proxy’lerin nasıl döndürüleceğini anlatır; C# CAPTCHA algılayıcısı bir metot seçmeden önce bir sayfanın hangi CAPTCHA’yı kullandığını anlamayı ele alır.

Mock çalıştırmasında program şöyle davrandı:

Mock ayarıProgram ne yaptı
Kaydırıcı engelleme sayfası, her çözüm yanıtlandıBir görev, ardından çerezle 200 durumu ve yeniden kullanımda yine 200
Cihaz kontrolü engelleme sayfasıAynı kod çalıştı: captchaUrl /interstitial/ adresini gösterdi ve sonuç aynıydı
İlk iki çözüm reddedildiİki deneme satırı, ardından üçüncü görevde 200 durumu
İlk üç çözüm reddedildiÜç deneme satırı, ardından DataDome’nin geçilmediğini bildiren ileti ve çıkış kodu 1
Sonuç, CapSkip’in kendi listesinin yaptığı gibi bir proxy adlandırıyorÇerez isteği o proxy üzerinden gitti ve 200 aldı
ProxyUrl içinde ayarlanan kendi proxy’nizin.php proxy’yi ve türünü aldı, istekler de onu kullandı
t=bv içeren engelleme sayfasıYakalanmamış ValidationException, mock CapSkip hiç istek görmedi

Proxy çalıştırmasının gösterdiği bir şey: şemadan sonraki metin CapSkip’e yazdığınız gibi gider. ProxyUrl içinde yüzde kodlamalı yazılmış bir giriş bilgisi, örneğin dolar işaretli bir parola için pa%24s, in.php’ye hâlâ kodlu hâlde ulaştı. Giriş bilgisini kodlama gerektiren karakterlerden uzak tutun ya da Proxy argümanını çözülmüş değerlerden kendiniz oluşturun.

Yaygın hatalar

HataNedenDüzeltme
ValidationExceptionDataDome challenge adresi olmayan bir captchaUrl, t=bv içeren bir engelleme sayfası, http ya da https olmayan bir url veya Windows için Chrome olmayan bir userAgent. İleti bunlardan hangisi olduğunu söylerAdresi yeni bir engelleme sayfasından yeniden oluşturun, yasaklıysa adresi değiştirin ve userAgent seçeneğini kaldırın
ApiException ile ERROR_WRONG_USERAGENTUser-Agent Windows için Chrome ama ana sürümü CapSkip’in tarayıcısından farklıuserAgent göndermeyin ve sonucun bildirdiğini kullanın
ERROR_CAPTCHA_UNSOLVABLE içeren ApiExceptionChallenge geçilemediSayfayı yeniden isteyin ve yeni captchaUrl değerini gönderin
ApiException ile ERROR_PROXY_BANNEDCapSkip, API belgelerine göre engelleme sayfası t=bv diyen bir görevi reddederProxy’yi değiştirin ve sayfayı yeniden isteyin
ApiException ile ERROR_PROXY_CONNECT_REFUSEDCapSkip, proxy üzerinden bağlanamadığında bunu bildirir. Yeniden üretmedimProxy adresini, giriş bilgisini ve türünü kontrol edin
CapSkip.TimeoutExceptionCapSkip 300 saniyelik bekleme içinde yanıt vermedi. Bu bir ApiException değildir, bu yüzden döngü onu yakalamaz. Tam adı yazın, çünkü System içinde de bir TimeoutException vardırCapSkip’in çalıştığını ve çözdüğünü kontrol edin, sonra programı yeniden çalıştırın
NetworkExceptionCapSkip çalışmıyor ya da host veya port yanlışCapSkip’i başlatın ve bağlantı ayarlarını kontrol edin
Çerezle birlikte hâlâ 403İstek başka bir IP adresinden çıktı, başka bir User-Agent kullandı ya da çerezin süresi dolduSonucun UserAgent ve Proxy değerlerini her seferinde gönderin ya da döngüyü yeni engelleme sayfasıyla yeniden çalıştırın

Çözücüyü bir sunucuda çalıştırmak

Programın CapSkip’ten iki şeye ihtiyacı var: bir host ve bir port. Local modunda bunlar 127.0.0.1 ve kendi bilgisayarınızdır. Server modunda CapSkip ağ adresinizi veya genel bir IP’yi dinler, böylece başka bir bilgisayardaki, bir VPS’teki veya barındırılan bir platformdaki program aynı API üzerinden ona erişebilir. Host ve port değerlerini CapSkipClient constructor’ında değiştirin; başka hiçbir şey değişmez. Kararlı uzaktan erişim için statik bir genel IP önerilir. Server modu da kendi donanımınızda çalışır ve çözüm başına ücretlendirme yoktur. Yine de portu her adrese açar, bu yüzden ulaşmaması gereken adresler için bir Windows Firewall engelleme kuralı ekleyin. Çözümün o makinede çalıştığını unutmayın; bu yüzden çerez sunucunun IP adresinden kazanılır. Bir proxy vermezseniz çerezi taşıyan istek de o sunucudan çıkmak zorundadır. Bir proxy kullanıyorsanız aynısını hem çözüme hem isteklerinize verin. Modlar arasında nasıl geçileceğini bağlantı ayarları bölümünde bulabilirsiniz.

FAQ

DataDomeAsync bir token döndürür mü?

Hayır. datadome çerezini döndürür. Bu çerezi, engellenen isteğin Cookie başlığında, aynı User-Agent ile birlikte gönderirsiniz. Doldurulacak bir form alanı yoktur.

DataDome için proxy gerekir mi?

Test için gerekmez. İlk çalıştırma için tek bir IP adresi yeterlidir. CapSkip belgelerine göre çerez yalnızca onu kazanan IP adresinden çalışır; bu yüzden çok sayıda istek göndermeye başladığınızda görev başına bir sticky proxy planlayın ve aynı proxy’yi hem çözüme hem isteklerinize verin.

Barındırılan bir platform bunu benim CapSkip’ime karşı çalıştırabilir mi?

Evet, Server modu ile. CapSkip’i ağınızı veya genel IP’nizi dinleyecek şekilde ayarlayın, CapSkipClient constructor’ını o host ve port bilgisine yöneltin; döngü aynı çalışır, tek bir farkla: çözüm artık sunucuda çalışır, bu yüzden çözüme ve isteklerinize aynı proxy’yi verin. Çözücü sizin donanımınızda kalır.

Kısa özet

C#’ta bir DataDome CAPTCHA’sını çözmek dört hamle sürer: engelleme sayfasını okuyun, captchaUrl adresini oluşturun, DataDomeAsync metodunu çağırın ve isteği çerezle, User-Agent ile ve ait olduğu IP adresiyle yineleyin. Kendi makinenizde yeniden denemek ücretsizdir. Bir captcha çözücü ile korumalı bir site size sürekli yeni challenge vermeye devam etse bile her yeniden deneme ücretsizdir. SDK’nın geri kalanı için bkz. C# CAPTCHA çözücü sayfası.