Cara Memecahkan reCAPTCHA di MechanicalSoup dan Mengirim Formulir

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

MechanicalSoup tidak pernah menjalankan widget CAPTCHA, jadi kolom yang seharusnya diisi widget biasanya tidak ada di formulir, dan mengisinya memunculkan LinkNotFoundError. Solusi CAPTCHA di MechanicalSoup terdiri dari tiga langkah. Baca sitekey dari halaman yang sudah di-parse MechanicalSoup, lalu pecahkan dengan CapSkip yang berjalan di mesin Windows Anda sendiri. Kemudian tambahkan sendiri kolom g-recaptcha-response dengan new_control sebelum Anda memanggil submit_selected. MechanicalSoup menangani sisanya: cookie sesi, kolom CSRF tersembunyi, dan header Referer yang diperiksa oleh server yang ketat. Panduan ini membahas checkbox reCAPTCHA, tombol invisible, v3, Enterprise, widget lain yang dipecahkan CapSkip, banyak login sekaligus, dan menjalankan solver di mesin lain.

Apa yang Anda butuhkan

  • CapSkip yang berjalan di komputer Windows, dengan solver reCAPTCHA diaktifkan.
  • Python 3.10 atau lebih baru, paket capskip, dan MechanicalSoup. Contoh kodenya dijalankan dengan capskip 1.3.0 dan MechanicalSoup 1.4.0, yang ikut menginstal requests, Beautiful Soup, dan lxml.
  • URL halaman yang berisi formulir. Sitekey, kolom formulir, dan tipe widget semuanya diambil dari HTML halaman itu.
  • Sebuah alamat untuk pemecah CAPTCHA. Dalam mode Local, CapSkip menjawab di 127.0.0.1 hanya untuk perangkat itu. Dalam mode Server, CapSkip mendengarkan di alamat jaringan atau IP publik Anda, sehingga skrip di VPS atau PC lain dapat memanggilnya melalui API. Keduanya diatur di pengaturan koneksi.
# Python 3.10 or later; MechanicalSoup brings requests, bs4 and lxml
pip install capskip mechanicalsoup

Langkah 1: buka halaman dan temukan sitekey

StatefulBrowser adalah session requests yang dilengkapi halaman hasil parsing. Objek ini menyimpan setiap cookie yang disetel situs, mengikuti redirect, dan mengingat alamat akhir yang dicapainya. Buka halaman login dan cari elemen reCAPTCHA:

# 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)

Widget checkbox adalah sebuah div dengan class g-recaptcha dan atribut data-sitekey. Invisible reCAPTCHA biasanya menaruh class dan atribut yang sama di tombol submit, sehingga satu selector bisa menemukan keduanya. Halaman yang memuat api.js dengan render=explicit membangun widget dari skripnya sendiri dan memberikan key ke grecaptcha.render, jadi baca key itu dari pemanggilan tersebut di DevTools. Pilih formulir yang membungkus widget, bukan formulir pertama di halaman. Banyak situs menaruh formulir pencarian di header, dan select_form memilih formulir itu secara default. Jika widget berada di luar formulirnya, fallback mengambil formulir pertama yang dikirim dengan metode POST, jadi berikan id formulir login sebagai gantinya jika halaman memiliki dua formulir.

browser.url adalah alamat asal halaman setelah semua redirect. Itulah URL yang harus dikirim ke solver, karena sitekey didaftarkan untuk domain situsnya dan pemecahan untuk halaman yang salah akan sia-sia. raise_on_404 membuat halaman yang tidak ada langsung gagal dengan jelas saat open dipanggil, bukan di bagian kode setelahnya.

Langkah 2: pecahkan reCAPTCHA dengan CapSkip

Satu pemanggilan ke paket capskip sudah cukup untuk memecahkannya. CapSkip memecahkan CAPTCHA di mesin Windows Anda dan mengembalikan token yang seharusnya dimasukkan widget ke formulir:

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

