Web Scraping Lanjutan dengan Selenium: Menangani JavaScript Heavy Pages dan Infinite Scroll
Mengapa Selenium Diperlukan untuk JavaScript-Heavy Pages
Website modern saat ini banyak yang dibangun dengan framework JavaScript seperti React, Vue, atau Angular. Framework ini merender konten secara dinamis di sisi klien -- artinya HTML yang diterima browser saat pertama kali loading hanyalah kerangka kosong, sementara konten sesungguhnya diisi oleh JavaScript setelah halaman dimuat. Pendekatan ini membuat metode scraping tradisional menggunakan requests dan BeautifulSoup tidak lagi memadai.
Ketika kita menggunakan requests.get() pada halaman JavaScript-heavy, yang kita dapatkan hanyalah HTML statis tanpa konten dinamis. Elemen-elemen yang seharusnya muncul setelah JavaScript dieksekusi tidak akan ada dalam response. Di sinilah Selenium berperan sebagai browser automation tool yang dapat menjalankan JavaScript secara penuh, memuat halaman sebagaimana browser sungguhan, dan memungkinkan kita mengekstrak konten setelah rendering selesai.
Perbedaan mendasar antara kedua pendekatan ini terlihat jelas saat kita mencoba mengambil data dari halaman yang memuat konten melalui API JavaScript. Requests hanya mengembalikan struktur halaman kosong, sementara Selenium mengembalikan DOM lengkap setelah JavaScript selesai diproses.
# Perbandingan: requests vs Selenium
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# Pendekatan requests -- gagal mendapat konten dinamis
url = "https://example-spa-site.com/products"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
print("Requests title:", soup.title.text if soup.title else "No title")
print("Requests product count:", len(soup.select(".product-item")))
# Output: Requests product count: 0
# Pendekatan Selenium -- berhasil merender konten
options = Options()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
driver.get(url)
products = driver.find_elements("css selector", ".product-item")
print("Selenium product count:", len(products))
# Output: Selenium product count: 24
driver.quit()Dari kode di atas, kita bisa melihat bahwa Selenium mampu menangani situasi di mana requests biasa tidak bisa menjangkau konten dinamis. Ini adalah alasan utama mengapa Selenium menjadi tools andalan untuk web scraping tingkat lanjut.
Setup Driver dan Strategi Waiting yang Tepat

