PHP में DataDome कैप्चा कैसे हल करें (Guzzle CookieJar)

datadome captcha in php - How to Solve DataDome CAPTCHA in PHP (Guzzle CookieJar)

PHP में DataDome कैप्चा का नतीजा token नहीं, एक cookie होती है। यह गाइड 403 ब्लॉक पेज पढ़ती है, चैलेंज का पता बनाती है और उसे capskip/capskip पैकेज के dataDome() method को देती है, जो CapSkip DataDome सॉल्वर से बात करता है। फिर यह लौटी हुई cookie को उस Guzzle client के jar में रखती है जो अगली रिक्वेस्ट भेजता है। CapSkip के दस्तावेज़ कहते हैं कि cookie सिर्फ़ उसी User-Agent और उसी पते के साथ काम करती है जिसके साथ वह मिली थी, और Guzzle client दोनों को बाँधकर रखता है।

आपको क्या चाहिए

  • किसी Windows PC पर CapSkip 3.2.0 या बाद का, जो DataDome (स्लाइडर और डिवाइस चेक) हल करने वाला पहला वर्ज़न है।
  • curl और json extensions के साथ PHP 8.0.2 या बाद का, और Composer, दोनों आपके PATH में। Composer की platform check ने 8.0.30 इंस्टॉल पर 8.0.2 बताया और 8.1.33 तथा 8.4.15 पर 8.1.0।
  • एक खाली working folder। उसमें composer require capskip/capskip guzzlehttp/guzzle चलाएँ और script को वहीं datadome-demo.php नाम से सेव करें। 1.5.0 से पुराने capskip रिलीज़ में dataDome method नहीं है।
  • एक CA bundle। Windows के लिए PHP बिना इसके आता है, इसलिए पहली https रिक्वेस्ट cURL error 60 के साथ फेल हो जाती है, जब तक आप curl.se से cacert.pem डाउनलोड करके php.ini में curl.cainfo को उसकी ओर न कर दें।
  • DataDome से सुरक्षित ऐसा पेज जिसे ऑटोमेट करने की आपको अनुमति हो।
  • CapSkip के लिए एक host और port। Local mode में वह सिर्फ़ 127.0.0.1 पर जवाब देता है, उसी PC की scripts के लिए। Server mode में दूसरी मशीन का PHP उसे आपके नेटवर्क या पब्लिक IP पर कॉल कर सकता है। mode आप यहाँ चुनते हैं: कनेक्शन सेटिंग्स.
  • टेस्ट से आगे किसी भी काम के लिए, हर टास्क पर एक sticky proxy।

टेस्ट का तरीका: मेरे लिखे दो mock सर्वर, सिर्फ़ Windows पर, और कोई असली DataDome साइट नहीं। एक उन जाँचों की नक़ल करता है जो CapSkip के API दस्तावेज़ के DataDome सेक्शन में सूचीबद्ध हैं। दूसरा एक संरक्षित साइट की भूमिका निभाता है जो 403 ब्लॉक पेज के साथ जवाब देती है, सिर्फ़ वही cookie स्वीकार करती है जो पहले mock ने जारी की हो, और proxy की तरह रिक्वेस्ट आगे भी भेजती है। https के लिए उसने एक throwaway CA इस्तेमाल किया। scripts ने CapSkip app को कभी नहीं छुआ, इसलिए असली हल का कोई समय नहीं है और किसी live साइट का कोई फ़ैसला भी नहीं।

कोड ब्लॉक PHP 8.0.30, 8.1.33, 8.2.29, 8.3.28 और 8.4.15 पर capskip 1.6.0 और Guzzle 8.3.1 के साथ चले, और 8.0.30 तथा 8.4.15 पर capskip 1.6.0 और Guzzle 7.16.1 के साथ भी। PHP 8.5.0 पर रन पूरा हो जाता है, लेकिन SDK की Http.php फ़ाइल CapSkip को भेजी गई हर रिक्वेस्ट के लिए curl_close() के बारे में एक Deprecated notice प्रिंट करती है, मेरे रन में तीन।

चरण 1: हर पहचान के लिए एक Guzzle client, फिर ब्लॉक पेज

पाँचों कोड ब्लॉक को दिखाए गए क्रम में datadome-demo.php में रखें। पहले ब्लॉक में हर use line है, इसलिए बाकी ब्लॉक कोई नहीं जोड़ते।

