Script Etiketinden Yola Çıkarak Python ile Friendly Captcha Nasıl Çözülür

solve friendly captcha in python - How to Solve Friendly Captcha in Python From the Script Tag

Python ile Friendly Captcha çözmek için frc-captcha öğesinden data-sitekey değerini okuyun, widget’ı yükleyen script etiketini bulun, v1 ile v2’yi bu script’e bakarak ayırt edin ve sitekey’i, sayfa URL’sini, sürümü ve script’in tam adresini CapSkip’in friendly_captcha metoduna verin. Karşılığında v1’de frc-captcha-solution, v2’de frc-captcha-response alanına gönderilecek bir token alırsınız. İşin önemli kısmı script etiketidir. Friendly Captcha tek bir ad ve tek bir sitekey biçimi altında birbiriyle ilgisiz iki protokol sunar; yanlış olanı çözdüğünüzde geçerli görünen ama sessizce reddedilen bir token elde edersiniz. CapSkip, Friendly Captcha desteğini 1.4.0 sürümünde ekledi. Bu rehber sayfayı okumayı, çağrıyı, gönderimi ve AsyncCapSkip ile aynı anda çok sayıda çözüm çalıştırmayı ele alıyor.

Neye ihtiyacınız var

  • Bir Windows makinesinde çalışan CapSkip 1.4.0 veya üzeri. Friendly Captcha desteği, CaptchaFox ve Capy Puzzle ile birlikte bu sürümle geldi.
  • Python 3.10 veya üzeri ve capskip paketinin 1.3.0 veya üzeri bir sürümü; friendly_captcha metodunu içeren ilk sürüm budur. Örnekler ayrıca sayfayı çekmek ve formu göndermek için requests kütüphanesini kullanır.
  • Widget’ı gösteren sayfanın URL’si. Sitekey, script adresi ve alan adı bu sayfanın HTML’inden çıkar; nerede olduklarını 1. adım gösteriyor.
  • Çözücü için bir adres. Local modu 127.0.0.1 üzerinden yalnızca o cihaza yanıt verir; Server modu ise ağ adresinizi veya genel IP’nizi dinler, böylece başka bir makinedeki bir betik onu API üzerinden çağırabilir. İkisi de bağlantı ayarları bölümünde yer alır; ne zaman geçiş yapmanız gerektiğini 4. adım anlatıyor.
# pip install capskip requests
pip install "capskip>=1.3.0" requests

Tırnak işaretlerini koruyun. cmd’de tırnak içinde olmayan bir büyüktür işareti yönlendirme anlamına gelir: pip, sürüm kontrolü yapmadan bulduğu capskip paketini kurar ve çıktısını 1.3.0 adlı bir dosyaya yazar. PowerShell argümanı olduğu gibi iletir, ancak tırnaklar her kabukta çalışır.

1. Adım: widget’ı ve script etiketini okuyun

Her iki sürüm de aynı öğeyi oluşturur: frc-captcha sınıfına ve bir data-sitekey özniteliğine sahip bir div. Yani öğe size sitekey’i verir, protokol hakkında ise hiçbir şey söylemez. Sürüm, widget’ı yükleyen script etiketindedir, çünkü v1 ve v2 farklı dosya adlarına sahip farklı paketlerdir:

<!-- v2: the @friendlycaptcha/sdk package -->
<div class="frc-captcha" data-sitekey="YOUR_SITEKEY"></div>
<script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk/site.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk/site.compat.min.js" async defer></script>

<!-- v1: the friendly-challenge package -->
<div class="frc-captcha" data-sitekey="YOUR_SITEKEY"></div>
<script type="module" src="https://cdn.jsdelivr.net/npm/friendly-challenge/widget.module.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/friendly-challenge/widget.min.js" async defer></script>

Çoğu site bu yolun içinde belirli bir sürüm numarasını sabitler; bu, burada hiçbir şeyi değiştirmez. Hepsini okumak için Python’ın standart kütüphanesi yeterlidir. Bu ayrıştırıcı, widget öğesinin özniteliklerini ve nomodule yedeği olmayan her script’in sayfa URL’sine göre çözümlenmiş adresini saklar:

# pip install capskip requests
from html.parser import HTMLParser
from urllib.parse import urljoin

import requests