Checkbox adalah pemanggilan polosnya, dan invisible reCAPTCHA adalah pemanggilan yang sama dengan invisible disetel ke 1. Tombol g-recaptcha yang juga membawa data-action adalah binding tombol v3 dari Google, jadi skrip memecahkannya sebagai v3 dengan action tersebut. Halaman yang memuat enterprise.js alih-alih api.js menjalankan reCAPTCHA Enterprise, dan enterprise=1 memberi tahu CapSkip tentang hal itu. Token berada di result["code"]. Token ini berlaku sekitar dua menit dan hanya bisa diverifikasi sekali, jadi pecahkan tepat sebelum Anda mengirim formulir dan pecahkan lagi untuk setiap login.

recaptcha melakukan polling hingga batas recaptchaTimeout, 300 detik secara default. Setel nilai lain di konstruktor, karena timeout yang diberikan per panggilan memunculkan TypeError: metode ini sudah memberikan timeout-nya sendiri. CapSkip juga menjalankan timer-nya sendiri. Sebuah tugas menunggu hingga 250 detik (Wait Timeout) untuk thread reCAPTCHA yang kosong, lalu pemecahan memiliki batasnya sendiri, yaitu Row Timeout, 250 detik secara default. Jika salah satunya habis, CapSkip menggagalkan tugas itu dan SDK memunculkan ApiException dengan ERROR_CAPTCHA_UNSOLVABLE. Klien CapSkip yang dipakai di sini tidak menyetel timeout pada pemanggilan HTTP-nya sendiri, jadi CapSkip yang menerima koneksi tetapi tidak pernah menjawab akan membuat pemanggilan itu tetap terbuka.

Langkah 3: tambahkan kolom yang seharusnya ditulis widget, lalu kirim

Inilah langkah yang menjegal setiap skrip CAPTCHA MechanicalSoup. Di browser sungguhan, skrip Google membuat textarea tersembunyi bernama g-recaptcha-response dan mengisinya saat CAPTCHA terpecahkan. MechanicalSoup mem-parse HTML yang dikirim server dan tidak menjalankan JavaScript, jadi di sebagian besar halaman, kolom itu tidak ada. Akibatnya, mengisinya dengan cara biasa memunculkan LinkNotFoundError dengan pesan No valid element named g-recaptcha-response. new_control menambahkan kolom itu ke formulir yang dipilih:

# 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 menghapus kolom apa pun yang bernama sama sebelum menambahkan kolomnya sendiri, dan ini penting di halaman yang mempertahankan fallback noscript dari Google. Fallback itu berisi textarea dengan nama yang sama, dan parser MechanicalSoup membacanya sebagai bagian dari formulir. Di halaman seperti itu, pengisian biasa juga berhasil, tetapi new_control menangani kedua jenis halaman dengan satu baris.

submit_selected mengirim setiap kolom yang dibawa formulir, termasuk token CSRF tersembunyi, pada sesi yang sama yang memuat halaman. Metode ini juga mengirim alamat halaman sebagai header Referer. requests biasa tidak mengirimnya, begitu pula metode tingkat rendah Browser.submit milik MechanicalSoup. Beberapa framework, salah satunya Django, menolak form post HTTPS tanpa Referer atau Origin, meskipun cookie dan token-nya sudah benar. Jadi tetaplah memakai submit_selected pada browser yang membuka halaman. Jika formulir punya beberapa tombol submit, berikan nama tombol yang Anda inginkan sebagai btnName. Selain itu, respons 200 tidak membuktikan bahwa login berhasil. Banyak situs menjawab CAPTCHA yang ditolak dengan formulir yang sama dan kode 200, jadi periksa browser.page untuk mencari sesuatu yang hanya ditampilkan halaman setelah login.

reCAPTCHA v3 tidak punya widget untuk ditemukan

v3 tidak menanyakan apa pun kepada pengguna. Situs memuat skrip Google dengan sitekey di alamatnya, setelah render=, dan skrip milik situs itu sendiri meminta token dengan nama action seperti login. Sebagian besar halaman v3 lalu menuliskan sendiri token itu ke input tersembunyi. Baca kedua nilai itu dari HTML, catat apakah skripnya api.js atau enterprise.js, lalu teruskan dengan version disetel ke v3. Jika halaman justru mengikat v3 ke sebuah tombol, Langkah 2 sudah menanganinya.

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