Guzzle के तीन defaults इस loop के ख़िलाफ़ काम करते हैं। client तब तक कोई cookie नहीं रखता जब तक आप उसे jar न दें। वह अपना नाम GuzzleHttp और उसके major वर्ज़न के रूप में बताता है, जो mock ने Guzzle 7 और 8 के सादे client से देखा। और वह किसी भी 4xx जवाब पर ClientException throw करता है, जो यहाँ सबसे ज़्यादा मायने रखता है, क्योंकि 403 ब्लॉक पेज ही वह चैलेंज है जिसे आपको पढ़ना है। उस default को चालू छोड़ने पर मेरा रन पहली ही रिक्वेस्ट पर मर गया और mock CapSkip को कोई टास्क नहीं मिला। इसलिए session() http_errors बंद कर देता है। वह jar, User-Agent और proxy भी एक साथ सेट करता है, ताकि वे अलग-अलग न हो जाएँ। नई पहचान का मतलब है नया client।

<?php
// composer require capskip/capskip guzzlehttp/guzzle
require __DIR__ . '/vendor/autoload.php';

use CapSkip\CapSkip;
use CapSkip\Exceptions\ApiException;
use CapSkip\Exceptions\CapSkipError;
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Cookie\SetCookie;
use Psr\Http\Message\ResponseInterface;

const PAGE_URL = 'https://www.example.com/'; // a page behind DataDome
// For the first request only. The cookie comes back with its own User-Agent.
const BROWSER_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
    . '(KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36';

// Guzzle fixes its options when the Client is built, so one Client is one
// identity: a cookie jar, a User-Agent and a proxy.
function session(CookieJar $jar, string $userAgent, ?string $proxyUrl = null): Client
{
    return new Client([
        'cookies' => $jar,
        'headers' => ['User-Agent' => $userAgent],
        'proxy' => $proxyUrl,
        'http_errors' => false, // a 403 block page is an answer, not an exception
        'timeout' => 30,
    ]);
}

जिस रिक्वेस्ट पर DataDome को शक होता है, उसे 403, या कभी-कभी 401, और एक छोटा HTML पेज मिलता है। चैलेंज का पता, जिसे दस्तावेज़ captcha_url कहते हैं, या तो geo.captcha-delivery.com से आए iframe का src होता है या कोई चीज़ जिसे script dd object से जोड़ती है। नीचे का function दोनों को संभालता है। dd object की rt entry चैलेंज पेज चुनती है: अक्षर c /captcha/ वाला स्लाइडर चुनता है, और अक्षर i /interstitial/ वाला डिवाइस चेक। दस्तावेज़ t की value bv को बैन किया गया पता बताते हैं और आम चैलेंज पर fe दिखाते हैं; fe की वह व्याख्या उन्हीं की है।

पहली रिक्वेस्ट एक सादे Chrome User-Agent के साथ जाती है जो CapSkip को कभी नहीं भेजा जाता, इसलिए उसके वर्ज़न का किसी चीज़ से मेल खाना ज़रूरी नहीं। हर ब्लॉक पेज एक नया चैलेंज होता है, इसलिए उसके पते का जल्दी इस्तेमाल करें।

// Build the challenge address from a DataDome block page, or null.
function captchaUrlFrom(ResponseInterface $resp): ?string
{
    if (!in_array($resp->getStatusCode(), [401, 403], true)) {
        return null;
    }
    $html = (string) $resp->getBody();
    // Some block pages carry the challenge iframe: its src is the address.
    if (preg_match('/<iframe[^>]+src="(https:\/\/[^"]*captcha-delivery\.com\/[^"]+)"/', $html, $found)) {
        return $found[1]; // an HTML-escaped &amp; is fine, CapSkip decodes it
    }
    // Most build it from the dd object in a script instead.
    if (!preg_match('/var\s+dd\s*=\s*(\{[^}]*\})/', $html, $found)) {
        return null;
    }
    $dd = json_decode(str_replace("'", '"', $found[1]), true);
    if (!is_array($dd)) {
        return null;
    }
    $query = ['initialCid' => $dd['cid'] ?? '', 'hash' => $dd['hsh'] ?? '', 'cid' => $dd['cookie'] ?? ''];
    if (!empty($dd['t'])) {
        $query['t'] = $dd['t']; // fe is a challenge, bv means the address is banned
    }
    $query['referer'] = PAGE_URL;
    $query['s'] = $dd['s'] ?? '';
    foreach (['e', 'b'] as $key) {
        if (!empty($dd[$key])) {
            $query[$key] = $dd[$key];
        }
    }
    $query['dm'] = 'cd';
    $path = ($dd['rt'] ?? '') === 'i' ? 'interstitial' : 'captcha';

    return 'https://' . ($dd['host'] ?? 'geo.captcha-delivery.com') . "/{$path}/?"
        . http_build_query($query, '', '&', PHP_QUERY_RFC3986);
}

