MechanicalSoup’ta reCAPTCHA Nasıl Çözülür ve Form Nasıl Gönderilir

mechanicalsoup captcha - How to Solve reCAPTCHA in MechanicalSoup and Submit the Form

MechanicalSoup, CAPTCHA widget’ını hiçbir zaman çalıştırmaz; bu yüzden widget’ın dolduracağı alan formda genellikle bulunmaz ve bu alana değer atamak LinkNotFoundError fırlatır. Bir MechanicalSoup CAPTCHA sorununun çözümü üç adımdan oluşur. Sitekey’i MechanicalSoup’un zaten ayrıştırdığı sayfadan okuyun ve kendi Windows makinenizde çalışan CapSkip ile çözün. Ardından submit_selected çağrısından önce g-recaptcha-response alanını new_control ile kendiniz ekleyin. Geri kalanını MechanicalSoup halleder: oturum çerezi, gizli CSRF alanı ve katı bir sunucunun aradığı Referer başlığı. Bu rehber reCAPTCHA onay kutusunu, görünmez düğmeyi, v3’ü, Enterprise’ı, CapSkip’in çözdüğü diğer widget’ları, aynı anda çok sayıda girişi ve çözücüyü başka bir makinede çalıştırmayı ele alıyor.

Neye ihtiyacınız var

  • reCAPTCHA çözücüsü açık hâlde bir Windows makinesinde çalışan CapSkip.
  • Python 3.10 veya üzeri, capskip paketi ve MechanicalSoup. Örnekler capskip 1.3.0 ve MechanicalSoup 1.4.0 ile çalıştırıldı; bunlar requests, Beautiful Soup ve lxml paketlerini de beraberinde kurar.
  • Formun bulunduğu sayfanın URL’si. Sitekey, form alanları ve widget türü hep bu sayfanın HTML’sinden elde edilir.
  • Çözücü için bir adres. Local modunda CapSkip 127.0.0.1 üzerinden yalnızca o cihaza yanıt verir. Server modunda ise ağ adresinizi veya genel IP’nizi dinler, böylece bir VPS’teki ya da başka bir bilgisayardaki bir betik onu API üzerinden çağırabilir. İkisi de şurada ayarlanır: bağlantı ayarları.
# Python 3.10 or later; MechanicalSoup brings requests, bs4 and lxml
pip install capskip mechanicalsoup

1. Adım: sayfayı açın ve sitekey’i bulun

StatefulBrowser, ayrıştırılmış bir sayfanın eklendiği bir requests oturumudur. Sitenin ayarladığı her çerezi saklar, yönlendirmeleri izler ve sonunda vardığı adresi hatırlar. Giriş sayfasını açın ve reCAPTCHA öğesini arayın:

# pip install capskip mechanicalsoup
import mechanicalsoup

PAGE_URL = "https://example.com/login"

# One StatefulBrowser per session: it keeps cookies, follows redirects
# and sends the current page as Referer when it submits a form.
browser = mechanicalsoup.StatefulBrowser(raise_on_404=True)
browser.open(PAGE_URL)

# The checkbox div, or the button on invisible reCAPTCHA, holds the key.
widget = browser.page.select_one(".g-recaptcha[data-sitekey]")
if widget is None:
    raise RuntimeError("No reCAPTCHA widget in the HTML; see the v3 section.")
sitekey = widget["data-sitekey"]

# The form around the widget; a page often has a search form first.
browser.select_form(widget.find_parent("form") or 'form[method="post" i]')
print(sitekey, browser.url)

Onay kutusu widget’ı, g-recaptcha sınıfına ve bir data-sitekey özniteliğine sahip bir div’dir. Görünmez reCAPTCHA genellikle aynı sınıfı ve özniteliği bunun yerine gönder düğmesine koyar; böylece tek bir seçici ikisini de bulur. api.js’i render=explicit ile yükleyen bir sayfa widget’ı kendi script’inden oluşturur ve anahtarı grecaptcha.render çağrısına verir; bu yüzden anahtarı DevTools’ta o çağrıdan okuyun. Sayfadaki ilk formu değil, widget’ı çevreleyen formu seçin. Birçok site sayfanın üst kısmına bir arama formu koyar ve select_form varsayılan olarak onu seçer. Widget formunun dışında duruyorsa yedek yol POST ile gönderilen ilk formu alır; bu yüzden sayfada iki form varsa bunun yerine giriş formunun id’sini verin.