class FriendlyPage(HTMLParser):
    """Collects the frc-captcha element and the page's script tags."""

    def __init__(self, page_url):
        super().__init__()
        self.page_url = page_url
        self.widget = {}
        self.scripts = []

    def handle_starttag(self, tag, attrs):
        a = dict(attrs)
        if "frc-captcha" in (a.get("class") or "").split():
            self.widget = a
        # Skip nomodule fallbacks; keep full addresses, never relative.
        if tag == "script" and a.get("src") and "nomodule" not in a:
            self.scripts.append(urljoin(self.page_url, a["src"]))


session = requests.Session()
page = FriendlyPage("https://example.com/signup")
page.feed(session.get(page.page_url, timeout=30).text)

urljoin çağrısı boşuna orada değil. Kendi sunucusunda barındırılan bir widget’ın src değeri /js/site.min.js gibi göreli bir yol olabilir; CapSkip’in ise çekebileceği bir adrese ihtiyacı vardır: v2’de çözümü yürütmek için aynı script’i kendi tarayıcısında yükler. Yalın yolu verirseniz widget hiç yüklenmez.

2. Adım: sürümü seçin ve friendly_captcha metodunu çağırın

CapSkip sürüme sabit bir sırayla karar verir ve ilk cevapta durur: önce sizin verdiğiniz sürüm, sonra module_script olarak verdiğiniz script adresi, en son da varsayılan v1. Tuzak bu son adımdır. CapSkip’in sürüm okuyamadığı bir script, örneğin bundle edilmiş bir /assets/app.4f2a.js, tek kelime edilmeden v1 olarak çözülür ve ardından bir v2 sitesi her token’ı reddeder.

Bu yüzden sürüme kendi kodunuzda karar verin; orada tahmin yürütmeyi reddedebilirsiniz. Bir CDN adresindeki paket adı yoruma yer bırakmaz. Kendi sunucusunda barındırılan bir derleme çoğu zaman paket adını atar ama dosya adını korur; resmi WordPress eklentisi de böyle yapar: site.min.js v2’dir, widget.module.min.js veya widget.min.js ise v1’dir. Widget’ın kendi öznitelikleri son çaredir, çünkü iki sürüm seçeneklerini farklı adlandırır:

from urllib.parse import urlparse

V2_FILES = {"site.min.js", "site.compat.min.js"}
V1_FILES = {"widget.module.min.js", "widget.min.js", "widget.polyfilled.min.js"}

def friendly_version(page):
    # Package names are unambiguous, so check them first.
    for src in page.scripts:
        if "@friendlycaptcha/sdk" in src:
            return "v2", src
        if "friendly-challenge" in src:
            return "v1", src
    # Self-hosted builds usually keep the file name. Themes ship their own
    # site.min.js too, so only trust a path that says friendly.
    for src in [s for s in page.scripts if "friendly" in s.lower()]:
        name = urlparse(src).path.rsplit("/", 1)[-1]
        if name in V2_FILES:
            return "v2", src
        if name in V1_FILES:
            return "v1", src
    # Last resort: v2 and v1 name their widget options differently.
    if {"data-api-endpoint", "data-form-field-name"} & page.widget.keys():
        return "v2", None
    if {"data-puzzle-endpoint", "data-solution-field-name"} & page.widget.keys():
        return "v1", None
    raise RuntimeError("v1 or v2? Read the page and set it by hand.")

Dosya adı kontrolü yalnızca içinde friendly geçen yollara güvenir, çünkü bir tema, widget ile hiçbir ilgisi olmayan kendi site.min.js dosyasını yükleyebilir.

Ardından çağrıyı yapın. Bulduğunuz sürümü ve varsa script adresini gönderin. Açıkça verilen sürüm önceliklidir; v2’de ise adres, CapSkip’e sitenin yüklediği derlemenin tam olarak aynısıyla çözmesini söyler. None değeri istekten basitçe çıkarılır.

from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)

version, script = friendly_version(page)
result = solver.friendly_captcha(
    page.widget["data-sitekey"],
    "https://example.com/signup",
    version=version,
    module_script=script,
    # data-api-endpoint="eu" (v2) or data-puzzle-endpoint (v1).
    api_server=page.widget.get("data-api-endpoint")
    or page.widget.get("data-puzzle-endpoint"),
)

print(result["token"][:40])   # v2 tokens start with AQQA.