चरण 2: इसे हल करें और cookie को jar में रखें

method dataDome(string $captchaUrl, string $url, array $options = []) है, और छोटे अक्षरों वाले datadome() ने मेरे रन में वही नतीजा दिया। options array userAgent और proxy लेता है, और उसमें null values भेजी नहीं जातीं। proxy नीचे जैसे type और uri वाले array के रूप में जाता है, या सादी string के रूप में, जिसे SDK HTTP proxy की तरह भेजता है।

userAgent छोड़ दें। तब CapSkip अपने ब्राउज़र का User-Agent इस्तेमाल करता है और उसे नतीजे में बताता है, और उसके बाद आप वही string भेजते हैं। आपका दिया हुआ User-Agent उस ब्राउज़र के major वर्ज़न वाला stock Chrome for Windows होना चाहिए: SDK ने लोकल रूप से Firefox string ठुकरा दी, और mock ने Chrome 120 string पर ERROR_WRONG_USERAGENT दिया। method recaptchaTimeout तक इंतज़ार करता है, जो डिफ़ॉल्ट रूप से 300 सेकंड है।

// Local mode. In Server mode, use your server's address instead.
$solver = new CapSkip(['host' => '127.0.0.1', 'port' => 8080]);

function solve(CapSkip $solver, string $captchaUrl, ?string $proxyUrl): array
{
    $options = [];
    if ($proxyUrl !== null) {
        [$scheme, $rest] = explode('://', $proxyUrl, 2);
        $options['proxy'] = ['type' => strtoupper($scheme), 'uri' => $rest];
    }
    // No userAgent option: CapSkip uses its own browser's and reports it.
    return $solver->dataDome($captchaUrl, PAGE_URL, $options);
}

नतीजा captchaId, code, userAgent और cookie keys वाला array है। code value वही cookie है जैसी DataDome ने सेट की, attributes समेत। cookie value सिर्फ़ datadome नाम और value की जोड़ी है। proxy key तभी दिखती है जब आपने कोई proxy नहीं दिया और CapSkip ने अपनी DataDome list का कोई proxy इस्तेमाल किया। उसमें scheme और login के बिना host और port होते हैं, इसलिए अगर उस list की entry को login चाहिए तो उसे खुद जोड़ें। तब cookie सिर्फ़ उसी proxy से चलती है।

SDK के exceptions CapSkip\Exceptions के नीचे रहते हैं और CapSkipError को extend करते हैं, जो Exception को extend करता है, RuntimeException को नहीं। सिर्फ़ ApiException, यानी ठुकराया गया टास्क, ऐसा नाकाम हल है जिसे दोबारा आज़माना सार्थक है।

यहीं PHP script उस script से अलग होती है जो Cookie header बनाती है। code value सीधे Guzzle SetCookie में parse हो जाती है, और फिर jar किसी ब्राउज़र के नियम लागू करता है: वह cookie सिर्फ़ उस host को भेजता है जो Domain attribute से मेल खाता हो, मेल खाते Path attribute के लिए, Max-Age खत्म होने तक, और https पर तभी जब Secure attribute सेट हो। वह बदली हुई cookie भी रख लेता है। mock ने 200 पेज के साथ एक नई cookie भेजी, और उसी client से अगली रिक्वेस्ट वह साथ ले गई।

jar चार तरीकों से चुपचाप फेल होता है, और मैं हर एक में फँसा। जिस cookie में Domain attribute नहीं है, वह स्टोर तो होती है पर कभी भेजी नहीं जाती। Domain या Secure में mismatch हो तो कोई Cookie header नहीं जाता, इसलिए jar में cookie होने पर भी साइट 403 देती है। setCookie() किसी cookie को ठुकराते समय, जैसे expired cookie को, throw करने के बजाय false लौटाता है। और जो datadome cookie ब्लॉक पेज ने खुद सेट की थी, वह हल की गई cookie के बगल में jar में रह जाती है, इसलिए दोनों चली जाती हैं और साइट गलत वाली पढ़ सकती है। मेरे पहले mock ने वह cookie कभी सेट नहीं की; जब उसने की, तब नीचे का clear() call उसे हटाने तक तीनों कोशिशें फेल रहीं। ब्लॉक चारों से बचाता है।

