Cara Memecahkan CAPTCHA di Camoufox Menggunakan Main World Eval

camoufox captcha - How to Solve CAPTCHA in Camoufox Using Main World Eval

Langkah captcha di Camoufox terlihat sama seperti yang lain: baca sitekey, kirim ke solver, tulis token ke halaman. Gerakan ketiga itulah yang membuat Camoufox berbeda. Camoufox menjalankan JavaScript yang Anda serahkan ke evaluate di dalam scope terisolasi yang tidak bisa dilihat halaman, dan scope terisolasi tidak dapat mengubah DOM halaman. Penulisan Anda selesai tanpa error, textarea tetap kosong, dan formulir gagal validasi. Perbaikannya adalah satu opsi peluncuran dan sebuah prefiks dua karakter.

Apa yang Anda butuhkan

  • Python 3.10 atau lebih baru. Camoufox 0.5 sudah mematok versi Playwright sendiri, jadi biarkan pip yang menyelesaikannya.
  • Browser Camoufox yang diunduh sekali dengan perintah fetch. Ini adalah build Firefox, bukan Chromium.
  • URL halaman dari formulir yang dilindungi. Sitekey dibaca saat runtime.
  • CapSkip yang berjalan dalam mode Local ketika skrip dan solver berada di satu mesin, atau dalam mode Server ketika keduanya tidak berada di satu mesin. Keduanya dijelaskan di pengaturan koneksi.
# The geoip extra is optional and worth having if you use proxies.
pip install -U capskip "camoufox[geoip]"

# Downloads the browser itself. Run once per machine.
camoufox fetch

Mengapa penyuntikan token yang biasa tidak berpengaruh di sini

Camoufox adalah Firefox yang sudah ditambal dengan wrapper Python tipis di atas Playwright. Kelas wrapper-nya adalah context manager Playwright, jadi yang Anda dapatkan darinya adalah objek browser Playwright biasa, dan setiap locator, klik, serta navigasi yang sudah Anda kenal bekerja tanpa perubahan. Itu kabar baiknya, dan itulah sebagian besar isi library ini.

Pengecualiannya adalah eksekusi skrip. Camoufox menjalankan semua JavaScript di dalam scope terisolasi yang tidak terlihat oleh halaman, dan itulah seluruh alasan keberadaannya: sebuah situs tidak bisa melihat otomatisasi yang sedang mengutak-atiknya. Pembacaan tidak terpengaruh, karena metode locator milik Playwright berjalan lewat protokol browser, bukan lewat scope tersebut. Penulisan terpengaruh, dan inilah kalimat yang perlu diingat: scope terisolasi tidak dapat mengubah DOM. Penetapan token di sana dibuang diam-diam.

Jawaban Camoufox adalah pintu keluar menuju main world. Berikan main_world_eval saat peluncuran, lalu tambahkan prefiks mw: pada skrip mana pun yang harus menyentuh halaman sungguhan, dan skrip itu akan berjalan di scope milik halaman itu sendiri. Ada dua konsekuensinya. Situs dapat mendeteksi kode yang berjalan di sana, jadi pakailah hanya untuk penyuntikan token dan tidak untuk hal lain. Dan Anda tidak bisa mengembalikan referensi elemen keluar dari main world, hanya nilai yang bertahan sebagai JSON.

Langkah 1: luncurkan dengan main world aktif

Opsi ini nonaktif secara default dan harus disetel pada saat peluncuran. Tidak ada cara untuk menyalakannya hanya untuk satu panggilan di kemudian waktu.

# pip install camoufox[geoip]
from camoufox.sync_api import Camoufox

with Camoufox(main_world_eval=True, headless=True) as browser:
    page = browser.new_page()
    page.goto("https://example.com/page-with-recaptcha")

    # browser is a normal Playwright Browser from here on.
    print(page.title())