browser.url, sayfanın tüm yönlendirmelerden sonra geldiği adrestir. Çözücüye gönderilecek URL budur, çünkü bir sitekey kendi sitesinin alan adları için kayıtlıdır ve yanlış sayfa için yapılan bir çözüm boşa gider. raise_on_404, eksik bir sayfanın ileride değil, daha open aşamasında açıkça hata vermesini sağlar.

2. Adım: reCAPTCHA’yı CapSkip ile çözün

Çözümü capskip paketine yapılan tek bir çağrı yapar. CapSkip, CAPTCHA’yı Windows makinenizde çözer ve widget’ın forma koyacağı token’ı döndürür:

from capskip import CapSkip

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

# enterprise.js in place of api.js means reCAPTCHA Enterprise.
enterprise = int("recaptcha/enterprise.js" in str(browser.page))
if widget.name == "button" and widget.get("data-action"):
    # Google's v3 button binding: a g-recaptcha button with an action is v3.
    result = solver.recaptcha(sitekey=sitekey, url=browser.url, version="v3",
                              action=widget["data-action"], enterprise=enterprise)
else:
    # A g-recaptcha button, or data-size="invisible", is the invisible widget.
    invisible = widget.name == "button" or widget.get("data-size") == "invisible"
    result = solver.recaptcha(sitekey=sitekey, url=browser.url,
                              invisible=int(invisible), enterprise=enterprise)

token = result["code"]
print(token[:40])   # what the widget would have written into the form

Onay kutusu yalın çağrıdır; görünmez reCAPTCHA ise invisible değeri 1 olan aynı çağrıdır. data-action da taşıyan bir g-recaptcha düğmesi Google’ın v3 düğme bağlamasıdır; bu yüzden betik onu o action ile v3 olarak çözer. api.js yerine enterprise.js yükleyen sayfalar reCAPTCHA Enterprise çalıştırır ve enterprise=1 bunu CapSkip’e bildirir. Token, result["code"] içinde durur. Yaklaşık iki dakika geçerlidir ve yalnızca bir kez doğrulanır; bu yüzden göndermeden hemen önce çözün ve her giriş için yeniden çözün.

recaptcha, en fazla recaptchaTimeout süresi kadar (varsayılanı 300 saniye) yoklama yapar. Farklı bir değeri constructor’da ayarlayın, çünkü çağrı başına verilen bir timeout TypeError fırlatır: metot zaten kendi timeout değerini verir. CapSkip de kendi zamanlayıcısını çalıştırır. Bir görev boş bir reCAPTCHA thread’i için en fazla 250 saniye (Wait Timeout) bekler, ardından çözümün de kendine ait bir sınırı vardır: varsayılanı 250 saniye olan Row Timeout. Bunlardan biri dolduğunda CapSkip görevi başarısız sayar ve SDK, ERROR_CAPTCHA_UNSOLVABLE ile bir ApiException fırlatır. Burada kullanılan CapSkip istemcisi kendi HTTP çağrılarında hiçbir timeout ayarlamaz; bu yüzden bağlantıyı kabul edip hiç yanıt vermeyen bir CapSkip, çağrıyı açık tutar.

3. Adım: widget’ın yazacağı alanı ekleyin, ardından gönderin

Her MechanicalSoup CAPTCHA betiğini tökezleten adım budur. Gerçek bir tarayıcıda Google’ın script’i g-recaptcha-response adlı gizli bir textarea oluşturur ve CAPTCHA çözüldüğünde onu doldurur. MechanicalSoup sunucunun gönderdiği HTML’yi ayrıştırır ve hiç JavaScript çalıştırmaz; bu yüzden çoğu sayfada o alan var olmaz. Ona alışılmış yoldan değer atamak da bu durumda No valid element named g-recaptcha-response mesajıyla bir LinkNotFoundError fırlatır. new_control alanı seçili forma ekler:

# This line raises LinkNotFoundError on most pages: the widget script
# creates the field in a real browser, so the parsed HTML lacks it.
# browser["g-recaptcha-response"] = token

# Add the field the widget would have written, replacing any copy.
browser.new_control("hidden", "g-recaptcha-response", token)
browser["email"] = "YOUR_EMAIL"
browser["password"] = "YOUR_PASSWORD"

response = browser.submit_selected()
print(response.status_code, browser.url)

new_control, kendi alanını eklemeden önce aynı ada sahip her alanı kaldırır; bu, Google’ın noscript yedeğini koruyan sayfalarda önemlidir. O yedek, aynı ada sahip bir textarea içerir ve MechanicalSoup’un ayrıştırıcısı onu formun bir parçası olarak okur. Bu sayfalarda düz atama da çalışır, ama new_control tek satırla iki tür sayfayı da karşılar.

submit_selected, gizli CSRF token’ı dahil formun taşıdığı her alanı, sayfayı yükleyen aynı oturum üzerinden gönderir. Ayrıca sayfanın adresini Referer başlığı olarak gönderir. Düz requests hiç Referer göndermez, MechanicalSoup’un daha alt düzeydeki Browser.submit metodu da göndermez. Django dahil bazı framework’ler, çerez ve token ne kadar doğru olursa olsun, Referer ya da Origin içermeyen bir HTTPS form gönderimini reddeder. Bu yüzden sayfayı açan browser nesnesinde submit_selected ile devam edin. Formda birkaç gönder düğmesi varsa istediğinizin adını btnName olarak verin. Ayrıca 200 yanıtı girişin başarılı olduğunu kanıtlamaz. Birçok site reddedilen bir CAPTCHA’ya aynı form ve 200 ile yanıt verir; bu yüzden browser.page içinde yalnızca oturum açılmış bir sayfanın gösterdiği bir şeyi kontrol edin.

reCAPTCHA v3’te bulunacak bir widget yoktur

v3 kullanıcıya hiçbir şey sormaz. Site, Google’ın script’ini adresinde render= sonrasında kendi sitekey’i olacak şekilde yükler ve kendi script’i login gibi bir action adıyla bir token ister. Çoğu v3 sayfası daha sonra token’ı kendisi gizli bir input’a yazar. İki değeri de HTML’den okuyun, script’in api.js mi yoksa enterprise.js mi olduğunu not edin ve bunları version değeri v3 olacak şekilde iletin. Bir sayfa v3’ü bunun yerine bir düğmeye bağlıyorsa 2. adım bunu zaten ele alır.

import re

# A v3 page has no widget element; the key sits in the script tags.
html = str(browser.page)
key = re.search(r"recaptcha/(api|enterprise)\.js\?[^\"'<>\s]*?\brender=(?!explicit\b)([\w-]+)", html)
action = re.search(r"execute\(\s*[^,()]+,\s*\{\s*[\"']?action[\"']?\s*:\s*[\"']([\w/]+)", html)
if key is None:
    raise RuntimeError("No v3 render key in the HTML; check DevTools.")

v3 = solver.recaptcha(sitekey=key.group(2), url=browser.url, version="v3",
                      enterprise=int(key.group(1) == "enterprise"),
                      **({"action": action.group(1)} if action else {}))

browser.select_form('form[method="post" i]')
# The field the page's own script fills in; read its name in DevTools.
browser.new_control("hidden", "g-recaptcha-response", v3["code"])

