DataLens

Menunggu elemen dan konten (auto-waiting)

Pahami kenapa scraper membaca halaman terlalu cepat, bagaimana auto-waiting Playwright memperbaiki sebagian besar masalahnya, dan cara menunggu secara eksplisit, mengatur timeout, serta menangani konten yang tidak pernah datang.

Level: Menengah · 7 menit baca

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.

Idenya

Di halaman dinamis, konten datang setelah halaman "termuat": spinner muncul, JavaScript mengambil data, kartu muncul setengah detik kemudian. Scraper yang membaca terlalu cepat tidak melihat apa-apa. Perbaikan klasiknya time.sleep(5), yang lambat saat halamannya cepat dan rusak saat halamannya lambat. Playwright punya jawaban yang lebih baik: tunggu hal yang kamu butuhkan, dan sebagian besar waktu ia melakukannya untukmu.

Masalahnya

Halaman katalog toko memuat produk-produknya sesaat setelah halamannya sendiri muncul. Bagi manusia ini tidak terlihat: kamu melihat spinner sepersekian detik, lalu produk-produknya. Inilah yang dilihat script kalau langsung menghitung produknya:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://nusantara-gadget.test/catalog")
    print(page.locator(".product-card").count())
    print(page.locator(".loading").inner_text())
    browser.close()
0
Loading products…

goto kembali begitu halaman termuat, tetapi kartu-kartu katalog datang dari request latar belakang yang selesai sesaat kemudian. count() melaporkan apa yang ada saat ini juga: belum ada apa-apa.

Auto-waiting

Aksi yang butuh sebuah elemen (inner_text, get_attribute, click, fill...) menunggunya secara otomatis, sampai 30 detik secara default:

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://nusantara-gadget.test/catalog")
    print(page.locator(".product-card .title").first.inner_text())   # waits for the first card
    print(page.locator(".product-card").count())                     # now they're all there
    browser.close()
Galaxy A55 5G
12

Aturan yang perlu diingat:

Menunggu otomatisLangsung kembali
inner_text(), text_content(), get_attribute(), click(), fill(), screenshot()count(), all(), all_inner_texts(), is_visible()

Jadi sebelum memakai method "langsung kembali" pada konten dinamis, tunggu kontennya lebih dulu.

Halaman flash deals menampilkan spinner selama 2,5 detik sebelum deals-nya muncul. Tiga cara untuk menunggu:

import time
from playwright.sync_api import expect

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://nusantara-gadget.test/deals")
    start = time.time()

    page.locator(".deal").first.wait_for()               # 1. wait for an element to be visible
    print(f"deals appeared after {time.time() - start:.1f}s")

    page.wait_for_selector(".spinner", state="detached")  # 2. wait for something to disappear
    expect(page.locator(".deal")).to_have_count(5)        # 3. wait for an exact condition
    print(page.locator(".deal .deal-name").all_inner_texts())
    browser.close()
deals appeared after 2.5s
['Redmi Note 13 Pro', 'Galaxy Buds FE', 'Soundcore Life Q30', '65W GaN Charger', 'Redmi Watch 4']
  1. locator.wait_for() menunggu sampai elemennya terlihat (state="attached", "hidden" atau "detached" untuk kondisi lain).
  2. page.wait_for_selector(selector, state=...) melakukan hal yang sama dengan string selector.
  3. expect(locator).to_have_count(n) (dan to_have_text, to_be_visible...) terus mencoba ulang sampai kondisinya terpenuhi atau 5 detik berlalu. Berguna saat kamu tahu persis seperti apa kondisi "selesai".

Halamannya butuh 2,5 detik, dan script-nya menunggu 2,5 detik: tidak lebih, tidak kurang. sleep(5) akan membuang separuh waktunya; sleep(2) akan membaca halaman kosong.

Saat konten tidak pernah datang

Waiting punya timeout. Kalau terlewati, Playwright memunculkan TimeoutError, yang bisa kamu tangkap:

from playwright.sync_api import TimeoutError as PlaywrightTimeout

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://nusantara-gadget.test/deals")
    try:
        page.locator(".deal").first.wait_for(timeout=1000)   # milliseconds
        print("deals found")
    except PlaywrightTimeout:
        print("no deals within 1 second")
    browser.close()
no deals within 1 second

Timeout dalam satuan milidetik. Atur per panggilan (timeout=...), atau untuk seluruh halaman dengan page.set_default_timeout(10_000). Timeout yang lebih pendek membuat halaman yang rusak gagal dengan cepat alih-alih menggantung selama 30 detik; pilih nilai yang cukup jauh di atas waktu muat normal halamannya.

(Mengimpornya sebagai PlaywrightTimeout mencegah kebingungan dengan TimeoutError bawaan Python.)

Ide utama

Tunggu elemen atau kondisi spesifik yang kamu butuhkan, jangan pernah menunggu dalam waktu yang tetap. Aksi menunggu otomatis; count() dan all() tidak, jadi tunggu dulu. Tangkap TimeoutError untuk konten yang memang mungkin tidak ada.

Bagaimana dengan wait_for_timeout dan networkidle?

Mengapa ini penting untuk kode yang bersih

Sleep dengan waktu tetap adalah sumber nomor satu scraper yang flaky: lulus di laptopmu yang cepat dan gagal di server yang lambat, atau sebaliknya. Menunggu kondisi yang eksplisit membuat kodenya menyatakan maksudnya ("tunggu sampai deals-nya tampil") dan membuatnya secepat yang diizinkan situsnya. Itu lebih mudah dibaca sekaligus lebih andal.

Rangkuman

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.

Berikutnya: Mengklik, mengetik, dan mengisi formulir →