Ada dua opsi bertetangga yang perlu Anda ketahui sebelum melangkah lebih jauh. humanize menggerakkan kursor mengikuti jalur yang tampak manusiawi, memakan waktu sampai sekitar 1,5 detik untuk menyeberangi jendela, dan itu penting jika Anda mengklik sendiri widget-nya alih-alih menyuntikkan token. Lalu disable_coop melepas Cross-Origin-Opener-Policy sehingga elemen di dalam iframe lintas origin, termasuk kotak centang Cloudflare Turnstile, baru bisa diklik.

Langkah 2: baca sitekey dari halaman

Sitekey berada di dokumen induk, bukan di dalam iframe widget. Markup milik Google menaruhnya pada sebuah container sebagai atribut data-sitekey, dan locator Playwright biasa sudah bisa membacanya. Tidak diperlukan prefiks main world, karena jalur ini tidak pernah melewati scope terisolasi.

# Locators wait by default, so this doubles as a wait
# condition for a widget that renders late.
holder = page.locator("div.g-recaptcha")
holder.wait_for(state="attached", timeout=15000)

sitekey = holder.get_attribute("data-sitekey")
print(sitekey)   # 6Lxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Sebagian situs tidak pernah menampilkan sitekey di halaman induk dan hanya meneruskannya di URL iframe widget. Dalam kasus itu, bacalah dari query string-nya.

# Fallback: the k= parameter on the anchor iframe.
from urllib.parse import urlparse, parse_qs

src = page.locator("iframe[src*='recaptcha/api2/anchor']").get_attribute("src")
sitekey = parse_qs(urlparse(src).query)["k"][0]

Periksa nilainya sebelum Anda menghabiskan satu pemecahan untuknya. Sitekey kosong akan berjalan sampai ke solver dan kembali sebagai ERROR_GOOGLEKEY, jauh sekali dari locator yang sebenarnya gagal.

Langkah 3: pecahkan di komputer Anda sendiri

SDK Python berbicara dengan CapSkip di port 8080 dan mengembalikan token sebagai string biasa. Satu metode mencakup reCAPTCHA v2, Invisible, Enterprise, dan v3, dengan varian-variannya diberikan sebagai opsi alih-alih sebagai panggilan terpisah.

# pip install capskip
from capskip import CapSkip

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

# Invisible v2 takes invisible=1, Enterprise takes enterprise=1,
# and v3 takes version="v3" with an action.
result = solver.recaptcha(sitekey=sitekey, url=PAGE_URL)

token = result["code"]   # the g-recaptcha-response value

Turnstile dan GeeTest punya metodenya sendiri dan berbentuk sama. Turnstile juga mengembalikan user agent yang dipakai saat pemecahan, dan halaman tantangan akan menolak token kecuali Anda mengirimkan user agent itu kembali bersamanya. Daftar parameter lengkapnya ada di dokumentasi API CapSkip.

Camoufox juga menyertakan kelas async, dan AsyncCapSkip dari SDK Python adalah implementasi asyncio yang sesungguhnya, bukan sekadar alias untuk versi sinkronnya. Pasangkan keduanya ketika Anda mengendalikan beberapa context sekaligus, agar satu pemecahan di satu halaman tidak menghambat halaman lain.

Langkah 4: suntikkan token di main world

Inilah bagian yang khas untuk browser ini. Textarea respons disembunyikan dengan display:none, sehingga tidak ada alat otomatisasi yang bisa mengetik ke dalamnya dan Anda harus menetapkan nilainya lewat JavaScript. Beri skrip itu prefiks mw: supaya berjalan di scope milik halaman itu sendiri, dan bangun string-nya dengan json.dumps alih-alih f-string, karena string literal JSON juga merupakan string literal JavaScript yang valid, lengkap dengan tanda kutip dan escape-nya.

# The mw: prefix is what makes this write land.
import json

page.evaluate(
    "mw:document.getElementById('g-recaptcha-response').value = "
    + json.dumps(token)
)

page.click("button[type=submit]")

Memverifikasi bahwa token sudah masuk punya satu kerumitan. Anda tidak bisa menyerahkan sebuah elemen keluar dari main world, jadi kembalikan sebuah nilai alih-alih node-nya. Sebuah panjang sudah memadai, dan itu memberi tahu persis apa yang ingin Anda ketahui.