Bir v3 sayfasında 1. adımı yalnızca browser.open satırına kadar çalıştırın, ardından 2. adımın çözücüyü oluşturan satırlarını, ardından bu bloğu, ardından da 3. adımın kimlik bilgilerini ve submit_selected çağrısını. action önemlidir, çünkü sitenin arka ucu genellikle token’ın beklediği action için verilip verilmediğini kontrol eder. Desen hiçbir action bulamazsa çağrı action olmadan gider ve CapSkip verify değerini kullanır; login bekleyen bir site bunu reddeder. execute çağrısı harici bir script’te bulunuyorsa action’ı DevTools’ta okuyun ve kendiniz verin. Alanın adı, sayfanın script’i hangi alanı dolduruyorsa odur; çoğu zaman g-recaptcha-response olur ama her zaman değil, bu yüzden formu bir kez elle göndererek DevTools’ta kontrol edin. Ardından kimlik bilgilerini doldurun ve yukarıdaki gibi submit_selected çağırın. Google’ın reCAPTCHA v3 dokümantasyonu bu desenlerin eşleştirdiği render ve execute çağrılarını gösterir. Python ile reCAPTCHA v3 çözme rehberi çağrının kendisini daha ayrıntılı ele alıyor.

Diğer widget’lar da aynı şekilde çalışır

CapSkip’in çözdüğü her widget, MechanicalSoup’ta aynı kalıbı izler: anahtarı okuyun, çözün, widget’ın oluşturacağı alanı ekleyin, gönderin. Yalnızca seçici, alanın adı ve metot değişir:

WidgetAnahtarın yeriEklenecek alancapskip metodu
reCAPTCHA v2 checkboxdiv.g-recaptcha, data-sitekeyg-recaptcha-responsesolver.recaptcha()
Görünmez reCAPTCHA v2g-recaptcha sınıfına sahip düğme ya da data-size=invisible özniteliğine sahip bir divg-recaptcha-responseinvisible=1 ile solver.recaptcha()
reCAPTCHA v3script adresinde api.js?render= veya enterprise.js?render= ya da data-action içeren bir g-recaptcha düğmesisayfanın script’inin doldurduğu inputversion="v3" ve action ile solver.recaptcha()
reCAPTCHA Enterpriseapi.js yerine enterprise.jsv2 veya v3 ile aynıenterprise=1 ile solver.recaptcha()
Cloudflare Turnstile widget’ıdiv.cf-turnstile, data-sitekeycf-turnstile-responsesolver.turnstile()
Friendly Captchadiv.frc-captcha, data-sitekeyv2’de frc-captcha-response, v1’de frc-captcha-solutionversion="v1" veya "v2" ile solver.friendly_captcha()
CaptchaFoxdiv.captchafox, data-sitekeycf-captcha-responsesolver.captchafox()

Ek bir kurala sahip olan CaptchaFox’tur. Onun token’ı, onu üreten tarayıcıya aittir. Bu yüzden sonuç bir userAgent taşıyorsa, göndermeden önce bu değeri set_user_agent ile browser nesnesine ayarlayın, çünkü MechanicalSoup aksi hâlde python-requests ve kendi adını gönderir. Python ile CaptchaFox çözme rehberi widget derlemesini ve yeniden denemeleri ele alıyor.

Tam çalışan örnek

İşte tek bir dosyada eksiksiz bir MechanicalSoup CAPTCHA girişi. login sayfayı yeni bir browser nesnesinde açar, onay kutusu, görünmez ya da v3 reCAPTCHA’yı bulur, çözer, alanı ekler ve gönderir:

# pip install capskip mechanicalsoup
# login.py: run with python login.py
import os
import re

import mechanicalsoup
from capskip import CapSkip, CapSkipError, TimeoutException

PAGE_URL = "https://example.com/login"

# render= carries a v3 key on api.js or enterprise.js; render=explicit is a v2 page.
V3_KEY = re.compile(r"recaptcha/(api|enterprise)\.js\?[^\"'<>\s]*?\brender=(?!explicit\b)([\w-]+)")
# The action passed to execute, whether the key is a string or a variable.
V3_ACTION = re.compile(r"execute\(\s*[^,()]+,\s*\{\s*[\"']?action[\"']?\s*:\s*[\"']([\w/]+)")

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")),
)