Di halaman v3, jalankan Langkah 1 hanya sampai browser.open, lalu baris di Langkah 2 yang membuat solver, lalu blok ini, lalu kredensial dan submit_selected dari Langkah 3. Action itu penting karena backend situs biasanya memeriksa bahwa token diterbitkan untuk action yang diharapkannya. Jika pola tidak menemukan action, pemanggilan dikirim tanpa action dan CapSkip memakai verify, yang akan ditolak oleh situs yang mengharapkan login. Jika pemanggilan execute berada di skrip eksternal, baca action-nya di DevTools dan berikan sendiri. Nama kolomnya adalah apa pun yang diisi oleh skrip halaman, sering kali g-recaptcha-response tetapi tidak selalu, jadi periksa di DevTools dengan mengirim formulir sekali secara manual. Kemudian isi kredensial dan panggil submit_selected seperti di atas. Dokumentasi reCAPTCHA v3 dari Google menunjukkan pemanggilan render dan execute yang dicocokkan oleh pola ini. Panduan memecahkan reCAPTCHA v3 di Python membahas pemanggilan itu sendiri secara lebih mendalam.

Widget lain bekerja dengan cara yang sama

Setiap widget yang dipecahkan CapSkip mengikuti pola yang sama di MechanicalSoup: baca key, pecahkan, tambahkan kolom yang seharusnya dibuat widget, lalu kirim. Hanya selector, nama kolom, dan metodenya yang berubah:

WidgetLetak keyKolom yang ditambahkanMetode capskip
reCAPTCHA v2 checkboxdiv.g-recaptcha, data-sitekeyg-recaptcha-responsesolver.recaptcha()
reCAPTCHA v2 invisibletombol dengan class g-recaptcha, atau div dengan data-size=invisibleg-recaptcha-responsesolver.recaptcha() dengan invisible=1
reCAPTCHA v3api.js?render= atau enterprise.js?render= di alamat skrip, atau tombol g-recaptcha dengan data-actioninput yang diisi oleh skrip halamansolver.recaptcha() dengan version="v3" dan action-nya
reCAPTCHA Enterpriseenterprise.js sebagai pengganti api.jssama seperti v2 atau v3solver.recaptcha() dengan enterprise=1
Widget Cloudflare Turnstilediv.cf-turnstile, data-sitekeycf-turnstile-responsesolver.turnstile()
Friendly Captchadiv.frc-captcha, data-sitekeyfrc-captcha-response di v2, frc-captcha-solution di v1solver.friendly_captcha() dengan version="v1" atau "v2"
CaptchaFoxdiv.captchafox, data-sitekeycf-captcha-responsesolver.captchafox()

CaptchaFox adalah satu-satunya yang punya aturan tambahan. Token-nya terikat pada browser yang menerbitkannya. Jadi jika hasilnya membawa userAgent, setel nilai itu pada browser MechanicalSoup dengan set_user_agent sebelum Anda mengirim formulir, karena jika tidak, MechanicalSoup mengirim python-requests beserta namanya sendiri. Lihat panduan memecahkan CaptchaFox di Python untuk pembahasan build widget dan percobaan ulangnya.

Contoh lengkap yang berfungsi

Berikut login lengkap dengan CAPTCHA di MechanicalSoup dalam satu file. login membuka halaman di browser baru, menemukan reCAPTCHA checkbox, invisible, atau v3, memecahkannya, menambahkan kolom, lalu mengirim formulir:

# 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))

Setiap pemanggilan login membuat StatefulBrowser-nya sendiri dan menutupnya setelah selesai, jadi dua login tidak pernah berbagi cookie. Blok with menutup sesi bahkan saat salah satu langkah gagal. Sebagian besar kegagalan menunjukkan di mana Anda harus mencari. LinkNotFoundError berarti 404, formulir yang tidak ada, atau nama kolom yang tidak cocok dengan halaman. RuntimeError berarti halaman tidak menampilkan reCAPTCHA yang dikenali skrip, jadi buka halaman itu di browser sungguhan dan periksa apa yang dimuat. Jika di sana halaman termuat dengan baik, cetak browser.open(page_url).status_code: 403 berarti situs menolak user agent bawaan. CapSkipError berasal dari pemecahan itu sendiri, jadi tabel error di bawah berlaku.

