Belajar Scrapling - Dynamic Content & SPA Handling
Episode 16 of 23

Belajar Scrapling - Dynamic Content & SPA Handling

Menghadapi website yang bergantung pada JavaScript: SPA, lazy loading, dan infinite scroll. Mempelajari waiting strategy dengan network_idle dan wait_selector, memanipulasi halaman lewat page_action, menangkap data XHR dari SPA, serta pola hybrid fetching untuk memilih fetcher sesuai kebutuhan tiap halaman.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Pendahuluan

Di episode 15 kalian menaklukkan anti-bot dengan StealthyFetcher dan solve_cloudflare. Sekarang kita menghadapi musuh yang lebih halus: website yang tidak menampilkan data tanpa JavaScript. Single Page Application (SPA), lazy loading, dan infinite scroll adalah standar web modern — dan mimpi buruk scraper yang masih berpikir "fetch HTML lalu parse".

Episode ini membahas cara menangani konten dinamis: kapan kalian benar-benar butuh browser, waiting strategy agar elemen siap sebelum diekstrak, teknik untuk infinite scroll dan lazy loading, menangkap data XHR yang sering kali lebih bersih dari DOM, dan pola hybrid fetching untuk memilih fetcher paling efisien per halaman.

Kapan Halaman Butuh Browser

Bukan semua website membutuhkan browser. Halaman statis yang datanya sudah ada di HTML mentah hanya buang waktu dan sumber daya jika dirender dengan browser. Tanda-tanda halaman butuh JavaScript:

  • Data hanya muncul setelah request API tambahan (fetch atau XHR).
  • Elemen dimuat saat di-scroll (lazy loading) — gambar, daftar produk, komentar.
  • Aplikasi bertipe SPA — seluruh konten digambar oleh JavaScript, HTML mentahnya nyaris kosong.
  • Data berada di blob JSON seperti __NEXT_DATA__ yang baru terisi setelah render.

Uji cepatnya: Fetcher.get lalu cek apakah selector target ada. Kalau tidak ada padahal browser manusia melihat data, berarti kalian butuh fetcher berbasis browser.

Waiting Strategy: Jangan Menebak Waktu

Kesalahan paling umum pada halaman dinamis adalah menebak-nebak jeda: "tunggu 5 detik pasti keluar". Menebak itu rapuh — halaman cepat membuat kalian menunggu sia-sia, halaman lambat membuat kalian mengekstrak data kosong. DynamicFetcher memberi waiting strategy eksplisit:

Pythonwait-strategy.py
from scrapling.fetchers import DynamicFetcher
 
page = DynamicFetcher.fetch(
    "https://example.com/spa",
    headless=True,
    network_idle=True,
    wait_selector=".product-list",
    wait_selector_state="visible",
)
produk = page.css(".product h2::text").getall()
  • network_idle=True menunggu jaringan tenang — tidak ada request baru yang tertunda. Ideal untuk SPA yang memuat data via API.
  • wait_selector menunggu elemen dengan selector tertentu benar-benar muncul; wait_selector_state menentukan kondisi yang diinginkan — attached, visible, atau hidden.
  • load_dom (default aktif) memastikan semua file JavaScript selesai dimuat sebelum wait_selector dicek.

Urutannya: halaman dimuat, network_idle dipenuhi (jika diaktifkan), lalu wait_selector dipenuhi (jika diaktifkan) — baru respons dikembalikan. Tidak ada angka ajaib, hanya kondisi yang jelas.

Info

DynamicFetcher dan StealthyFetcher memakai timeout dalam milidetik (default 30000). Fetcher HTTP memakai detik. Jangan campur satuan saat menyesuaikan timeout.

Infinite Scroll dan Lazy Loading dengan page_action

page_action adalah fungsi yang menerima objek Page Playwright dan dijalankan tepat sebelum wait_selector dicek. Di sinilah kalian bisa men-scroll, mengeklik tombol "Load More", atau menunggu elemen tertentu — apa pun yang dibutuhkan halaman:

Pythoninfinite-scroll.py
from playwright.sync_api import Page
 
from scrapling.fetchers import DynamicFetcher
 
def muat_semua(page: Page):
    for _ in range(5):
        page.mouse.wheel(0, 3000)
        page.wait_for_timeout(1500)
 
page = DynamicFetcher.fetch(
    "https://example.com/daftar-tak-habis",
    page_action=muat_semua,
)
item = page.css(".item::text").getall()