def solve_page(browser):
    """Solve the page's reCAPTCHA and select its form. Returns the token."""
    html = str(browser.page)
    enterprise = int("recaptcha/enterprise.js" in html)
    widget = browser.page.select_one(".g-recaptcha[data-sitekey]")
    if widget is not None:
        browser.select_form(widget.find_parent("form") or 'form[method="post" i]')
        if widget.name == "button" and widget.get("data-action"):
            # Google's v3 button binding: plain api.js, the action on the button.
            return solver.recaptcha(sitekey=widget["data-sitekey"], url=browser.url, version="v3",
                                    action=widget["data-action"], enterprise=enterprise)["code"]
        invisible = widget.name == "button" or widget.get("data-size") == "invisible"
        return solver.recaptcha(sitekey=widget["data-sitekey"], url=browser.url,
                                invisible=int(invisible), enterprise=enterprise)["code"]
    key = V3_KEY.search(html)
    if key is None:
        raise RuntimeError(f"No reCAPTCHA found on {browser.url}")
    action = V3_ACTION.search(html)
    browser.select_form('form[method="post" i]')
    return solver.recaptcha(sitekey=key.group(2), url=browser.url, version="v3",
                            enterprise=int(key.group(1) == "enterprise"),
                            **({"action": action.group(1)} if action else {}))["code"]


def login(email, password, page_url=PAGE_URL):
    # A new browser per login, so sessions never share cookies.
    with mechanicalsoup.StatefulBrowser(raise_on_404=True) as browser:
        browser.open(page_url)
        token = solve_page(browser)
        browser.new_control("hidden", "g-recaptcha-response", token)
        browser["email"] = email
        browser["password"] = password
        response = browser.submit_selected()
        return response.status_code, browser.url


if __name__ == "__main__":
    try:
        print(login("YOUR_EMAIL", "YOUR_PASSWORD"))
    except TimeoutException:
        raise SystemExit("gave up after recaptchaTimeout")
    except CapSkipError as exc:
        raise SystemExit(f"solve failed: {type(exc).__name__}: {exc}")
    except mechanicalsoup.LinkNotFoundError as exc:
        # A 404, or a form or field the page does not have.
        raise SystemExit(f"not found: {str(exc) or PAGE_URL}")
    except RuntimeError as exc:
        raise SystemExit(str(exc))

login fonksiyonuna yapılan her çağrı kendi StatefulBrowser nesnesini oluşturur ve iş bitince onu kapatır; böylece iki giriş hiçbir zaman çerez paylaşmaz. with bloğu, bir adım başarısız olsa bile oturumu kapatır. Çoğu hata nereye bakmanız gerektiğini söyler. LinkNotFoundError bir 404, eksik bir form ya da adı sayfadakiyle eşleşmeyen bir alan anlamına gelir. RuntimeError, sayfanın betiğin tanıdığı hiçbir reCAPTCHA göstermediği anlamına gelir; bu yüzden sayfayı bir web tarayıcısında açın ve neyin yüklendiğini kontrol edin. Orada sorunsuz yükleniyorsa browser.open(page_url).status_code değerini yazdırın: 403, sitenin varsayılan user agent’ı reddettiği anlamına gelir. CapSkipError ise çözümün kendisinden gelir; bu durumda aşağıdaki hata tablosu geçerlidir.

Aynı anda çok sayıda giriş ve başka bir makinedeki çözücü

MechanicalSoup senkron çalışır, burada kullanılan CapSkip istemcisi de öyle. Bu yüzden bir MechanicalSoup CAPTCHA işinin üstesinden gelmesi gereken çok sayıda giriş olduğunda, onları yan yana çalıştırmanın en basit yolu thread’lerdir. Her thread için bir StatefulBrowser tutun (login bunu zaten yapıyor) ve havuzu, CapSkip’in reCAPTCHA için varsayılan Max. Threads değeri olan on ile sınırlayın. Bunun ötesinde kuyruğa alınan bir görev boş bir thread bekler ve uzun bir kuyruk, SDK’nın yoklama yaptığı 300 saniyeden yer. max_workers değerini yükseltmeden önce CapSkip’in reCAPTCHA ayarlarında Max. Threads değerini yükseltin. Tam örneği login.py olarak kaydedin ve bunu yanındaki ikinci bir dosyadan çalıştırın:

