Bir Apify Actor İçinde CAPTCHA Nasıl Çözülür (Node.js)

apify captcha - How to Solve CAPTCHA Inside an Apify Actor (Node.js)

Apify’de bir captcha adımı her zamanki üç hamleden, artı platforma özgü bir hamleden oluşur. Sitekey’i okuyun, çözün, token’ı formla birlikte geri gönderin. Fazladan hamle, çözücünün nerede duracağına karar vermek; çünkü bir Actor sizin dizüstünüzde çalışmaz. Apify’nin altyapısında bir konteynerde çalışır, dolayısıyla içindeki loopback adresi yalnızca o konteynere aittir. Bu tek kararı doğru verin, gerisi yirmi satır.

Neye ihtiyacınız var

  • Node.js 18 veya üzeri, Apify CLI ve bir Apify hesabı.
  • Actor içinde kurulu apify ve capskip paketleri.
  • Actor’ın erişebileceği bir makinede, sabit genel IP ve anahtar doğrulaması açık şekilde Sunucu modunda çalışan CapSkip. Kendi makinenizde geliştirme yaparken Yerel mod hâlâ işinizi görür. Her iki mod da şurada açıklanıyor: bağlantı ayarları.
# Scaffold an Actor, then add the solver client.
apify create captcha-actor -t getting_started_node
cd captcha-actor
npm install capskip

Burada Sunucu modu isteğe bağlı değil

İnsanların takıldığı kısım burası, o yüzden en başa alıyoruz. Actor’lar Apify’nin worker’larında çalışır. Actor kodunuz 127.0.0.1:8080 adresine bir bağlantı açtığında kendi konteyneriyle konuşuyordur; o konteynerde bir çözücü çalışmıyordur ve çağrı, çözücü çökmüş gibi görünen bir bağlantı hatasıyla başarısız olur. Hiçbir şey çökmedi. Adres yalnızca yanlış makineye göre yereldi.

CapSkip’in tam da bu yüzden iki bağlantı modu var. Yerel mod 127.0.0.1 adresine bağlanır ve yalnızca o cihaza yanıt verir. Sunucu modu ağ adresinize veya genel IP’nize bağlanır; böylece başka bir makine, bir VPS ya da Apify gibi barındırılan bir platform aynı çözücüyü API üzerinden çağırabilir. Sabit bir genel IP, adresi çalıştırmalar arasında sabit tutar.

Sık gündeme geldiği için açıkça söylemekte fayda var: Sunucu modu, CapSkip’i ölçümlü bir bulut hizmetine dönüştürmez. Hâlâ sizin makineniz ve hâlâ sınırsız. Değişen tek şey, hangi arayüzü dinlediği. Bir ağ adresini dinlemeye başladığında anahtar doğrulamasını açın ve Actor’a kendi anahtarını verin; böylece o anahtar başka hiçbir şeyi rahatsız etmeden iptal edilebilir.

Adım 1: çözücü adresini gizli bir girdi olarak tanımlayın

Host değerini koda gömmeyin. Apify’nin girdi şeması şifreli alanları destekler; bir çözücü adresi ve anahtarı için doğru yer burasıdır ve bu, değerlerin bir build’e gömülmek yerine her çalıştırmada ayarlanması demektir. Şifreleme textfield, textarea ve hidden editörleriyle çalışır.

{
  "title": "CAPTCHA actor input",
  "type": "object",
  "schemaVersion": 1,
  "properties": {
    "targetUrl": {
      "title": "Target URL",
      "type": "string",
      "editor": "textfield"
    },
    "solverHost": {
      "title": "Solver host",
      "type": "string",
      "editor": "textfield",
      "isSecret": true
    },
    "solverKey": {
      "title": "Solver API key",
      "type": "string",
      "editor": "textfield",
      "isSecret": true
    }
  },
  "required": ["targetUrl", "solverHost"]
}

O dosya, actor.json’ın yanında .actor klasörüne konur ve değerler kodunuza girdi nesnesi üzerinden ulaşır.

Adım 2: host’u çalışma zamanında seçin

Her iki yerde de çalışan tek bir Actor istiyorsunuz: yerel olarak çalıştırırken 127.0.0.1 ile, dağıtıldıktan sonra ise sunucunuzla konuşan bir Actor. SDK tam olarak bunun için bir boolean sunuyor. Actor.isAtHome(), kod Apify platformunda çalışırken true, çalışmıyorken false döndürür.

// npm install apify capskip
import { Actor } from 'apify';
import { CapSkip } from 'capskip';

await Actor.init();
const input = await Actor.getInput();

// Local run talks to the loopback address. A platform run
// talks to the server address that came in as a secret.
const solver = new CapSkip({
  host: Actor.isAtHome() ? input.solverHost : '127.0.0.1',
  port: 8080,
  apiKey: input.solverKey,
});