api_server satırı, Friendly Captcha’nın EU endpoint’ini kullanan siteleri ele alır. Bir EU sitesinin sitekey’i global endpoint’ten de token alır; dolayısıyla onu orada çözmek yalnızca sitenin kendi doğrulamasında başarısız olur ve bu, sürümdeki sessiz hatanın aynısıdır. İki öznitelik de yoksa değer None olur ve CapSkip global endpoint’i kullanır.

Sonuç sıradan bir dict nesnesidir. result["token"] gönderilecek dizedir, result["code"] başka bir çözücüden taşınan betikler için aynı dizeyi tutar, result["captchaId"] ise CapSkip’in bu iş için kullandığı referanstır. v1, v2, 1 veya 2 dışında bir sürüm, boş bir sitekey ya da metodun kabul etmediği bir seçenek, daha hiçbir şey gönderilmeden ValidationException yükseltir.

3. Adım: token’ı widget’ın kullandığı alana gönderin

Sürümler arasında farklı olan ikinci şey alan adıdır ve bir site bu adı widget öğesi üzerinde değiştirebilir:

import requests

# A renamed field wins; otherwise the default for the version.
field = (
    page.widget.get("data-form-field-name")          # v2 rename
    or page.widget.get("data-solution-field-name")   # v1 rename
    or ("frc-captcha-response" if version == "v2" else "frc-captcha-solution")
)

# session is the requests.Session that fetched the page.
resp = session.post(
    "https://example.com/signup",
    data={"email": "YOUR_EMAIL", field: result["token"]},
    # Browsers send the page as Referer; some servers refuse a post without it.
    headers={"Referer": page.page_url},
    timeout=30,
)
print(resp.status_code)

v1’e özgü bir durum: tek bir kısa çizgiye ayarlanmış data-solution-field-name, widget’ın hiç gizli alan yazmadığı ve token’ı sitenin kendi script’inin başka bir yolla gönderdiği anlamına gelir. Aşağıda anlatıldığı gibi o isteği birebir taklit edin.

Formun action özniteliği nereyi gösteriyorsa oraya gönderin ve formun diğer alanlarını da ekleyin; CSRF token’ı gibi gizli alanlar da buna dahildir. Sayfayı çekmek ve formu göndermek için tek bir requests.Session kullanmak sitenin çerezlerini korur; o CSRF token’ı da genellikle bu çerezlere bağlıdır. Sayfa URL’sini Referer olarak da gönderin: requests ne Referer ne de Origin başlığı gönderir ve aralarında Django’nun da bulunduğu bazı framework’ler, çerez ve token ne kadar doğru olursa olsun ikisi de olmayan bir HTTPS form gönderimini reddeder.

Token’ı data= ile gövdede gönderin, asla params= ile URL’de göndermeyin. Bir v2 token’ı yaklaşık altı kilobayttır ve bu, bir sunucunun URL uzunluğu sınırına takılmaya yeter; bir v1 token’ı ise noktalarla ayrılmış dört parçadan oluşur ve birkaç yüz karakterdir. Token’ı kırpmadan ya da yeniden kodlamadan olduğu gibi iletin. Tek bir gönderim için geçerlidir: Friendly Captcha’nın doğrulaması daha önce kullanılmış veya süresi dolmuş bir yanıtı reddeder, bu yüzden her form için yeniden çözün.

Bazı siteler formu klasik bir form gönderimiyle değil, JavaScript ve bir JSON gövdesiyle gönderir. Alan adı doğru olduğu hâlde gönderim başarısız oluyorsa DevTools’u açın, formu bir kez elle gönderin ve sayfanın gönderdiğini birebir kopyalayın.

4. Adım: aynı anda çok sayıda form ve çözücünün nerede çalıştığı

Python’daki AsyncCapSkip, engelleyici (blocking) istemcinin bir takma adı değil, httpx üzerine kurulu gerçek bir asenkron istemcidir; bu yüzden tek bir event loop aynı anda çok sayıda çözümü sürdürebilir. CapSkip’in aynı anda üzerinde çalışacağından fazlası hiçbir zaman çalışmasın diye bunları bir semaforla sınırlayın. CapSkip’in Friendly Captcha bölümündeki Max. Threads ayarının varsayılan değeri 10’dur.

import asyncio
from capskip import AsyncCapSkip

solver = AsyncCapSkip(host="127.0.0.1", port=8080)
limit = asyncio.Semaphore(10)   # match Friendly Captcha Max. Threads