# pool.py: save next to login.py, the full example, and run this file
from concurrent.futures import ThreadPoolExecutor

from login import login

accounts = [("YOUR_EMAIL_1", "YOUR_PASSWORD_1"), ("YOUR_EMAIL_2", "YOUR_PASSWORD_2")]


def attempt(account):
    # One failed login must not stop the others.
    try:
        return login(*account)
    except Exception as exc:
        return f"{type(exc).__name__}: {exc}"


# Ten at a time, to match CapSkip's default Max. Threads for reCAPTCHA.
with ThreadPoolExecutor(max_workers=10) as pool:
    for account, outcome in zip(accounts, pool.map(attempt, accounts)):
        print(account[0], outcome)

pool.map sonuçları hesapların verildiği sırayla döndürür ve attempt bir hatayı bir değere dönüştürür; böylece tek bir kötü giriş diğerlerini durdurmaz. Bu havuzdan geçen yirmi beş giriş, bir mock çözücüye karşı hiçbir zaman aynı anda ondan fazla çözümü işlemde tutmadı.

Betik ve CapSkip aynı Windows bilgisayarı paylaştığı sürece 127.0.0.1 doğru adrestir. Betiği bir VPS’e, başka bir makinedeki zamanlanmış bir işe ya da bir konteynere taşıdığınızda loopback yanlış makineyi gösterir; bu yüzden ilk çözüm bir NetworkException fırlatır. CapSkip’i Server moduna alın; o zaman ağ adresinizi veya genel IP’nizi dinler, böylece bunların hepsi onu aynı API üzerinden çağırabilir. Rota internetten geçiyorsa statik bir genel IP kullanın. CapSkip, Server modu başladığında portunu her adrese açar; bu yüzden erişimi beklediğiniz makinelerle sınırlı tutmak için diğer adreslere yönelik bir Windows Firewall engelleme kuralı ekleyin. Bu hâlâ sizin kendi makinenizdir ve çözüm başına ücretlendirme hâlâ yoktur. İstemci ortam değişkenlerini kendiliğinden okumaz; tam örneğin CAPSKIP_HOST değerini okuyup istemciye vermesinin nedeni budur.

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

GördüğünüzNedenDüzeltme
LinkNotFoundError: No valid element named g-recaptcha-responseWidget’ın script’i hiç çalışmadı, bu yüzden oluşturduğu alan ayrıştırılan formda yokGöndermeden önce alanı new_control ile ekleyin
Token arama formuyla gönderiliyor ya da select_form LinkNotFoundError fırlatıyorSayfada önce başka bir form var ya da widget formunun dışında duruyorWidget’ı çevreleyen formu ya da giriş formunu id’sine göre seçin
Yeni bir token olduğu hâlde 403 ya da CSRF hatasıGönderim yeni bir oturumda, requests.post ile ya da Browser.submit ile yapıldı; bu yüzden çerez ya da Referer eksiktiFormu, sayfayı açan browser nesnesinde submit_selected ile doldurup gönderin
CapSkip bir token döndürdüğü hâlde site CAPTCHA’nın başarısız olduğunu söylüyorGörünmez bir widget onay kutusu olarak çözüldü, bir v3 action’ı sayfanınkinden farklı, bir Enterprise sayfası enterprise=1 olmadan çözüldü ya da token iki dakikadan eskiydiinvisible=1, enterprise=1 ya da sayfanın action değerini verin ve göndermeden hemen önce çözün
TypeError: capskip.solver.CapSkip.solve() got multiple values for keyword argument timeoutrecaptcha metoduna çağrı başına bir timeout verildiBunun yerine recaptchaTimeout değerini CapSkip constructor’ında ayarlayın
ERROR_CAPTCHA_UNSOLVABLE içeren ApiExceptionCapSkip görevi kendi zamanlayıcısına göre başarısız saydı ya da sitekey ile sayfa URL’si birbirine ait değilbrowser.url değerini ve sayfanın kendi sitekey’ini gönderin; aynı anda en fazla on çözümü işlemde tutun
300 saniye sonra TimeoutExceptionBoş bir thread’i bekleme süresi ile çözüm toplamda SDK’nın sınırını aştıHavuzu Max. Threads değeriyle sınırlayın ya da constructor’da recaptchaTimeout değerini yükseltin
Gerçek bir tarayıcıda sorunsuz yüklenen bir sayfada No reCAPTCHA found hatasıSite, python-requests ve MechanicalSoup adlarını içeren varsayılan user agent’ı reddettiği için 403 ile yanıt verdiStatefulBrowser nesnesini oluştururken user_agent olarak bir tarayıcı user agent’ı verin
İlk çözümde NetworkExceptionCapSkip çalışmıyor, host ya da port yanlış veya betik başka bir makinede çalışıyorCapSkip’i başlatın, ardından Local modunda mı yoksa Server modunda mı olması gerektiğini kontrol edin

