Cara Memecahkan CAPTCHA di Scrapling Dengan page_action

scrapling captcha - How to Solve CAPTCHAs in Scrapling With page_action

Pekerjaan captcha di Scrapling terbagi rapi menjadi dua, dan mengetahui Anda sedang berada di bagian yang mana bisa menghemat waktu satu sore. Cloudflare punya sakelarnya sendiri: berikan solve_cloudflare ke StealthyFetcher dan tantangan Turnstile atau interstitial akan ditangani untuk Anda. Sisanya menjadi urusan Anda. reCAPTCHA v2, reCAPTCHA v3, GeeTest, dan CAPTCHA gambar dipecahkan di luar fetcher lalu disuntikkan ke halaman melalui hook page_action. Artikel ini membahas bagian kedua itu, termasuk satu detail yang diam-diam merusaknya.

Apa yang Anda butuhkan

  • Python 3.10 atau yang lebih baru. Scrapling mensyaratkannya.
  • Scrapling yang terinstal dengan extra fetchers, plus langkah unduhan browser sekali jalan.
  • URL halaman yang dilindungi. Anda tidak perlu sitekey di awal, karena pass pertama yang membacanya.
  • CapSkip berjalan dalam mode Local jika scraper dan pemecah berbagi satu mesin, atau dalam mode Server jika tidak. Keduanya dijelaskan di bagian pengaturan koneksi.
# pip install capskip
pip install capskip
pip install "scrapling[fetchers]"

# Scrapling needs one more step. The bare package is the parser
# engine only, and importing from scrapling.fetchers without this
# raises ModuleNotFoundError.
scrapling install

Apa yang dicakup solve_cloudflare, dan apa yang tidak

Perlu dijelaskan secara tepat, karena namanya terdengar lebih luas daripada fiturnya. Argumen solve_cloudflare milik StealthyFetcher menyelesaikan tantangan Turnstile dan interstitial dari Cloudflare sebelum menyerahkan responsnya kepada Anda. Itu kemudahan yang nyata dan Anda sebaiknya memakainya. Itu juga seluruh daftarnya.

Tantangan di halamanSiapa yang menanganinya
Cloudflare Turnstile, dan interstitial satu halaman penuhScrapling, melalui argumen solve_cloudflare
reCAPTCHA v2, termasuk varian invisible dan enterpriseAnda, melalui hook page_action
reCAPTCHA v3 dengan nama actionAnda, melalui hook page_action
GeeTest v3, slider dengan tiga field dalam jawabannyaAnda, melalui hook page_action
CAPTCHA gambar dengan teks terdistorsiAnda, melalui hook page_action

Jadi bentuk pekerjaannya selalu sama. Ambil parameter tantangan dari halaman, pecahkan di tempat lain, lalu kembalikan jawabannya ke DOM dan biarkan form milik situs itu sendiri yang membawanya. Scrapling memberi Anda tepat satu tempat untuk mengerjakan bagian tengah itu, yaitu page_action.

Langkah 1: baca sitekey tanpa menjalankan browser

Scrapling punya tiga fetcher dan ketiganya tidak bisa saling menggantikan. Fetcher adalah HTTP biasa. DynamicFetcher menjalankan browser Playwright. StealthyFetcher adalah versi yang diperkuat, dan sejak versi 0.3.13 ia berjalan di atas Patchright, bukan build Camoufox yang dipakainya sebelum ini, dan itu perlu diketahui jika Anda mengikuti panduan lama.

Untuk sitekey Anda hampir tidak pernah membutuhkan browser. Nilainya ada di markup sebagai atribut data, jadi fetcher yang murah pun bisa membacanya.

# pip install capskip
from scrapling.fetchers import Fetcher

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

# Plain HTTP. No browser starts, so this costs almost nothing
# and tells you which CAPTCHA you are actually facing.
page = Fetcher.get(PAGE_URL)

sitekey = page.css("[data-sitekey]::attr(data-sitekey)").get()
print(sitekey)

Jika hasilnya kembali kosong, widget ditulis oleh JavaScript dan tidak ada di HTML yang dikirim server. Ambil halaman yang sama sekali saja dengan DynamicFetcher lalu baca dari DOM yang sudah dirender, atau buka di browser dan tulis nilainya secara hardcode. Sitekey bersifat publik dan stabil, jadi menuliskannya secara hardcode bukan jalan pintas yang perlu Anda sesali.