async def solve(sitekey, url, version, script):
    async with limit:
        r = await solver.friendly_captcha(
            sitekey, url, version=version, module_script=script)
        return r["token"]

async def main(jobs):
    # jobs: (sitekey, url, version, script) tuples from Step 2
    return await asyncio.gather(*(solve(*j) for j in jobs))

Çözüm sürelerinin değişken olmasını bekleyin. Friendly Captcha her istek için gereken iş miktarını kendisi belirler ve daha önce çok gördüğü adresler için bu miktarı artırır; CapSkip de her v2 widget’ını gerçek bir tarayıcıda çözer. Metodun 120 saniyelik defaultTimeout yerine varsayılanı 300 saniye olan recaptchaTimeout ile yoklama yapmasının nedeni budur. CapSkip’in kendi sınırları da vardır. Friendly Captcha ayarlarına göre bir görev, boş bir thread için 250 saniye bekleyebilir ve ardından çözüm için 120 saniye harcayabilir; bu sürelerin sonunda CapSkip görevi başarısız sayar. Bu yüzden yavaş bir çözüm daha yüksek bir Row Timeout değeri gerektirir ve SDK’nın kendi zaman aşımı süresi, yani varsayılan 300 saniye ya da çağrı başına verdiğiniz bir timeout=, bu süreyi ve görevin bir thread için bekleyebileceği süreyi de kapsamalıdır. Yukarıdaki semafor bu bekleme süresini sıfıra yakın tutar. Uzun bir çalışma boyunca çözümler yavaşlıyorsa olağan sebep tek bir adreste artan zorluktur. Her çözüm aynı adresten gelmesin diye istek başına bir proxy’yi type ve uri anahtarları olan bir dict olarak verin ya da CapSkip’te bir proxy havuzu yapılandırın.

Örnekler 127.0.0.1 kullanıyor, çünkü betiğiniz ile çözücü aynı makinede olduğunda doğru adres budur. Betik başka herhangi bir yerde, örneğin bir VPS’te, bir konteynerde veya bir CI çalıştırıcısında çalışmaya başladığında loopback yanlış makineyi gösterir ve ilk çağrı NetworkException yükseltir. CapSkip’i Server moduna alın; o zaman ağ adresinizi veya genel IP’nizi dinler, böylece bunların hepsi ona aynı API üzerinden ulaşabilir. Rota internetten geçiyorsa statik bir genel IP kullanın ve beklediğiniz adresler için bir güvenlik duvarı kuralı ekleyin. Donanım yine sizin donanımınızdır ve çözüm başına ücretlendirme yine yoktur. İstemci ortam değişkenlerini kendiliğinden okumaz; bu yüzden, aşağıdaki tam örnekte olduğu gibi, CAPSKIP_HOST değerini kendi kodunuzda okuyup istemciye verin.

Tam çalışan örnek

# pip install capskip requests
import os
from html.parser import HTMLParser
from urllib.parse import urljoin, urlparse

import requests
from capskip import CapSkip
from capskip.exceptions import CapSkipError, ValidationException

PAGE_URL = "https://example.com/signup"
V2_FILES = {"site.min.js", "site.compat.min.js"}
V1_FILES = {"widget.module.min.js", "widget.min.js", "widget.polyfilled.min.js"}


class FriendlyPage(HTMLParser):
    def __init__(self, page_url):
        super().__init__()
        self.page_url, self.widget, self.scripts = page_url, {}, []

    def handle_starttag(self, tag, attrs):
        a = dict(attrs)
        if "frc-captcha" in (a.get("class") or "").split():
            self.widget = a
        if tag == "script" and a.get("src") and "nomodule" not in a:
            self.scripts.append(urljoin(self.page_url, a["src"]))


def friendly_version(page):
    for src in page.scripts:
        if "@friendlycaptcha/sdk" in src:
            return "v2", src
        if "friendly-challenge" in src:
            return "v1", src
    for src in [s for s in page.scripts if "friendly" in s.lower()]:
        name = urlparse(src).path.rsplit("/", 1)[-1]
        if name in V2_FILES or name in V1_FILES:
            return ("v2" if name in V2_FILES else "v1"), src
    keys = page.widget.keys()
    if {"data-api-endpoint", "data-form-field-name"} & keys:
        return "v2", None
    if {"data-puzzle-endpoint", "data-solution-field-name"} & keys:
        return "v1", None
    raise SystemExit("v1 or v2? Read the page and set it by hand.")