Gambar: Perlengkapan kerja yang dibutuhkan untuk menjalankan Selenium WebDriver — Sumber: [Unsplash](https://unsplash.com/photos/laptop-computer-beside-monitor-with-keyboard-and-mouse-EJMTKCZ00I0)
Langkah pertama sebelum memulai scraping dengan Selenium adalah melakukan setup driver dengan benar. Kita bisa menggunakan webdriver-manager untuk mengelola WebDriver secara otomatis tanpa perlu mengunduh binary secara manual.
Salah satu aspek paling penting dalam menggunakan Selenium adalah strategi waiting. Halaman JavaScript-heavy membutuhkan waktu untuk memuat elemen-elemennya, dan kita perlu menunggu dengan cerdas -- bukan dengan time.sleep() yang boros waktu, melainkan dengan WebDriverWait yang menunggu hingga kondisi tertentu terpenuhi.
!pip install selenium webdriver-manager
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
# Konfigurasi headless Chrome
options = Options()
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
# Inisialisasi driver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=options)
# Explicit wait -- menunggu elemen muncul
wait = WebDriverWait(driver, 10)
driver.get("https://example.com")
# Tunggu hingga elemen dengan ID tertentu muncul
element = wait.until(
EC.presence_of_element_located((By.ID, "dynamic-content"))
)
print("Element found:", element.text)
API Development with Golang
A hands-on, project-based course designed to teach beginners how to build robust...
Terdapat tiga jenis wait di Selenium. Implicit wait memberitahu driver untuk menunggu dalam durasi tetap sebelum melempar exception. Explicit wait memungkinkan kita menunggu hingga kondisi spesifik terpenuhi -- ini yang paling sering digunakan. Fluent wait adalah variasi explicit wait dengan polling interval dan ignore exception yang bisa dikustomisasi. Untuk scraping JavaScript-heavy, explicit wait adalah pilihan terbaik karena efisien dan presisi.
Menangani Infinite Scroll dan Dynamic Content Loading
Infinite scroll adalah pola umum di website modern seperti media sosial, e-commerce, dan portal berita. Konten dimuat secara bertahap saat pengguna scroll ke bawah halaman, memicu AJAX request yang mengambil data tambahan dari server.
Untuk menangani infinite scroll dengan Selenium, kita perlu melakukan scroll ke bawah secara bertahap, menunggu konten baru muncul, lalu mengulanginya hingga semua konten habis. Deteksi bahwa semua konten telah dimuat bisa dilakukan dengan membandingkan jumlah elemen sebelum dan sesudah scroll -- jika tidak bertambah, berarti kita sudah mencapai batas.
import time
def scrape_infinite_scroll(driver, scroll_pause=2, max_scrolls=20):
"""Melakukan infinite scroll dan mengumpulkan semua item."""
items = set()
scroll_count = 0
while scroll_count < max_scrolls:
# Ambil item yang sudah ada
current_items = driver.find_elements(By.CSS_SELECTOR, ".content-item")
new_items = [item.text for item in current_items]
old_count = len(items)
items.update(new_items)
# Scroll ke bottom
driver.execute_script(
"window.scrollTo(0, document.body.scrollHeight);"
)
time.sleep(scroll_pause)
# Cek apakah ada item baru
new_count = len(items)
if new_count == old_count and scroll_count > 2:
print(f"Tidak ada konten baru setelah scroll ke-{scroll_count}")
break
scroll_count += 1
print(f"Scroll {scroll_count}: {new_count} item ditemukan")
return list(items)
# Penggunaan
driver.get("https://example.com/infinite-scroll-page")
all_items = scrape_infinite_scroll(driver)
print(f"Total item terkumpul: {len(all_items)}")Logika di atas melakukan scroll bertahap dengan jeda 2 detik untuk memberi waktu konten baru termuat. Setiap iterasi membandingkan jumlah item yang terkumpul untuk menentukan kapan harus berhenti. Pendekatan ini lebih andal daripada menggunakan jumlah scroll tetap karena bisa beradaptasi dengan kecepatan loading halaman.
Interaksi dengan Elemen JavaScript Dinamis

Gambar: Browser automation memungkinkan kita berinteraksi dengan elemen dinamis di halaman web — Sumber: [Unsplash](https://unsplash.com/photos/a-computer-with-a-keyboard-and-mouse-yGQmjh2uOTg)
Tidak semua konten dinamis bisa diakses hanya dengan scroll. Banyak website menggunakan tombol "Load More", dropdown interaktif, atau modal popup yang perlu diklik sebelum data tambahan muncul. Selenium menyediakan kemampuan untuk berinteraksi dengan elemen-elemen ini secara terprogram.
Tantangan utama saat berinteraksi dengan elemen dinamis adalah StaleElementReferenceException -- exception yang terjadi ketika elemen yang sebelumnya ditemukan sudah tidak lagi terhubung ke DOM. Ini sering terjadi ketika halaman di-render ulang setelah suatu aksi. Solusinya adalah dengan menggunakan mekanisme retry atau mencari ulang elemen setiap kali akan berinteraksi.
from selenium.common.exceptions import (
StaleElementReferenceException,
TimeoutException
)
def click_load_more(driver, max_clicks=10):
"""Klik tombol Load More secara berulang dengan retry mechanism."""
clicks = 0
while clicks < max_clicks:
try:
# Cari tombol setiap iterasi untuk hindari stale element
load_more = WebDriverWait(driver, 5).until(
EC.element_to_be_clickable((By.ID, "load-more-btn"))
)
load_more.click()
clicks += 1
time.sleep(2) # Tunggu konten baru muncul
# Tutup modal popup jika muncul
try:
modal_close = driver.find_element(By.CSS_SELECTOR, ".modal-close")
modal_close.click()
time.sleep(1)
except:
pass # Tidak ada modal -- lanjutkan
except TimeoutException:
print(f"Tombol Load More tidak ditemukan setelah {clicks} klik")
break
except StaleElementReferenceException:
print("Stale element -- mencari ulang elemen...")
time.sleep(1)
continue
return clicks
# Penggunaan
total_clicks = click_load_more(driver)
print(f"Load More diklik sebanyak {total_clicks} kali")Kode ini menangani dua skenario umum: tombol yang perlu diklik berulang kali dan modal popup yang kadang menghalangi interaksi. Dengan WebDriverWait dan element_to_be_clickable, kita memastikan tombol benar-benar siap diklik sebelum mengeksekusi aksi.
Best Practice -- Headless Mode, Resource Blocking, dan Error Handling
Setelah berhasil mengimplementasikan scraping dengan Selenium, ada beberapa praktik terbaik yang perlu diterapkan untuk memastikan script berjalan efisien dan stabil di production.
Headless mode memungkinkan browser berjalan tanpa antarmuka grafis, menghemat resource dan mempercepat eksekusi secara signifikan. Resource blocking -- memblokir CSS, font, dan gambar -- bisa mengurangi bandwidth dan mempercepat loading halaman karena kita hanya butuh konten teks. Error handling yang baik memastikan script tidak berhenti total saat satu elemen tidak ditemukan.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import (
TimeoutException,
NoSuchElementException,
WebDriverException
)
def create_optimized_driver():
"""Membuat Chrome driver dengan optimasi untuk web scraping."""
options = Options()
# Headless mode
options.add_argument("--headless=new")
# Block resource yang tidak perlu
prefs = {
"profile.managed_default_content_settings.images": 2,
"profile.managed_default_content_settings.stylesheets": 2,
}
options.add_experimental_option("prefs", prefs)
# Optimasi performa
options.add_argument("--disable-gpu")
options.add_argument("--disable-extensions")
options.add_argument("--disable-notifications")
driver = webdriver.Chrome(options=options)
driver.set_page_load_timeout(30)
return driver
def safe_scrape(driver, url):
"""Wrapper dengan error handling untuk scraping."""
try:
driver.get(url)
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.TAG_NAME, "body"))
)
return driver.page_source
except TimeoutException:
print(f"Timeout saat memuat {url}")
return None
except WebDriverException as e:
print(f"WebDriver error: {e}")
return None
finally:
# Pastikan driver selalu ditutup
driver.quit()
# Penggunaan
driver = create_optimized_driver()
html = safe_scrape(driver, "https://example.com")
if html:
print(f"Berhasil mendapatkan halaman ({len(html)} chars)")Dengan konfigurasi di atas, browser berjalan tanpa GUI, tidak memuat gambar dan stylesheet yang tidak diperlukan, memiliki page load timeout 30 detik, dan dilengkapi error handling untuk menangani kegagalan koneksi atau elemen yang tidak ditemukan. Pastikan selalu memanggil driver.quit() untuk mencegah memory leak, terutama jika script dijalankan dalam loop atau batch processing.
Menguasai Selenium untuk JavaScript-heavy pages adalah kemampuan yang sangat berharga di era web modern. Dari pemahaman dasar tentang mengapa Selenium diperlukan, setup driver yang tepat, strategi waiting, penanganan infinite scroll, interaksi elemen dinamis, hingga optimasi performa -- setiap aspek saling terkait untuk membangun pipeline scraping yang andal.
Tertarik memperdalam Python untuk data dan web scraping? Kunjungi program bootcamp Python di Rumah Coding untuk pembelajaran terstruktur dari praktisi industri.
Kursus Terkait
API Development with Golang
A hands-on, project-based course designed to teach beginners how to build robust, high-performance RESTful APIs using Golang. Starting from core Go syntax and concurrency, students will progress through routing, database integration, security, and finally containerize their application for production deployment.
EduTrack: Learning Management & Assessment API
- Role-Based Access Control (RBAC)
- Course & Module Management
- Enrollment & Progress Tracking
Advanced Architecture with Laravel Containers & Queues
A problem-driven, project-based course designed to elevate intermediate developers to a senior architectural mindset. Instead of just memorizing documentation, students will tackle real-world bottleneck issues by mastering the Inversion of Control (IoC) principle, Dependency Injection, and asynchronous background processing. Learn to decouple services and orchestrate robust queues to build scalable, high-performance applications that never freeze under heavy loads.
Asynchronous Bulk E-Certificate & Notification Engine
- Decoupled Service Architecture: PDF generation and Email delivery are built as independent, interface-driven services injected via the Service Container.
- Job Chaining: Ensuring processes run in strict asynchronous order (Generate PDF ➔ Upload to Storage ➔ Send Email).
- Job Batching & Real-Time Tracking: Grouping hundreds of jobs together and displaying a live progress bar (e.g., "65% Completed") on the admin dashboard.
Building Modern Apps with Filament
Learn how to build modern, full-stack web applications rapidly using FilamentPHP and the TALL stack. This project-based course is designed for beginners, guiding you step-by-step to create a fully functional backend administration panel. By the end of the course, you will have mastered Filament's powerful Panel Builder, robust CRUD operations, complex database relationships, and interactive dashboards.
Mini Academic Portal (Learning Management System)
- Master Data Management: Comprehensive CRUD functionality to manage Instructors and Course catalogs.
- Student Registry: A dedicated module to manage student profiles and personal information.
- Dynamic Enrollment System: Handling Many-to-Many relationships to enroll students into specific courses using Filament's pivot features.