Langkah 2: pecahkan di hardware Anda sendiri

Satu panggilan. Setiap varian reCAPTCHA memakai metode yang sama dengan keyword argument yang berbeda: invisible disetel ke 1, enterprise disetel ke 1, atau version disetel ke v3 dengan nama action. Turnstile dan GeeTest punya metodenya sendiri dengan bentuk yang sama, dan daftar parameter lengkapnya ada di dokumentasi API CapSkip.

# CapSkip listens on your own machine, so this is a loopback call.
from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)
token = solver.recaptcha(sitekey=sitekey, url=PAGE_URL)["code"]

Panggilan itu memblokir selama melakukan polling, dan pollingnya tidak memakai interval datar: SDK mulai dari 250 milidetik lalu melambat menuju pollingInterval, sehingga biasanya kembali lebih cepat daripada loop tulisan tangan ke endpoint mentah. Batas atasnya adalah recaptchaTimeout, yang secara default 300 detik.

Langkah 3: suntikkan token di page_action

page_action menerima sebuah fungsi, mendapat objek page Playwright, dan berjalan setelah navigasi dan setelah penantian network_idle, tetapi sebelum wait_selector. Urutan itulah bagian yang berguna: widget sudah dirender saat fungsi Anda berjalan, dan apa pun yang Anda tunggu setelahnya melihat hasil dari apa yang Anda lakukan.

from playwright.sync_api import Page

def inject_token(page: Page):
    # The response textarea is hidden, so set the value directly
    # rather than trying to type into it.
    page.evaluate(
        "(t) => document.getElementById('g-recaptcha-response').value = t",
        token,
    )
    page.click("button[type=submit]")

Fungsi itu tidak perlu mengembalikan apa pun. Versi Scrapling yang lebih lama meminta objek page dikembalikan sedangkan dokumentasi saat ini tidak, jadi tidak mengembalikan apa pun adalah penulisan yang aman untuk keduanya.

Konteks eksekusi terisolasi, bagian yang paling sering menjebak

Secara default StealthyFetcher mengevaluasi JavaScript Anda di konteks eksekusi terisolasi milik Patchright. DOM dibagikan, jadi membaca dan menulis elemen bekerja persis seperti yang Anda harapkan. Global JavaScript milik halaman itu sendiri tidak dibagikan, jadi apa pun yang ditaruh situs pada window sama sekali tidak ada.

Perbedaan itu menentukan apakah injeksi Anda berhasil. Menyetel nilai textarea respons hanya menyentuh DOM, dan itu bekerja dengan baik di dunia terisolasi. Memanggil callback reCAPTCHA milik situs tidak bekerja, karena callback itu berada pada global yang dibuat oleh skrip halaman itu sendiri. Situs yang memakai callback alih-alih submit form biasa akan terlihat seperti mengabaikan token yang sebenarnya sempurna.

def inject_and_fire_callback(page: Page):
    # isolated_context=False drops into the page's own world,
    # where the site's grecaptcha object actually exists.
    page.evaluate(
        """(t) => {
            document.getElementById('g-recaptcha-response').value = t;
            window.onCaptchaSuccess(t);
        }""",
        token,
        isolated_context=False,
    )

Baca nama callback dari widget sebelum Anda menulis ini. Namanya adalah apa pun yang tertulis pada atribut data-callback di elemen reCAPTCHA, dan berbeda-beda di setiap situs.

Contoh lengkap yang berfungsi

Satu pass HTTP murah untuk membaca sitekey, satu pemecahan, lalu satu fetch stealthy yang menyuntikkan dan mengirimkan. Perhatikan bahwa pemecahan terjadi sebelum fetch, jadi token sudah di tangan saat page_action berjalan.

# pip install capskip
from capskip import CapSkip
from playwright.sync_api import Page
from scrapling.fetchers import Fetcher, StealthyFetcher

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

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

sitekey = Fetcher.get(PAGE_URL).css("[data-sitekey]::attr(data-sitekey)").get()
token = solver.recaptcha(sitekey=sitekey, url=PAGE_URL)["code"]

def inject_token(page: Page):
    page.evaluate(
        "(t) => document.getElementById('g-recaptcha-response').value = t",
        token,
    )
    page.click("button[type=submit]")

page = StealthyFetcher.fetch(
    PAGE_URL,
    headless=True,
    network_idle=True,
    page_action=inject_token,
    wait_selector=".dashboard",
)