# Return a number, never the element itself.
length = page.evaluate(
    "mw:document.getElementById('g-recaptcha-response').value.length"
)
print(length)   # 0 means the injection did not land

Jika situs mendefinisikan callback sukses alih-alih membaca textarea saat formulir dikirim, panggil callback itu setelah Anda menetapkan nilainya. Inilah kasus ketika main world bukan sekadar praktis melainkan wajib, karena fungsi yang didefinisikan halaman sama sekali tidak ada di scope terisolasi. Baca nama fungsinya dari markup halaman itu sendiri alih-alih menebak-nebak. Kedua gaya pengiriman itu pada dasarnya tetap reCAPTCHA v2 biasa, dan keduanya dibahas secara rinci di halaman pemecah reCAPTCHA v2.

Contoh lengkap yang berfungsi

Semua yang di atas dalam satu skrip. Solver dibuat sekali, browser menutup dirinya sendiri ketika bloknya berakhir, dan sitekey diperiksa sebelum satu pemecahan dihabiskan untuknya.

# pip install capskip camoufox[geoip]
import json
from camoufox.sync_api import Camoufox
from capskip import CapSkip

PAGE_URL = "https://example.com/page-with-recaptcha"

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

with Camoufox(main_world_eval=True, headless=True) as browser:
    page = browser.new_page()
    page.goto(PAGE_URL)

    holder = page.locator("div.g-recaptcha")
    holder.wait_for(state="attached", timeout=15000)
    sitekey = holder.get_attribute("data-sitekey")
    if not sitekey:
        raise RuntimeError("Widget found but data-sitekey was empty.")

    result = solver.recaptcha(sitekey=sitekey, url=PAGE_URL)
    page.evaluate(
        "mw:document.getElementById('g-recaptcha-response').value = "
        + json.dumps(result["code"])
    )

    page.click("button[type=submit]")
    page.wait_for_load_state("networkidle")
    print(page.url)   # where you land after submitting

Menjalankan solver di mesin lain

Camoufox cenderung berakhir di mesin yang lebih besar daripada mesin tempat Anda menulis skripnya, dan ada satu detail platform yang harus tepat ketika itu terjadi. Memberikan headless sebagai string virtual akan memulai display Xvfb, yang merupakan fitur khusus Linux: di Windows dan macOS nilai itu justru memunculkan error tidak didukung. headless biasa bekerja di mana saja, jadi gunakan nilai boolean kecuali Anda memang sengaja berada di Linux.

Solver tidak harus ikut berpindah bersama browser. CapSkip punya dua mode koneksi. Local mengikat ke 127.0.0.1 dan hanya melayani perangkat itu, dan itulah pengaturan yang tepat selama Anda menulis skrip di mesin tempat aplikasinya berjalan. Server mengikat ke alamat jaringan atau IP publik Anda, sehingga VM scraping, workstation kedua, atau mesin Linux yang menjalankan Camoufox bisa memanggil mesin Windows yang sama lewat API. IP publik statis menjaga alamat itu tetap stabil. Tidak ada bagian kode yang berubah selain host-nya, dan biayanya pun tidak berubah, karena ini tetap perangkat keras Anda sendiri.

# Same SDK, same call. Only the host moves.
solver = CapSkip(host="10.0.0.12", port=8080, apiKey="YOUR_API_KEY")

Aktifkan validasi key begitu solver mendengarkan di alamat jaringan, dan beri setiap mesin key-nya sendiri agar satu key bisa dicabut tanpa mengusik yang lain. Kedua mode itu dijelaskan langkah demi langkah dalam panduan penyiapan CapSkip.

Kesalahan umum dan artinya