function sendWithCookie(array $result, CookieJar $jar, ?string $proxyUrl): array
{
    // code is the cookie as DataDome set it, with Domain, Path and Max-Age.
    $cookie = SetCookie::fromString($result['code']);
    if (!$cookie->getDomain()) {
        // A jar keeps a cookie with no Domain but never sends it.
        $cookie->setDomain(parse_url(PAGE_URL, PHP_URL_HOST));
    }
    // The block page may have set a datadome cookie of its own. Drop it.
    $jar->clear(parse_url(PAGE_URL, PHP_URL_HOST), '/', 'datadome');
    if (!$jar->setCookie($cookie)) { // false, not an exception, for a cookie it rejects
        throw new RuntimeException('The cookie jar refused: ' . $result['code']);
    }
    // The cookie works only from the address that earned it.
    $via = $proxyUrl;
    if ($via === null && !empty($result['proxy'])) {
        // CapSkip solved through its own list. Add user:pass@ if that entry needs a login.
        $via = 'http://' . $result['proxy'];
    }
    $http = session($jar, $result['userAgent'], $via);

    return [$http, $http->get(PAGE_URL)];
}

चरण 3: 403 retry loop के साथ पूरा उदाहरण

आख़िरी ब्लॉक सभी चरणों को जोड़ता है। इसे datadome-demo.php में जोड़ें और working folder में php datadome-demo.php चलाएँ। हर कोशिश नए jar और client से शुरू होती है, इसलिए हर बार नया चैलेंज जाता है और किसी नाकाम कोशिश की कोई cookie साथ नहीं जाती। यह चुनाव मेरा है और DataDome के सामने आज़माया नहीं गया।

loop ApiException को दोबारा आज़माता है, जैसे ERROR_CAPTCHA_UNSOLVABLE, और उस cookie को भी जिस पर फिर भी 403 आए। ERROR_PROXY_ का नाम लेने वाला ApiException तुरंत दोबारा throw किया जाता है, क्योंकि वही पता उसी तरह फेल होगा। dd object या iframe के बिना 403 loop को रोक देता है। हर retry साइट से एक और ब्लॉक पेज रिक्वेस्ट कराती है, इसलिए तीन कोशिशें समझदारी भरी सीमा हैं।

function fetchProtected(CapSkip $solver, ?string $proxyUrl = null): array
{
    for ($attempt = 1; $attempt <= 3; $attempt++) {
        $jar = new CookieJar(); // a clean jar for every attempt
        $http = session($jar, BROWSER_UA, $proxyUrl);
        $block = $http->get(PAGE_URL); // a fresh block page, a fresh challenge
        $captchaUrl = captchaUrlFrom($block);
        if ($captchaUrl === null) {
            if ($block->getStatusCode() >= 400) {
                throw new RuntimeException('HTTP ' . $block->getStatusCode() . ' without a DataDome block page');
            }
            return [$http, $block]; // nothing to solve here
        }
        try {
            $result = solve($solver, $captchaUrl, $proxyUrl);
        } catch (ApiException $e) {
            if (str_contains($e->getMessage(), 'ERROR_PROXY_')) {
                throw $e; // the address is the problem, so a retry fails the same way
            }
            echo "attempt {$attempt}: {$e->getMessage()}\n";
            continue;
        }
        [$http, $page] = sendWithCookie($result, $jar, $proxyUrl);
        if ($page->getStatusCode() === 200) {
            return [$http, $page];
        }
        echo "attempt {$attempt}: HTTP {$page->getStatusCode()} with the cookie\n";
    }
    throw new RuntimeException('DataDome not passed in 3 attempts');
}

try {
    [$http, $page] = fetchProtected($solver); // or ($solver, 'http://user:[email protected]:8080')
    echo $page->getStatusCode(), ' ', substr((string) $page->getBody(), 0, 40), PHP_EOL;
    // Same Client, so the same jar, User-Agent and proxy.
    echo $http->get(PAGE_URL)->getStatusCode(), PHP_EOL;
} catch (CapSkipError | RuntimeException $e) {
    fwrite(STDERR, get_class($e) . ': ' . $e->getMessage() . PHP_EOL);
    exit(1);
}

mock के सामने PHP में DataDome कैप्चा की पूरी script ने यह प्रिंट किया:

200 <html><body><h1>Real content</h1></body>
200