print(page.css(".dashboard h1::text").get())

Dua argumen di sana bersifat menentukan. network_idle membuat fetcher menunggu sampai tidak ada koneksi jaringan selama 500 milidetik, yang biasanya cukup lama bagi widget untuk dirender. wait_selector adalah yang membuktikan submit berhasil: arahkan ke sesuatu yang hanya ada di sisi seberang form, dan submit yang gagal menjadi timeout alih-alih keberhasilan semu.

Menangani beberapa halaman dengan session

Setiap fetch di tingkat kelas menjalankan browser lalu membuangnya. Untuk lebih dari beberapa halaman, buka session saja dan pertahankan browser di antara permintaan. Sebagian besar argumen fetch bisa disetel sekali pada session dan ditimpa per permintaan, termasuk page_action, wait_selector, dan solve_cloudflare.

from scrapling.fetchers import StealthySession

# solve_cloudflare here handles the Cloudflare layer for every
# page in the session. Anything else still goes through
# page_action, per request.
with StealthySession(headless=True, solve_cloudflare=True) as session:
    for url in urls:
        page = session.fetch(url, page_action=inject_token)
        print(page.css("title::text").get())

Pecahkan selambat yang Anda bisa. Token reCAPTCHA berlaku sekitar dua menit, jadi memecahkan dua puluh token di awal lalu menyusuri antrean akan membuat sebagian besar token kedaluwarsa. Pecahkan di dalam loop, tepat sebelum fetch yang memakainya. Apa arti masa berlaku itu dalam praktik dibahas di panduan kedaluwarsa token reCAPTCHA.

Menjalankan solver di mesin lain

Scraper berpindah-pindah. VPS, container, atau worker terjadwal bukanlah mesin tempat pemecah berada, dan loopback di sana menunjuk ke worker itu sendiri, tempat tidak ada yang mendengarkan.

CapSkip punya dua mode koneksi persis untuk keperluan ini. Local mengikat ke 127.0.0.1 dan hanya melayani perangkat itu. Server mengikat ke alamat jaringan atau IP publik Anda, sehingga scraper di mana pun bisa menjangkau mesin Windows yang sama lewat API. IP publik statis menjaga alamatnya tetap stabil. Keduanya tetap hardware Anda dan keduanya tetap tanpa batas pemakaian, jadi satu proses yang memecahkan lima puluh ribu tantangan biayanya sama dengan yang memecahkan lima puluh.

# The SDK reads these three itself, so the same script runs
# whether the solver is on this box or on another one:
#   CAPSKIP_HOST=192.0.2.10
#   CAPSKIP_PORT=8080
#   CAPSKIP_API_KEY=your-key

solver = CapSkip()

Aktifkan validasi key begitu pemecah mendengarkan di alamat jaringan, dan beri setiap worker key-nya sendiri agar satu key bisa dicabut tanpa mengganggu yang lain. Kedua mode dibahas langkah demi langkah di panduan penyiapan CapSkip.

Proxy di kedua sisi

Jika situs memeriksa bahwa token datang dari alamat yang mengirimkannya, pemecahan dan fetch harus keluar dari exit yang sama. Scrapling menerima argumen proxy pada fetcher-nya, dan CapSkip menerima satu proxy per task untuk reCAPTCHA, Turnstile, dan GeeTest. CAPTCHA gambar tidak menerima proxy dan tidak membutuhkannya.

PROXY = "http://user:[email protected]:3128"

token = solver.recaptcha(
    sitekey=sitekey,
    url=PAGE_URL,
    proxy={"type": "HTTP", "uri": "user:[email protected]:3128"},
)["code"]

page = StealthyFetcher.fetch(PAGE_URL, proxy=PROXY, page_action=inject_token)

Kedua cara penulisannya memang sengaja dibuat berbeda: pemecah menerima type dan URI sebagai field terpisah, fetcher menerima satu string URL. Salah menulis salah satunya berarti pemecahan keluar dari alamat asli Anda sementara fetch keluar dari proxy, yang terlihat persis seperti token buruk padahal bukan. Cara memilih jenis proxy dibahas di panduan merotasi proxy saat memecahkan CAPTCHA.

Kesalahan umum dan artinya