solver = CapSkip(
    apiKey=os.getenv("CAPSKIP_API_KEY", "capskip"),
    host=os.getenv("CAPSKIP_HOST", "127.0.0.1"),
    port=int(os.getenv("CAPSKIP_PORT", "8080")),
)
session = requests.Session()

page = FriendlyPage(PAGE_URL)
page.feed(session.get(PAGE_URL, timeout=30).text)
if not page.widget.get("data-sitekey"):
    raise SystemExit("No frc-captcha widget in the HTML; it may be built by JS.")

version, script = friendly_version(page)
try:
    result = solver.friendly_captcha(
        page.widget["data-sitekey"], PAGE_URL,
        version=version, module_script=script,
        api_server=page.widget.get("data-api-endpoint")
        or page.widget.get("data-puzzle-endpoint"),
    )
except ValidationException as exc:
    raise SystemExit(f"not sent: {exc}")
except CapSkipError as exc:
    raise SystemExit(f"solve failed: {exc!r}")

field = (page.widget.get("data-form-field-name")
         or page.widget.get("data-solution-field-name")
         or ("frc-captcha-response" if version == "v2" else "frc-captcha-solution"))

# Post wherever the form's action points, with its other fields.
# Browsers send the page as Referer; some servers refuse a post without it.
resp = session.post(PAGE_URL, data={"email": "YOUR_EMAIL", field: result["token"]},
                    headers={"Referer": PAGE_URL}, timeout=30)
print(resp.status_code, version, field)

Sürüm bir kez belirlenir ve iki kez kullanılır: çözüm için ve alan adı için; böylece ikisi asla birbiriyle çelişemez. Ayrıştırıcı hiç widget bulamazsa sayfa genellikle onu JavaScript ile oluşturuyordur ve sitekey’i render edilmiş sayfadan ya da onu oluşturan script’ten almanız gerekir. Ham endpoint’in kabul ettiği tüm parametreler şurada: Friendly Captcha API referansı.

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

GördüğünüzNedenDüzeltme
Site, CapSkip’in hatasız döndürdüğü bir token’ı reddediyorYanlış sürüm çözüldü; çoğu zaman v1 sessizce varsayılan olarak kullanıldığı içinSürüme 2. adımdaki gibi kodda karar verin ve onu iletin
Widget’ında data-api-endpoint (v2) veya data-puzzle-endpoint (v1) olan bir sitede token reddediliyorToken global endpoint’ten geldi, site ise EU endpoint’ini kullanıyorÖzniteliğin değerini api_server olarak verin
Token doğru ama gönderim yine de başarısız oluyorToken yanlış alana gitti, gönderim Referer olmadan yapıldı ya da site JSON gönderiyorYeniden adlandırma özniteliklerini kontrol edin ve sayfa URL’sini Referer olarak gönderin, ardından isteği DevTools’ta birebir taklit edin
Widget’ı kendisi barındıran bir sitede v2 çözümü başarısız oluyormodule_script göreli bir yoldu, bu yüzden widget hiç yüklenmedisrc değerini vermeden önce urljoin ile tam adrese çevirin
Her seferinde birkaç saniye içinde ERROR_CAPTCHA_UNSOLVABLE diyen bir ApiExceptionFriendly Captcha sitekey’i ya da sayfanın origin’ini reddediyor veya anahtarın hesabında v2 ya da EU endpoint’i etkin değilSitekey’i, sayfa URL’sini ve api_server değerini kontrol edin; yeniden denemek işe yaramaz
Daha hiçbir şey gönderilmeden ValidationExceptionBoş bir sitekey, v1, v2, 1 veya 2 dışında bir sürüm ya da bilinmeyen bir seçenekAyrıştırıcının widget’ı bulduğunu kontrol edin ve mesajda adı geçen seçeneği çıkarın
Uzun bir çalışma ilerledikçe çözümler yavaşlıyorFriendly Captcha yoğun bir adres için gereken işi artırıyorİstek başına proxy’ler ya da CapSkip’te bir proxy havuzu ekleyin
İki dakika ya da daha uzun süre sonra ERROR_CAPTCHA_UNSOLVABLE diyen bir ApiExceptionBir çözüm CapSkip’in 120 saniyelik Row Timeout süresini aştı ya da boş bir thread için 250 saniyelik Wait Timeout süresinden fazla beklediSemaforu Max. Threads değerinde tutun, proxy ekleyin ya da Friendly Captcha ayarlarında Row Timeout değerini yükseltin
300 saniye sonra TimeoutExceptionBir görevin boş bir thread için beklemesi ve ardından çözülmesi, SDK’nın yoklama süresinden uzun sürdüSemaforu Max. Threads değerinde tutun ya da daha uzun bir timeout= verin
İlk çağrıda NetworkExceptionCapSkip çalışmıyor ya da host ve port yanlışCapSkip’i başlatın, ardından Local modunda mı yoksa Server modunda mı olması gerektiğini kontrol edin