FAQ

Neden düz requests yerine MechanicalSoup kullanmalı?

Formu yeniden kurma zahmetinden kurtarır. requests ile gizli alanları kendiniz toplar, oturumu korur, formun action değerini çözümler ve Referer başlığını ayarlarsınız; bunlardan herhangi birini atlamak, iyi bir token’ın reddedilmesine yol açar. MechanicalSoup bu dördünü de ayrıştırılmış sayfadan yapar; böylece CAPTCHA için eklediğiniz tek şey o tek alan olur. Formlarını JavaScript ile oluşturan sayfalar için ikisi de yeterli değildir ve bir tarayıcı otomasyon aracı daha uygundur.

CAPTCHA’dan sonra oturum açık kalır mı?

Evet. StatefulBrowser, sitenin girişten sonra ayarladığı çerezleri saklar; böylece aynı browser nesnesinde sonradan açtığınız sayfalarda oturum açık olur. Tam örnek, login döndüğünde browser nesnesini kapatır; bu yüzden sonraki isteklerinizi o with bloğunun içine taşıyın. Ya da browser nesnesini with olmadan oluşturun, döndürün ve işiniz bittiğinde browser.close() çağırın.

VPS’teki bir MechanicalSoup betiği, Windows bilgisayarımdaki CapSkip’i kullanabilir mi?

Evet. Bağlantı ayarlarından CapSkip’i Server moduna alın; böylece loopback yerine bir ağ adresini dinler. Ardından betiğin çalıştığı yerde, tam örneğin okuduğu CAPSKIP_HOST değişkenini ayarlayın. Bir VPS, bir cron işi ve bir konteyner; hepsi aynı HTTP API üzerinden bağlanır. Rota internetten geçiyorsa statik bir genel IP kullanın ve porta ulaşmaması gereken adresler için bir Windows Firewall engelleme kuralı ekleyin. Çözücü sizin sahip olduğunuz donanımda kalır; bu yüzden giriş sayısı artsa da ödediğiniz tutar değişmez.

Kısa özet

Bir MechanicalSoup CAPTCHA sorununun çözümü tek bir eksik alana dayanır, çünkü MechanicalSoup o alanı yazan widget’ı çalıştıramaz. Sayfayı bir StatefulBrowser ile açın ve widget’ı çevreleyen formu seçin. Sitekey’i g-recaptcha öğesinden ya da render anahtarını ve action’ı bir v3 sayfasının script’lerinden okuyun ve browser.url kullanarak CapSkip ile çözün. g-recaptcha-response alanını new_control ile ekleyin, geri kalanını doldurun ve aynı browser nesnesinde submit_selected çağırın; bu çağrı çerezleri, CSRF alanını ve Referer başlığını da beraberinde getirir. Göndermeden hemen önce çözün, aynı anda en fazla on girişi işlemde tutun ve betik başka bir yerde çalıştığında CapSkip’i Server moduna alın.

MechanicalSoup hiçbir zaman tarayıcı başlatmadığı için bir scraper’ı hafif tutar. Onu kendi Windows makinenizde çalışan bir captcha çözücü ile eşleştirin; böylece ağır iş kontrolünüzdeki donanımda kalır ve çözüm başına hiçbir fatura çıkmaz.