Apa yang Anda lihatPenyebabPerbaiki
ModuleNotFoundError pada impor fetchersPaket dasarnya hanya berisi mesin parserInstal dengan extra fetchers, lalu jalankan perintah scrapling install
Selektor sitekey tidak mengembalikan apa punWidget disuntikkan oleh JavaScript, jadi tidak ada di HTML yang dikirim serverBaca sekali dengan DynamicFetcher, atau tulis secara hardcode
Token masuk ke field tetapi form tidak pernah terkirimSitus memakai callback, dan global itu tidak ada di dunia terisolasiBerikan isolated_context yang disetel ke False pada panggilan evaluate
Timeout pada wait_selector setelah pemecahan yang bersihSubmit gagal, jadi elemen di sisi seberang tidak pernah munculAmbil screenshot di page_action dan lihat apa yang sebenarnya ditampilkan halaman
Token yang valid ditolakPemecahan dan fetch keluar dari alamat yang berbedaPasang proxy yang sama pada keduanya, dengan cara penulisan masing-masing
NetworkException dari pemecahCapSkip tidak berjalan, atau host dan port-nya salahJalankan aplikasinya, atau arahkan variabel lingkungan host ke alamat server
ValidationException dari pemecahArgumen yang tidak diterima oleh jenis CAPTCHA tersebutPeriksa jenisnya. Action pada v2, atau invisible pada v3, akan memicunya

FAQ

Apakah solve_cloudflare juga menangani reCAPTCHA?

Tidak. Cakupannya adalah widget Turnstile dan halaman tantangan interstitial milik Cloudflare, yang merupakan produk Cloudflare. reCAPTCHA milik Google, GeeTest berdiri sendiri, dan CAPTCHA gambar adalah apa pun yang digambar situs itu. Ketiganya melalui page_action dengan token yang Anda pecahkan sendiri.

Apakah StealthyFetcher masih dibangun di atas Camoufox?

Tidak lagi sejak versi 0.3.13. Sekarang ia berjalan di atas Patchright, yang merupakan stack Chromium dan bukan Firefox. Dokumentasinya masih menjelaskan resep opsional untuk kembali ke Camoufox dengan membuat subclass dari session, tetapi defaultnya sudah berubah, jadi panduan yang menyebut StealthyFetcher sebagai wrapper Camoufox sedang menjelaskan rilis yang lebih lama.

Bisakah saya memecahkan di dalam page_action alih-alih sebelum fetch?

Bisa, dan untuk reCAPTCHA v3 atau GeeTest Anda sering harus begitu, karena parameternya baru ada setelah halaman dijalankan. Ingat saja bahwa browser menganggur selama seluruh proses pemecahan. Jika sitekey ada di HTML yang dikirim server, memecahkan lebih dulu lalu menyuntikkan setelahnya membuat browser terbuka jauh lebih singkat.

Apakah fetcher async mengubah semua ini?

Hanya cara penulisannya. Gunakan metode fetch async, dan jadikan page_action sebagai fungsi async yang menerima page Playwright versi async. Di sisi pemecah gunakan AsyncCapSkip, yang di Python merupakan implementasi async sungguhan dan bukan sekadar alias, sehingga ia berbagi event loop Anda alih-alih memarkir satu thread.

Versi singkatnya

Biarkan Scrapling menangani Cloudflare dengan solve_cloudflare, dan tangani sisanya sendiri di page_action. Baca sitekey dengan fetcher HTTP biasa, pecahkan, lalu setel field respons dari dalam hook. Jika situs memicu callback alih-alih mengirim form, jalankan evaluate dengan konteks terisolasi dimatikan, atau token akan mendarat di dunia yang tidak bisa dilihat halaman. Arahkan wait_selector ke sesuatu yang hanya ada setelah submit berhasil, sehingga kegagalan terlihat jelas.

Cerita Python yang lebih luas ada di halaman pemecah CAPTCHA Python, detail tantangan checkbox di halaman pemecah reCAPTCHA v2, dan sisi Cloudflare di halaman pemecah Cloudflare Turnstile. Tiga panggilan yang sama juga ada di Node.js, PHP, dan C#, yang tercantum di halaman SDK pemecahan CAPTCHA.

Satu hal yang perlu ditimbang sebelum Anda mengarahkan ini ke crawl sungguhan. CapSkip adalah pemecah captcha tanpa batas yang berjalan di hardware yang sudah Anda miliki, jadi pos biaya CAPTCHA pada scraping berskala besar menjadi biaya tetap, bukan biaya per pemecahan.