Pola yang sama berlaku untuk tombol "Load More": klik tombol, tunggu kontainer baru muncul, ulangi. Untuk versi async, tulis page_action sebagai fungsi async dan pakai DynamicFetcher.async_fetch.

Jika halaman tidak perlu semua interaksi browser, matikan sumber daya yang tidak penting dengan disable_resources=True — memblokir gambar, font, dan media bisa mempercepat fetch sekitar 25 persen. Hemat waktu dan kuota proxy kalian.

Menangkap Data XHR dari SPA

SPA sering memuat data dari API internal (XHR/fetch). Alih-alih menunggu DOM dirender, tangkap langsung respons API-nya — biasanya JSON, yang jauh lebih bersih dan lengkap daripada hasil render:

Pythoncapture-xhr.py
from scrapling.fetchers import DynamicSession
 
with DynamicSession(
    headless=True,
    capture_xhr=r"https://api\.example\.com/.*",
) as session:
    page = session.fetch("https://example.com/spa")
    for req in page.captured_xhr:
        print(req.url, req.response.status)

Dengan capture_xhr, Scrapling mengumpulkan request XHR yang cocok dengan pola URL selama halaman dimuat. Data dari API ini sering kali lebih terstruktur daripada hasil parse DOM — dan lebih tahan terhadap perubahan layout, karena API jarang berubah sesering tampilan.

Perlu diingat: jangan ekstrak data dari API internal situs yang melarangnya atau untuk area yang dikunci. Mendapatkan JSON "lebih mudah" tidak berarti lebih sah — etika dari episode 14 tetap berlaku.

Hybrid Fetching: Fetcher Tepat untuk Tiap Halaman

Aturan efisiensi: fetch cepat kalau bisa, browser hanya kalau harus. Fetcher HTTP puluhan kali lebih cepat dan hemat sumber daya daripada browser. Strategi terbaik adalah memetakan kebutuhan tiap jenis halaman, lalu merutekan ke fetcher yang sesuai.

Di dalam spider, ini semudah mendefinisikan dua session dan memilih sid per request:

Pythonhybrid-spider.py
from scrapling.fetchers import AsyncDynamicSession, FetcherSession
from scrapling.spiders import Request, Spider, Response
 
class HybridSpider(Spider):
    name = "hybrid"
    start_urls = ["https://example.com/"]
 
    def configure_sessions(self, manager):
        manager.add("fast", FetcherSession(impersonate="chrome"))
        manager.add("browser", AsyncDynamicSession(headless=True), lazy=True)
 
    async def parse(self, response: Response):
        for link in response.css("a.statistik::attr(href)").getall():
            yield Request(link, sid="fast", callback=self.parse_item)
 
        for link in response.css("a.spa::attr(href)").getall():
            yield Request(link, sid="browser", callback=self.parse_item)

Halaman statistik yang ringan lewat FetcherSession; halaman SPA yang berat lewat AsyncDynamicSession yang baru menyala saat pertama dipakai (lazy=True). Server merasa kalian pelanggan biasa, sumber daya kalian tetap hemat, dan crawl selesai lebih cepat.

Penutup

Kalian sekarang bisa menangani web modern tanpa pusing: tahu kapan sebuah halaman benar-benar butuh browser, memakai network_idle dan wait_selector alih-alih menebak waktu, men-scroll dan mengklik lewat page_action, menangkap data XHR yang lebih bersih dari DOM, dan memilih fetcher yang tepat per halaman dengan hybrid fetching. Ini adalah keseimbangan antara kelengkapan data dan efisiensi.

Inti yang harus dibawa pulang:

  • Uji dulu dengan Fetcher — hanya pindah ke browser jika selector target benar-benar kosong di HTML mentah.
  • Gunakan network_idle dan wait_selector dengan state eksplisit, bukan jeda tebakan.
  • page_action memberi kekuatan Playwright penuh untuk scroll dan klik pada halaman tak berujung.
  • capture_xhr mengambil JSON dari API internal SPA — lebih stabil daripada hasil render DOM.
  • Hybrid fetching: Fetcher untuk yang bisa, browser untuk yang harus, dirutekan lewat sid di spider.

Di episode 17 semua keterampilan kalian disatukan dalam skala besar: spiders lanjutan. Kalian akan belajar checkpoint dan pause/resume agar crawl panjang tidak sia-sia saat terputus, streaming hasil dengan real-time stats, dan strategi scaling ke ribuan halaman dengan concurrency yang terkendali.

Belajar Scrapling - Dynamic Content & SPA Handling | Belajar Scrapling