Banyak login sekaligus, dan solver di mesin lain

MechanicalSoup bersifat sinkron, begitu pula klien CapSkip yang dipakai di sini. Jadi jika pekerjaan CAPTCHA MechanicalSoup harus menyelesaikan banyak login, thread adalah cara paling sederhana untuk menjalankannya secara berdampingan. Gunakan satu StatefulBrowser per thread, yang sudah dilakukan login, dan batasi pool hingga sepuluh, yaitu nilai default Max. Threads untuk reCAPTCHA di CapSkip. Tugas yang mengantre melewati batas itu menunggu thread yang kosong, dan antrean panjang menggerus 300 detik yang dipakai SDK untuk polling. Naikkan Max. Threads di pengaturan reCAPTCHA pada CapSkip sebelum Anda menaikkan max_workers. Simpan contoh lengkap sebagai login.py, lalu jalankan kode ini dari file kedua di sebelahnya:

# 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 mengembalikan hasil sesuai urutan akun yang diberikan, dan attempt mengubah kegagalan menjadi sebuah nilai, jadi satu login yang gagal tidak menghentikan sisanya. Dua puluh lima login melalui pool ini tidak pernah menjalankan lebih dari sepuluh pemecahan bersamaan saat diuji dengan solver tiruan (mock).

127.0.0.1 sudah benar selama skrip dan CapSkip berada di PC Windows yang sama. Pindahkan skrip ke VPS, job terjadwal di mesin lain, atau container, maka loopback menunjuk ke mesin yang salah, sehingga pemecahan pertama memunculkan NetworkException. Alihkan CapSkip ke mode Server dan ia akan mendengarkan di alamat jaringan atau IP publik Anda, sehingga semua lingkungan itu dapat memanggilnya melalui API yang sama. Gunakan IP publik statis jika rutenya melewati internet. CapSkip membuka port-nya untuk semua alamat saat mode Server dimulai, jadi untuk membatasinya hanya pada mesin yang Anda harapkan, tambahkan aturan blokir Windows Firewall untuk alamat lainnya. Mesinnya tetap milik Anda sendiri, dan pemecahannya tetap tanpa kuota. Klien tidak membaca variabel lingkungan dengan sendirinya, itulah sebabnya contoh lengkap membaca CAPSKIP_HOST lalu meneruskannya ke klien.

Kesalahan umum dan artinya

Apa yang Anda lihatPenyebabPerbaiki
LinkNotFoundError: No valid element named g-recaptcha-responseSkrip widget tidak pernah berjalan, jadi kolom yang dibuatnya tidak ada di formulir hasil parsingTambahkan kolom itu dengan new_control sebelum mengirim formulir
Token terkirim bersama formulir pencarian, atau select_form memunculkan LinkNotFoundErrorHalaman memiliki formulir lain di urutan pertama, atau widget berada di luar formulirnyaPilih formulir yang membungkus widget, atau formulir login berdasarkan id-nya
403 atau error CSRF meski token masih baruFormulir dikirim pada sesi baru, melalui requests.post, atau melalui Browser.submit, sehingga cookie atau Referer-nya tidak adaIsi dan kirim formulir pada browser yang membuka halaman, dengan submit_selected
Situs menyatakan CAPTCHA gagal padahal CapSkip mengembalikan tokenWidget invisible dipecahkan sebagai checkbox, action v3 berbeda dari action halaman, halaman Enterprise dipecahkan tanpa enterprise=1, atau token sudah berumur lebih dari dua menitBerikan invisible=1, enterprise=1, atau action halaman, dan pecahkan tepat sebelum mengirim formulir
TypeError: capskip.solver.CapSkip.solve() got multiple values for keyword argument timeoutTimeout diberikan ke recaptcha per panggilanSetel recaptchaTimeout di konstruktor CapSkip sebagai gantinya
ApiException dengan ERROR_CAPTCHA_UNSOLVABLECapSkip menggagalkan tugas karena batas waktunya sendiri, atau sitekey dan URL halaman tidak saling cocokKirim browser.url dan sitekey milik halaman itu sendiri, dan jaga agar paling banyak sepuluh pemecahan berjalan bersamaan
TimeoutException setelah 300 detikWaktu menunggu thread yang kosong ditambah pemecahannya melewati batas SDKBatasi ukuran pool sesuai Max. Threads, atau naikkan recaptchaTimeout di konstruktor
No reCAPTCHA found, padahal halaman termuat dengan baik di browser sungguhanSitus menjawab dengan 403 karena menolak user agent bawaan, yang menyebut python-requests dan MechanicalSoupBerikan user agent browser sebagai user_agent saat Anda membuat StatefulBrowser
NetworkException pada solve pertamaCapSkip tidak berjalan, host atau port-nya salah, atau skrip berjalan di mesin lainJalankan CapSkip, lalu periksa apakah ia harus berada di mode Local atau mode Server