FAQ

Neden yalnızca module_script gönderip kararı CapSkip’e bırakmayalım?

Bunu yapabilirsiniz ve CDN’den yüklenen bir script için işe yarar, çünkü paket adı adreste yer alır. Risk, varsayılana geri düşme davranışındadır. Adres, CapSkip’in tanıdığı hiçbir derlemeyi işaret etmiyorsa CapSkip başarısız olmak yerine v1 çözer ve sonuçta bunu size söyleyen hiçbir şey olmaz. Kontrolü kendi kodunuzda yapmak, bu sessiz varsayılanı görebileceğiniz bir istisnaya dönüştürür.

Çözüm için benim tarafımda Selenium veya Playwright gibi bir tarayıcı gerekir mi?

Hayır. Betiğinizin tek ihtiyacı HTML’dir, onu da requests çeker. Tarayıcı gerektiren her iş CapSkip’in içinde gerçekleşir; CapSkip v2 widget’larını kendi tarayıcısında, v1 bulmacalarını ise doğrudan çözer. Başka nedenlerle zaten bir tarayıcı yönetiyorsanız aynı çağrı çalışır; bu durumda sitekey’i ve script adresini canlı sayfadan okuyun.

Bir VPS’teki ya da buluttaki bir betik, Windows bilgisayarımdaki bir çözücüyü kullanabilir mi?

Evet. Bağlantı ayarlarından CapSkip’i Server moduna alın; böylece loopback yerine bir ağ adresini dinler. Betiğin çalıştığı yerde CAPSKIP_HOST değişkenini ayarlayın ve tam örnekte olduğu gibi istemciye verin. Bir VPS, bir konteyner ve barındırılan bir çalıştırıcı; hepsi aynı HTTP API üzerinden bağlanır. Rota internetten geçiyorsa bir güvenlik duvarı kuralıyla birlikte statik bir genel IP kullanın. Çözücü sizin sahip olduğunuz donanımda kalır, dolayısıyla daha fazla çözüm çalıştırmak ödediğiniz tutarı asla değiştirmez.

Bunun C# rehberinden farkı nedir?

Ham endpoint, seçenekler ve sonuç alanları her CapSkip SDK’sında aynıdır; yalnızca yazımları değişir. Asıl fark bunların etrafındaki koddadır: standart kütüphanedeki ayrıştırıcı, sitenin çerezlerini taşıyan requests oturumu ve Python’da ayrı bir asenkron istemci olan AsyncCapSkip. .NET’te ise AsyncCapSkip, zaten asenkron olan CapSkipClient’ın başka bir adıdır. Bu iş akışının .NET sürümü şurada: C# Friendly Captcha rehberi. Python’da çözümleri eşzamanlı çalıştırma hakkında daha fazlası ise şurada: paralel çözme rehberi.

Kısa özet

Python ile Friendly Captcha çözmek için sayfayı ayrıştırarak frc-captcha öğesini ve script etiketlerini bulun, her src değerini tam bir adrese çevirin. v1 mi v2 mi olduğuna paket adından ya da widget’ın kendi özniteliklerinden karar verin; ikisi de bir şey söylemiyorsa bir istisna yükseltin. friendly_captcha metodunu sitekey, sayfa URL’si, bu sürüm ve script adresiyle çağırın; EU siteleri için api_server da ekleyin. Token’ı bir kez, gövdede, sürümün ve widget’ın belirttiği alanda gönderin.

Hacimle ilgili bir şey daha. Friendly Captcha zorluğun bedelini para olarak değil CPU zamanı olarak tahsil eder, bu yüzden yoğun bir günün maliyeti kendi makinenizdeki çözüm süresidir. Yerel olarak sınırsız captcha çözücü çalıştırın; o zaman sizi sınırlayan bir sayaç değil, yalnızca o makine ve ona verdiğiniz thread’ler olur.