Apa yang Anda lihatPenyebabPerbaiki
Token sudah disetel tetapi formulir tetap gagalPenulisan berjalan di scope terisolasi dan dibuangLuncurkan dengan main_world_eval dan beri skrip prefiks mw:
evaluate memunculkan error tentang nilai kembalian yang tidak didukungSebuah referensi node dikembalikan keluar dari main worldKembalikan sebuah panjang atau string alih-alih elemennya
Fungsi callback bernilai undefinedVariabel global halaman tidak ada di scope terisolasiPanggil dari main world dengan prefiks yang sama
Kotak centang Turnstile tidak bisa diklikKotak itu berada di dalam iframe lintas originLuncurkan dengan disable_coop, atau suntikkan token alih-alih mengklik
ERROR_GOOGLEKEYSitekey kosong sampai ke solverPastikan nilainya benar sebelum memanggil recaptcha
NetworkExceptionCapSkip tidak berjalan, atau host-nya salahJalankan aplikasinya, atau arahkan host ke alamat server
TimeoutExceptionProses pemecahan melampaui recaptchaTimeoutNaikkan di atas nilai default 300 detik
Display virtual tidak didukungheadless disetel ke virtual di luar LinuxGunakan headless=True sebagai gantinya

FAQ

Apakah main world eval membuat saya lebih mudah dideteksi?

Apa pun yang berjalan di main world terlihat oleh halaman, jadi secara prinsip memang ya. Dalam praktiknya, paparan itu hanya berupa satu penetapan nilai yang berlangsung dalam hitungan mikrodetik dan tampak identik dengan yang dilakukan skrip milik widget itu sendiri ketika seorang manusia melewati tantangannya. Simpan semua skrip lain di scope terisolasi, lakukan penyuntikan dalam satu panggilan alih-alih beberapa, dan Anda tidak menyerahkan banyak hal.

Bisakah saya memakai ulang kode Playwright yang sudah ada?

Hampir semuanya. Launcher-nya mengembalikan browser Playwright sungguhan, jadi locator, context, route, dan wait berperilaku seperti biasa. Dua hal yang perlu ditinjau ulang adalah setiap panggilan yang menulis ke DOM lewat evaluate, yang memerlukan prefiks, dan apa pun yang mengasumsikan Chromium, karena yang ini Firefox. Gambaran yang lebih luas untuk engine tersebut ada di halaman pemecah CAPTCHA Playwright.

Sebaiknya saya mengklik widget-nya alih-alih menyuntikkan token?

Hanya untuk Turnstile, dan hanya kadang-kadang. Kotak centang Turnstile dalam mode managed bisa lolos dengan sendirinya jika browser-nya tampak meyakinkan, dan untuk itulah Camoufox ada, sementara mengkliknya menuntut Cross-Origin-Opener-Policy dilepas lebih dulu. Klik pada kotak centang reCAPTCHA justru hanya membuka tantangan gambar, jadi tidak ada yang bisa didapat di sana. Detail di sisi widget ada di halaman pemecah Cloudflare Turnstile.

Crawler saya berjalan di VPS Linux. Di mana CapSkip harus ditempatkan?

Di mesin Windows yang Anda kendalikan, dengan mode Server dinyalakan. VPS lalu memanggilnya lewat API persis seperti layanan internal lainnya, sehingga Camoufox dan solver tidak perlu berbagi sistem operasi atau bahkan segmen jaringan yang sama. Arahkan argumen host ke alamat itu, aktifkan validasi key, dan beri VPS key-nya sendiri.

Versi singkatnya

Luncurkan Camoufox dengan main_world_eval menyala, baca sitekey dengan locator biasa, pecahkan lewat CapSkip di 127.0.0.1:8080, lalu suntikkan token melalui panggilan evaluate berprefiks mw: dan kirimkan formulirnya. Prefiks itulah seluruh triknya, karena tanpanya penulisan Anda mendarat di scope yang tidak pernah dilihat halaman. Untuk sisa lanskap Python, termasuk Selenium dan Playwright, lihat halaman pemecah CAPTCHA Python.

Satu hal lagi yang perlu diketahui sebelum Anda menskalakan sebuah crawl. CapSkip adalah pemecah captcha lokal yang berjalan di perangkat keras yang sudah Anda miliki, sehingga proses yang mengulang seribu halaman berbiaya persis sama dengan proses yang mengulang sepuluh halaman.