Actor’ın geri kalanının bu farkı bilmesi gerekmez. Aynı kod yolu ikisini de karşılar ve başarısız bir dağıtım artık sabitleri düzenlemek anlamına gelmez.

Adım 3: sitekey’i okuyun, çözün, token’ı gönderin

Sitekey, ana belgede bir data-sitekey özniteliği olarak durur; dolayısıyla düz bir fetch ve bir düzenli ifade onu tarayıcı başlatmadan alır. Ücretli bir platformda bu önemli: tarayıcısız bir Actor çok daha az belleğe ihtiyaç duyar ve Apify, belleği süreyle çarparak faturalandırır. Aşağıdaki Actor.fail() geri dönmek yerine çalıştırmayı sonlandırır; bu yüzden ondan sonraki kod eşleşmenin başarılı olduğunu varsayabilir.

// Fetch the form page and lift the sitekey out of it.
const html = await (await fetch(input.targetUrl)).text();
const match = html.match(/data-sitekey="([^"]+)"/);

if (!match) {
  await Actor.fail('No sitekey on the page. Did the widget render?');
}

const result = await solver.recaptcha(match[1], input.targetUrl);
const token = result.code;   // the g-recaptcha-response value

Sonra formu, token sitenin beklediği alanda olacak şekilde gönderin. reCAPTCHA v2 için gizli textarea’nın adı g-recaptcha-response olur ve çoğu form onu aynı adla gönderir. Sayfa token’ı bunun yerine bir callback’e veriyorsa, callback’in gerçekte ne gönderdiğini kontrol edin.

// The token travels as an ordinary form field.
const body = new URLSearchParams({
  email: 'someone@example.com',
  'g-recaptcha-response': token,
});

const posted = await fetch(input.targetUrl, { method: 'POST', body });
await Actor.pushData({ url: input.targetUrl, status: posted.status });

Turnstile ve GeeTest’in aynı istemci üzerinde kendi metotları var ve ikisi de aynı şekli alıyor. Turnstile ayrıca, challenge sayfalarında token ile birlikte gönderilmesi gereken bir user agent döndürüyor. Tam parametre listeleri şurada: CapSkip API dokümantasyonu.

Tam çalışan örnek

src/main.js olarak Actor’ın tamamı. Bir Actor, üst düzey await içeren bir ES modülüdür; bu yüzden sarmalayıcı bir fonksiyon yoktur ve sondaki Actor.exit(), veri kümesini boşaltıp çalıştırmayı temiz biçimde kapatan şeydir.

// npm install apify capskip
import { Actor } from 'apify';
import { CapSkip, NetworkException, TimeoutException } from 'capskip';

await Actor.init();

const input = await Actor.getInput();
const solver = new CapSkip({
  host: Actor.isAtHome() ? input.solverHost : '127.0.0.1',
  port: 8080,
  apiKey: input.solverKey,
});

try {
  const html = await (await fetch(input.targetUrl)).text();
  const match = html.match(/data-sitekey="([^"]+)"/);
  if (!match) throw new Error('No sitekey found on the page.');

  const result = await solver.recaptcha(match[1], input.targetUrl);

  const body = new URLSearchParams({
    'g-recaptcha-response': result.code,
  });
  const posted = await fetch(input.targetUrl, { method: 'POST', body });

  await Actor.pushData({ url: input.targetUrl, status: posted.status });
} catch (err) {
  if (err instanceof NetworkException) {
    await Actor.fail('Cannot reach the solver. Check Server mode and the host.');
  }
  if (err instanceof TimeoutException) {
    await Actor.fail('The solve outlasted recaptchaTimeout.');
  }
  throw err;
}

await Actor.exit();

Bağlantıyla ilgili iki istisnayı ayrı ayrı yakalamak altı satıra değer. Actor.fail(), çıkış kodu 1 ve iliştirilmiş bir mesajla birlikte Actor.exit() demektir; böylece çalıştırma FAILED olarak biter ve günlükte kurulumun hangi yarısının bozulduğunu söyleyen bir cümle bulunur. Bu olmadan, sadece erişilemeyen bir çözücü ile gerçekten okunamayan bir çözüm aynı kırmızı çalıştırmayı üretir.

Dağıtmadan önce yerelde çalıştırmak

Önce Actor’ı kendi makinenizde, çözücü Yerel moddayken çalıştırın. Orada isAtHome() false döndürür, dolayısıyla kod siz hiçbir şey değiştirmeden 127.0.0.1 adresine uzanır ve ağ atlamasını eklemeden önce sitekey okumasının ve form gönderiminin çalıştığını kanıtlamış olursunuz.