नीचे की हर पंक्ति उन सभी combinations पर एक जैसी आई, सिर्फ़ एक बात को छोड़कर: cURL error 60 पर Guzzle 8 ConnectException उठाता है, और Guzzle 7 RequestException।

mock सेटिंगscript ने क्या किया
स्लाइडर, डिवाइस चेक या iframe वाला ब्लॉक पेजएक टास्क, फिर 200, और दोबारा इस्तेमाल पर फिर 200
पहले दो हल ठुकराए गए, या तीनोंतीसरे टास्क पर 200, या RuntimeException और exit code 1
t=bv वाला ब्लॉक पेजValidationException; mock को कुछ नहीं मिला
नतीजा एक proxy का नाम देता है, या आप अपना देते हैंcookie वाली दोनों रिक्वेस्ट उसी proxy से गईं और सफल रहीं
हल का जवाब ERROR_PROXY_BANNED आयाएक टास्क के बाद ApiException
बेमेल Domain attribute, या सादे http पर Secureतीन कोशिशें, हर एक cookie के साथ 403
बिना Domain attribute वाली cookieचरण 2 के ब्लॉक में fallback के साथ 200, उसके बिना तीन 403 कोशिशें
ऐसा ब्लॉक पेज जो अपनी datadome cookie खुद सेट करता हैचरण 2 के ब्लॉक में clear() के साथ 200, उसके बिना तीन 403 कोशिशें
throwaway CA वाली https साइटcurl.cainfo के बिना cURL error 60; उसके साथ 200, Secure cookie पर भी

cookie का बाँधा जाना, और mock साइट का दो datadome cookies में से पहली को पढ़ना, वे नियम हैं जो मैंने CapSkip के दस्तावेज़ पढ़ने के बाद लिखे। वे असली service के बारे में कुछ नहीं कहते।

Command line या वेब रिक्वेस्ट?

SDK एक हल के लिए 300 सेकंड तक इंतज़ार करता है। PHP मैनुअल के अनुसार max_execution_time का डिफ़ॉल्ट 30 सेकंड है, और command line पर 0। मैंने mock टास्क को 8 सेकंड रोके रखा, इसलिए script को करीब 13 सेकंड लगे, और हर रन को PHP 8.4.15 पर उसके पहले statement से आख़िरी तक नापा; PHP 8.0.30 ने 0.1 सेकंड के भीतर वही नतीजे दिए। इस PC की php.ini में 120 है, और 5 की सीमा के लिए मैंने उसकी एक कॉपी इस्तेमाल की। php.exe, php.ini में max_execution_time को अनदेखा करता है, इसलिए console रन को सिर्फ़ -d flag या set_time_limit() सीमित करता है, जबकि php-cgi.exe, यानी वह CGI build जिससे वेब सर्वर PHP चला सकता है, उस फ़ाइल को पढ़ता है।

कैसे चलानतीजा
php.exe, कुछ सेट नहीं (php.ini में 120 है, php.exe 0 पढ़ता है)पूरा हुआ: status 200 दो बार, करीब 13 सेकंड
php.exe, php.ini में 5 की सीमापूरा हुआ: सीमा अनदेखी हुई, करीब 13 सेकंड
php.exe -d max_execution_time=57.9 सेकंड बाद Fatal error
php-cgi.exe, php.ini में 5 की सीमा7.9 सेकंड बाद Fatal error
php-cgi.exe -d max_execution_time=5, पहले statement के रूप में set_time_limit(60) के साथकरीब 13 सेकंड बाद पूरा हुआ
php-cgi.exe, कुछ सेट नहीं (वह php.ini से 120 पढ़ता है)करीब 13 सेकंड बाद पूरा हुआ

मैंने इसे सिर्फ़ Windows पर चलाया, जहाँ सीमा इंतज़ार को भी गिनती है, इसलिए SDK के poll loop में अटकी script उसे खत्म कर देती है। PHP ने fatal error करीब 8 सेकंड पर उठाया, 5 पर नहीं, क्योंकि सीमा पार होते समय script polls के बीच सो रही थी। script cookie पढ़े बिना ही मर जाती है, और CapSkip हल पहले ही खर्च कर चुका होता है। इस loop को console, scheduled task या queue worker से चलाएँ; worker वाला तरीका Laravel queue गाइड में दिया गया है। वेब रिक्वेस्ट के अंदर पहले set_time_limit() को इतनी सीमा के साथ call करें जो एक हल के लगने वाले समय से ज़्यादा हो।

आम errors