FAQ

Mengapa memakai MechanicalSoup, bukan requests biasa?

MechanicalSoup menghemat pekerjaan menyusun ulang formulir. Dengan requests, Anda mengumpulkan sendiri kolom tersembunyi, menjaga sesi, me-resolve action formulir, dan menyetel Referer, dan melewatkan salah satunya saja sudah membuat token yang valid ditolak. MechanicalSoup melakukan keempatnya dari halaman hasil parsing, jadi satu-satunya yang Anda tambahkan untuk CAPTCHA adalah satu kolom itu. Untuk halaman yang membangun formulirnya dengan JavaScript, keduanya tidak cukup, dan alat berbasis browser lebih cocok.

Apakah sesi tetap dalam keadaan login setelah CAPTCHA?

Ya. StatefulBrowser menyimpan cookie yang disetel situs setelah login, jadi halaman yang Anda buka berikutnya di browser yang sama sudah dalam keadaan login. Contoh lengkap menutup browser-nya saat fungsi login selesai, jadi pindahkan permintaan lanjutan Anda ke dalam blok with tersebut. Atau buat browser tanpa with, kembalikan objek itu dari fungsi, dan panggil browser.close() setelah selesai.

Bisakah skrip MechanicalSoup di VPS memakai CapSkip di PC Windows saya?

Bisa. Alihkan CapSkip ke mode Server di pengaturan koneksi agar ia mendengarkan di alamat jaringan, bukan di loopback. Lalu setel CAPSKIP_HOST di tempat skrip berjalan, yang dibaca oleh contoh lengkap. VPS, cron job, dan container semuanya terhubung melalui API HTTP yang sama. Gunakan IP publik statis jika rutenya melewati internet, dan tambahkan aturan blokir Windows Firewall untuk alamat yang tidak boleh menjangkau port tersebut. Solver tetap berada di perangkat keras milik Anda, jadi lebih banyak login tidak pernah mengubah biaya yang Anda bayar.

Versi singkatnya

Solusi CAPTCHA di MechanicalSoup bermuara pada satu kolom yang hilang, karena MechanicalSoup tidak bisa menjalankan widget yang menulisnya. Buka halaman dengan StatefulBrowser dan pilih formulir yang membungkus widget. Baca sitekey dari elemen g-recaptcha, atau key render dan action dari skrip halaman v3, lalu pecahkan dengan CapSkip memakai browser.url. Tambahkan kolom g-recaptcha-response dengan new_control, isi sisanya, lalu panggil submit_selected pada browser yang sama, yang ikut membawa cookie, kolom CSRF, dan Referer. Pecahkan tepat sebelum Anda mengirim formulir, jaga agar paling banyak sepuluh login berjalan bersamaan, dan alihkan CapSkip ke mode Server jika skrip berjalan di tempat lain.

MechanicalSoup membuat scraper tetap ringan karena tidak pernah menjalankan browser. Pasangkan dengan pemecah captcha yang berjalan di mesin Windows Anda sendiri, maka pekerjaan beratnya tetap berada di perangkat keras yang Anda kendalikan, tanpa tagihan per pemecahan.