# Reads INPUT from storage/key_value_stores/default.
apify run

Bu geçtiğinde CapSkip’i Sunucu moduna alın, artık dinlediği adresi not edin ve o adresi platformdaki solverHost girdisine yazın. Değişen tek şey bir dize.

Sık görülen hatalar ve anlamları

GördüğünüzNedenDüzeltme
Platformda NetworkException, yerelde hiç yokActor 127.0.0.1 adresini çağırdı ve kendi konteynerine ulaştıÇözücüyü Sunucu moduna alın ve adresini geçirin
ERROR_WRONG_USER_KEYAnahtar doğrulaması açık ve Actor yanlış anahtarı gönderdiAnahtarı gizli bir girdi olarak ayarlayın ve girdiden okuyun
ERROR_GOOGLEKEYDüzenli ifade hiçbir şeyle eşleşmedi ve boş bir anahtar gittiBir çözüm harcamadan önce eşleşmeyi kontrol edin
Çalıştırma durumu TIMED-OUTÇalıştırmanın zaman aşımı, fetch artı çözüm artı gönderimden kısaActor’ın varsayılan çalıştırma seçeneklerinde zaman aşımını artırın
TimeoutExceptionÇözüm recaptchaTimeout süresini aştıVarsayılan 300 saniyenin üzerine çıkarın
Form, sorunsuz çözülmüş bir token’ı reddediyorToken, çözüm ile gönderim arasında eskidiÇalıştırmanın başında değil, göndermeden hemen önce çözün

FAQ

Bir Apify Actor gerçekten kendi makinemdeki bir çözücüye erişebilir mi?

Evet, herhangi bir dahili servis için kullanacağı API’nin aynısı üzerinden. Sunucu modu, CapSkip’in loopback adresi yerine ağ adresinizi ya da genel IP’nizi dinlemesini sağlar; böylece Actor onu herhangi bir HTTP uç noktası gibi çağırır. Adresin çalıştırmalar arasında yerinden oynamaması için sabit bir genel IP öneriliyor ve portu açmadan önce anahtar doğrulaması açık olmalı.

Bu, Apify üzerindeki bir Crawlee crawler’ı içinde de çalışır mı?

Çalışır ve üç hamle birebir aynı. Fark, nerede durdukları: sitekey okuması ve token enjeksiyonu request handler’ın içine girer, çözücü istemcisi ise dışarıda bir kez oluşturulur ki her istek tek bir örneği paylaşsın. Bir çözümü neden kendi yeniden deneme döngünüze sarmamanız gerektiği dahil, crawler’a özgü ayrıntılar şurada anlatılıyor: Crawlee CAPTCHA rehberi.

Apify Proxy üzerinden mi çözmeliyim?

Yalnızca site, çözümü yapan IP’yi puanlıyorsa. Proxy desteği reCAPTCHA, Turnstile ve GeeTest için mevcut ve token, onu isteyen adrese karşı kontrol edildiğinde önem kazanıyor. Proxy’yi, çözücünün tamamını onun üzerinden yönlendirmek yerine çözüm çağrısında geçirin; böylece istediğinizde getirme ile çözüm farklı çıkışlar kullanabilir. Dengeler şu rehberde anlatılıyor: CAPTCHA proxy rotasyonu.

Actor’a ne kadar bellek vermeliyim?

Tarayıcıyı atlarsanız sandığınızdan az. Yukarıdaki Actor HTML getiriyor, bir ağ çağrısını bekliyor ve bir form gönderiyor; yani ömrünün çoğunda boşta duruyor ve Chromium ölçeğinde bir ayak izine hiç ihtiyacı yok. Tarayıcıya yalnızca sayfa, JavaScript çalıştırmadan sitekey’ini vermiyorsa başvurun. Çözümün kendisi her hâlükârda çözücünün makinesinde gerçekleşir, ama beklerken Actor’ınız da çalışmaya devam eder.

Kısa özet

Çözücüyü Sunucu moduna alın, adresini ve anahtarını şifreli girdiler olarak saklayın ve o adres ile 127.0.0.1 arasında Actor.isAtHome() seçsin. Sonra sitekey’i okuyun, çözün ve token’ı g-recaptcha-response olarak gönderin. Puppeteer ve Playwright dahil daha geniş Node tablosu için bkz. Node.js CAPTCHA çözücü sayfası. Aynı problemin tarama tarafı şurada anlatılıyor: web scraping sayfasında ele alınıyor..

Bir Actor’ı büyütmeden önce bir sonucu açıkça söylemekte fayda var. Bu captcha atlatma zaten sahip olduğunuz donanımda çalıştığı için, on çalıştırmadan bine geçmek Apify faturanızı artırır ve çözüm faturanızı olduğu yerde bırakır.