त्रुटिकारणफिक्स
cURL error 60: SSL certificate problemWindows के लिए PHP में CA bundle नहीं होताcurl.se से cacert.pem डाउनलोड करें और php.ini में curl.cainfo सेट करें
ValidationExceptionSDK ने input ठुकरा दिया: captcha-delivery.com से बाहर का पता, t=bv वाला ब्लॉक पेज, ऐसा पेज URL जो http या https नहीं है, या ऐसा userAgent जो stock Chrome for Windows नहीं है। संदेश बताता है कि इनमें से कौन-सा हैनए ब्लॉक पेज से पता दोबारा बनाएँ, बैन हो तो proxy बदलें, userAgent हटा दें
ERROR_PROXY_BANNED, ERROR_PROXY_CONNECT_REFUSED या ERROR_WRONG_USERAGENT वाला ApiExceptionपता बैन है, CapSkip proxy के ज़रिए कनेक्ट नहीं कर पा रहा (दोहराया नहीं), या आपके userAgent का Chrome major वर्ज़न उस ब्राउज़र से अलग है जिससे CapSkip हल करता हैproxy बदलें या जाँचें, या userAgent न भेजें
TimeoutException या NetworkException300 सेकंड का इंतज़ार खत्म हो गया, या आपके दिए host और port पर कोई जवाब नहीं दे रहाCapSkip शुरू करें और अपने host और port को उसकी कनेक्शन सेटिंग्स से मिलाएँ
jar में cookie होने पर भी cookie के साथ HTTP 403Domain attribute उस host से मेल नहीं खाता जिसे आप रिक्वेस्ट कर रहे हैं, या Secure attribute सेट है और URL http हैjar प्रिंट करें और उसे PAGE_URL से मिलाएँ
Fatal error: Maximum execution time of 30 seconds exceededवेब रिक्वेस्ट SDK की polling के दौरान PHP की time limit तक पहुँच गई; संख्या आपकी अपनी सीमा हैscript को console या worker से चलाएँ

FAQ

क्या मैं सर्वर पर चल रहे CapSkip के सामने PHP में DataDome कैप्चा चला सकता हूँ?

हाँ, Server mode इसी के लिए है। तब CapSkip आपके नेटवर्क या पब्लिक IP से कॉल स्वीकार करता है (स्टैटिक IP सुझाया जाता है), और आपको constructor में सिर्फ़ host और port बदलना होता है। Windows मशीन आपकी ही रहती है और उस पर कोई प्रति-हल शुल्क नहीं लगता। Server mode शुरू होते समय port को हर पते के लिए खोल देता है, इसलिए जिन मशीनों की आपको उम्मीद नहीं उनके लिए Windows Firewall का block rule लगाकर उसे private रखें। हल server पर चलता है, इसलिए cookie उसी के पते की है: अपनी Guzzle रिक्वेस्ट उसी मशीन से भेजें, या हल और client, दोनों को वही proxy दें।

cookie jar क्यों, Cookie header क्यों नहीं?

header भी चलता है। SDK का अपना datadome.php उदाहरण सादे cURL handle से header में cookie value भेजता है; मैंने उसे चलाया नहीं। jar Domain और Path attributes पर मिलान, expiry और बदली हुई cookie जोड़ता है, और कीमत यह है कि चरण 2 की चुपचाप होने वाली नाकामियाँ आती हैं।

क्या proxy ज़रूरी है?

पहले टेस्ट के लिए नहीं। CapSkip के दस्तावेज़ कहते हैं कि cookie सिर्फ़ उसी पते से काम करती है जिससे वह मिली थी, इसलिए ज़्यादा volume पर हर टास्क के लिए एक sticky proxy चाहिए। उन्हें बारी-बारी से बदलने के लिए यह पढ़ें: proxy rotation गाइड.

संक्षेप में

PHP में DataDome कैप्चा हल करने में ये चीज़ें लगती हैं: हर पहचान के लिए एक Guzzle client, http_errors बंद रखकर पढ़ा गया ब्लॉक पेज, एक dataDome() call, और लौटी हुई cookie client के jar में। अपनी खुद की Windows मशीन पर चल रहे लोकल captcha सॉल्वर पर दोबारा कोशिश करने में कुछ खर्च नहीं होता, इसलिए loop को पुराना चैलेंज दोबारा इस्तेमाल करने के बजाय नया चैलेंज माँगने दें। बाकी CAPTCHA प्रकारों की SDK calls के लिए यह देखें: PHP कैप्चा सॉल्वर पेज.