Pernah nggak sih kamu butuh data dari internet, tapi nggak ada API-nya? Atau datanya ada, tapi formatnya berantakan dan harus copy-paste ratusan halaman secara manual? Kalau pernah, selamat — kamu baru saja menemukan alasan kenapa web scraping itu game changer banget.

Saya pribadi pertama kali kenal web scraping sekitar tahun 2019. Waktu itu saya butuh data harga produk dari beberapa marketplace buat riset sederhana. Bayangin, ada ribuan produk yang harus saya catat. Manual? Bisa gila. Akhirnya saya coba Python, dan sejak saat itu saya nggak pernah balik lagi ke cara manual.

Artikel ini saya tulis berdasarkan pengalaman bertahun-tahun — termasuk jatuh bangun kena ban, bikin scraper yang crash di tengah jalan, sampai akhirnya menemukan workflow yang solid. Di panduan web scraping dengan Python ini, kamu akan belajar dari nol sampai bisa bikin scraper yang andal di tahun 2026.

Nggak perlu jago Python banget kok. Asal kamu kenal dasar-dasarnya — variabel, loop, function — kamu sudah bisa mulai. Oke, langsung gas aja ya.


Apa Itu Web Scraping dan Kenapa Kamu Perlu Tahu?

Secara sederhana, web scraping adalah teknik mengambil data dari halaman web secara otomatis. Alih-alih kamu buka browser, scroll, copy-paste satu per satu, scraper melakukannya untukmu — dalam hitungan detik.

Bayangin kamu lagi riset harga laptop di 5 e-commerce. Setiap situs punya ratusan produk. Kalau manual, bisa seharian. Dengan scraper? Mungkin 10 menit, tergantung seberapa banyak data dan seberapa cepat servernya.

Use Case Nyata di 2026

Sekarang di tahun 2026, web scraping makin relevan. Beberapa use case yang paling sering saya temui:

  • Riset pasar: Ambil data harga, review, rating dari marketplace atau situs kompetitor.
  • Machine learning: Kumpulkan dataset dari berbagai sumber untuk training model AI.
  • Monitoring harga: Pantau fluktuasi harga tiket pesawat, hotel, atau properti secara real-time.
  • Aggregasi konten: Kumpulkan berita, artikel, atau lowongan kerja dari berbagai portal.
  • SEO analysis: Scrape data SERP untuk analisis keyword dan posisi ranking.

Yang perlu kamu catat: web scraping itu legal, tapi ada batasannya. Selama kamu menghormati robots.txt, nggak mengganggu server, dan nggak mengambil data pribadi yang dilindungi, kamu aman-aman saja. Jangan lupa juga baca Terms of Service situs yang mau kamu scrape. Etika tetap nomor satu, bro.


Persiapan: Tools dan Library yang Kamu Butuhkan

Sebelum mulai nulis kode, mari siapkan “dapur” dulu. Berikut ini setup yang saya rekomendasikan di tahun 2026.

Python Environment

Pastikan kamu sudah install Python 3.10 ke atas. Saya sarankan bikin virtual environment biar rapi:

python -m venv scraping-env
source scraping-env/bin/activate  # Linux/Mac
scraping-env\Scripts\activate     # Windows

Library Utama

Ada beberapa library yang wajib kamu kenal:

LibraryFungsi
requestsMengirim HTTP request ke server
BeautifulSoup4Parsing HTML dan mengekstrak data
lxmlParser HTML/XML yang cepat
ScrapyFramework scraping tingkat lanjut
SeleniumAutomasi browser untuk situs dinamis
PlaywrightAlternatif modern untuk Selenium
pandasMengolah dan menyimpan data

Install semuanya sekaligus:

pip install requests beautifulsoup4 lxml scrapy selenium playwright pandas

Kalau mau pakai Playwright, jangan lupa install juga browser-nya:

playwright install

Saya di sini akan pakai requests + BeautifulSoup sebagai starting point karena paling ramah pemula. Nanti kita juga akan bahas Scrapy dan Selenium/Playwright untuk kasus yang lebih kompleks.


Web Scraping Pertamamu: BeautifulSoup + Requests

Oke, ini bagian paling seru — langsung praktik. Kita akan scrape data sederhana dari sebuah halaman web.

Langkah 1: Kirim HTTP Request

Pertama-tama, kita perlu “minta” halaman web dari server:

import requests

url = "https://quotes.toscrape.com"
response = requests.get(url)

print(response.status_code)  # 200 artinya sukses
print(response.text[:500])   # Lihat 500 karakter pertama HTML-nya

Situs quotes.toscrape.com sengaja dibuat untuk latihan scraping, jadi kamu nggak perlu khawatir soal legalitas. Cocok banget buat belajar.

Langkah 2: Parsing HTML

Sekarang kita ambil HTML-nya dan ubah jadi objek yang bisa kita “telusuri”:

from bs4 import BeautifulSoup

soup = BeautifulSoup(response.text, "lxml")

# Lihat judul halaman
print(soup.title.text)
# Output: Quotes to Scrape

Langkah 3: Ekstrak Data

Ini bagian intinya. Kita akan ambil semua quotes beserta penulisnya:

quotes = soup.find_all("div", class_="quote")

for quote in quotes:
    text = quote.find("span", class_="text").get_text()
    author = quote.find("small", class_="author").get_text()
    tags = [tag.get_text() for tag in quote.find_all("a", class_="tag")]
    
    print(f"Quote: {text}")
    print(f"Author: {author}")
    print(f"Tags: {', '.join(tags)}")
    print("-" * 50)

Output-nya kurang lebih seperti ini:

QATQATuuauuaotgotgthsthseo:eo::r:r:c:a“h“bTAaIJihlnt.lebgKieei.twr,siotRerdooslEeuw,dierlnpicas-cnhstthgoehoiwioicenucegeshhsat,vse,Hactrrhreiyan,tkeitdnhgai,ttwsiohsrolwadwphraotcewsestorfuloyurarteh.i.n.k"ing..."

Langkah 4: Simpan ke CSV

Data tanpa penyimpanan yang proper itu percuma. Simpan ke CSV pakai pandas:

import pandas as pd

data = []
for quote in quotes:
    text = quote.find("span", class_="text").get_text()
    author = quote.find("small", class_="author").get_text()
    tags = [tag.get_text() for tag in quote.find_all("a", class_="tag")]
    data.append({"quote": text, "author": author, "tags": ", ".join(tags)})

df = pd.DataFrame(data)
df.to_csv("quotes.csv", index=False, encoding="utf-8")
print(f"Berhasil menyimpan {len(data)} quotes!")

Mudah kan? Nah, tapi itu baru permukaan. Situs di dunia nyata jauh lebih rumit. Makanya kita perlu naik level.


Scraping Situs dengan Pagination dan Data Dinamis

Di dunia nyata, data jarang muat di satu halaman. Biasanya ada pagination — “Halaman 1, 2, 3 … Next”. Kita harus bisa handle itu.

Menangani Pagination

import requests
from bs4 import BeautifulSoup
import pandas as pd
import time

base_url = "https://quotes.toscrape.com/page/{}/"
all_quotes = []

for page in range(1, 11):  # Scrape halaman 1-10
    url = base_url.format(page)
    response = requests.get(url)
    soup = BeautifulSoup(response.text, "lxml")
    
    quotes = soup.find_all("div", class_="quote")
    
    if not quotes:
        print(f"Halaman {page} kosong, berhenti.")
        break
    
    for quote in quotes:
        text = quote.find("span", class_="text").get_text()
        author = quote.find("small", class_="author").get_text()
        all_quotes.append({"quote": text, "author": author})
    
    print(f"Halaman {page} selesai: {len(quotes)} quotes")
    time.sleep(1)  # Jangan spam server!

df = pd.DataFrame(all_quotes)
df.to_csv("all_quotes.csv", index=False)
print(f"Total: {len(all_quotes)} quotes berhasil di-scrape!")

Perhatikan time.sleep(1) di situ. Ini penting banget. Kamu nggak mau server mengira kamu DDoS. Kasih jeda 1-3 detik antar request. Ini soal etika dan keamanan — kalau kamu di-ban IP-nya, scraper-mu mati.

Menangani Situs Dinamis (JavaScript-rendered)

Nah ini yang bikin banyak orang frustasi. Banyak situs modern di 2026 yang datanya di-load pakai JavaScript. Kalau kamu pakai requests biasa, yang kamu dapat cuma HTML kosong — datanya belum di-render.

Solusinya: Selenium atau Playwright.

Contoh pakai Playwright (saya lebih rekomendasikan ini di 2026 karena lebih modern dan cepat):

from playwright.sync_api import sync_playwright
import pandas as pd

data = []

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example-dynamic-site.com/products")
    
    # Tunggu konten muncul
    page.wait_for_selector(".product-card")
    
    products = page.query_selector_all(".product-card")
    
    for product in products:
        name = product.query_selector(".product-name").inner_text()
        price = product.query_selector(".product-price").inner_text()
        data.append({"name": name, "price": price})
    
    browser.close()

df = pd.DataFrame(data)
df.to_csv("products.csv", index=False)
print(f"Berhasil scrape {len(data)} produk!")

Playwright itu lebih stabil dibanding Selenium karena arsitekturnya berbeda. Dia juga support async, yang artinya kamu bisa scrape beberapa halaman sekaligus secara paralel. Di 2026, Playwright sudah jadi standar baru untuk browser automation.

Async Scraping dengan Playwright

Untuk dataset besar, scraping async bisa menghemat waktu secara drastis:

import asyncio
from playwright.async_api import async_playwright
import pandas as pd

async def scrape_page(browser, url):
    page = await browser.new_page()
    await page.goto(url)
    await page.wait_for_selector(".product-card")
    
    products = await page.query_selector_all(".product-card")
    results = []
    
    for product in products:
        name = await (await product.query_selector(".product-name")).inner_text()
        price = await (await product.query_selector(".product-price")).inner_text()
        results.append({"name": name, "price": price})
    
    await page.close()
    return results

async def main():
    urls = [f"https://example.com/page/{i}" for i in range(1, 21)]
    all_data = []
    
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        
        # Scrape 5 halaman sekaligus
        for i in range(0, len(urls), 5):
            batch = urls[i:i+5]
            tasks = [scrape_page(browser, url) for url in batch]
            results = await asyncio.gather(*tasks)
            
            for result in results:
                all_data.extend(result)
            
            print(f"Batch {i//5 + 1} selesai")
        
        await browser.close()
    
    df = pd.DataFrame(all_data)
    df.to_csv("products_async.csv", index=False)
    print(f"Total: {len(all_data)} produk!")

asyncio.run(main())

Naik Level: Web Scraping dengan Scrapy

Kalau kamu serius mau scraping di skala besar — ribuan sampai jutaan halaman — pakai Scrapy. Ini adalah framework scraping paling powerful di Python.

Scrapy bukan sekadar library, dia adalah framework lengkap yang sudah include:

  • Request management dan concurrency
  • Pipeline untuk memproses dan menyimpan data
  • Middleware untuk rotasi user-agent dan proxy
  • Built-in retry mechanism
  • Export ke berbagai format (JSON, CSV, XML)

Install dan Buat Project Scrapy

pip install scrapy
scrapy startproject my_scraper
cd my_scraper
scrapy genspider quotes quotes.toscrape.com

Tulis Spider

Buka file my_scraper/spiders/quotes.py:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["https://quotes.toscrape.com/"]
    
    def parse(self, response):
        for quote in response.css("div.quote"):
            yield {
                "text": quote.css("span.text::text").get(),
                "author": quote.css("small.author::text").get(),
                "tags": quote.css("a.tag::text").getall(),
            }
        
        # Ikuti pagination
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, self.parse)

Jalankan:

scrapy crawl quotes -o quotes.json

Dan voila — semua data tersimpan dalam quotes.json. Scrapy otomatis handle pagination, error handling, dan rate limiting. Dia juga punya robots.txt compliance yang bisa kamu aktifkan di settings.py:

# settings.py
ROBOTSTXT_OBEY = True
DOWNLOAD_DELAY = 1
CONCURRENT_REQUESTS = 8

Scrapy + Playwright untuk Situs Dinamis

Di 2026, ada plugin scrapy-playwright yang memungkinkan kamu pakai browser rendering langsung di dalam Scrapy pipeline:

pip install scrapy-playwright
import scrapy


class DynamicSpider(scrapy.Spider):
    name = "dynamic"
    
    def start_requests(self):
        yield scrapy.Request(
            url="https://example.com/dynamic-page",
            meta={"playwright": True},
        )
    
    def parse(self, response):
        for item in response.css(".dynamic-item"):
            yield {
                "title": item.css("h3::text").get(),
                "content": item.css("p::text").get(),
            }

Ini kombinasi terbaik untuk scraping modern: kekuatan Scrapy + rendering JavaScript dari Playwright.


Best Practices: Biar Scraper-mu Nggak Gampang Mati

Setelah bertahun-tahun scraping, saya belajar banyak dari kesalahan. Berikut ini tips yang saya wish someone told me earlier:

1. Selalu Pakai Header yang Realistis

Banyak situs yang nge-block request tanpa User-Agent yang jelas:

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/128.0.0.0 Safari/537.36",
    "Accept-Language": "id-ID,id;q=0.9,en;q=0.8",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}

response = requests.get(url, headers=headers)

2. Implementasi Retry Mechanism

Server bisa error kapan saja. Selalu siapkan retry:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)

response = session.get(url, headers=headers, timeout=30)

3. Cache Response Saat Development

Nggak mau kan setiap kali debug harus request ulang? Pakai requests-cache:

pip install requests-cache
import requests_cache

requests_cache.install_cache("my_cache", expire_after=3600)  # Cache 1 jam

# Request pertama ke server, setelahnya dari cache
response = requests.get(url)

4. Handle Error Gracefully

def safe_scrape(url):
    try:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        return BeautifulSoup(response.text, "lxml")
    except requests.exceptions.HTTPError as e:
        print(f"HTTP error untuk {url}: {e}")
    except requests.exceptions.ConnectionError:
        print(f"Koneksi gagal: {url}")
    except requests.exceptions.Timeout:
        print(f"Timeout: {url}")
    except Exception as e:
        print(f"Error tak terduga: {e}")
    return None

5. Simpan Progress

Kalau scraper-mu berjalan lama, simpan progress secara berkala supaya kalau crash, kamu nggak harus mulai dari awal:

import json
import os

PROGRESS_FILE = "progress.json"

def load_progress():
    if os.path.exists(PROGRESS_FILE):
        with open(PROGRESS_FILE, "r") as f:
            return json.load(f)
    return {"last_page": 0, "scraped_ids": []}

def save_progress(data):
    with open(PROGRESS_FILE, "w") as f:
        json.dump(data, f)

# Di dalam loop scraping:
progress = load_progress()
start_page = progress["last_page"]

for page in range(start_page, total_pages):
    # ... scraping logic ...
    progress["last_page"] = page + 1
    save_progress(progress)

6. Rotasi User-Agent dan Proxy

Kalau kamu scraping ribuan halaman, rotasi user-agent bisa mengurangi risiko di-ban:

import random

user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/128.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15",
    "Mozilla/5.0 (X11; Linux x86_64; rv:130.0) Gecko/20100101 Firefox/130.0",
]

headers = {"User-Agent": random.choice(user_agents)}

Studi Kasus: Scrape Data Produk E-Commerce

Sekarang mari kita gabungkan semua yang sudah dipelajari ke dalam studi kasus nyata. Kita akan buat scraper yang cukup robust untuk mengambil data produk:

import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import random
from datetime import datetime


class ProductScraper:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                          "AppleWebKit/537.36 Chrome/128.0.0.0 Safari/537.36",
            "Accept-Language": "id-ID,id;q=0.9",
        })
        self.data = []
        self.scraped_at = datetime.now().isoformat()
    
    def fetch_page(self, url):
        try:
            response = self.session.get(url, timeout=30)
            response.raise_for_status()
            return BeautifulSoup(response.text, "lxml")
        except Exception as e:
            print(f"[ERROR] Gagal fetch {url}: {e}")
            return None
    
    def parse_products(self, soup):
        products = soup.select(".product-card")
        
        for product in products:
            try:
                name = product.select_one(".product-title").get_text(strip=True)
                price = product.select_one(".product-price").get_text(strip=True)
                rating = product.select_one(".rating-value")
                rating_text = rating.get_text(strip=True) if rating else "N/A"
                
                self.data.append({
                    "name": name,
                    "price": price,
                    "rating": rating_text,
                    "scraped_at": self.scraped_at,
                })
            except AttributeError:
                continue
        
        return len(products)
    
    def scrape(self, base_url, max_pages=10):
        print(f"🚀 Mulai scraping: {base_url}")
        
        for page in range(1, max_pages + 1):
            url = f"{base_url}?page={page}"
            soup = self.fetch_page(url)
            
            if not soup:
                break
            
            count = self.parse_products(soup)
            print(f"  ✅ Halaman {page}: {count} produk")
            
            if count == 0:
                print("  📌 Tidak ada data lagi, berhenti.")
                break
            
            # Random delay antara 1-3 detik
            delay = random.uniform(1, 3)
            time.sleep(delay)
        
        print(f"🏁 Selesai! Total: {len(self.data)} produk")
        return self.data
    
    def save(self, filename="products.csv"):
        df = pd.DataFrame(self.data)
        df.to_csv(filename, index=False, encoding="utf-8-sig")
        print(f"💾 Disimpan ke {filename}")


# Penggunaan
if __name__ == "__main__":
    scraper = ProductScraper()
    scraper.scrape("https://example-ecommerce.com/products", max_pages=20)
    scraper.save("ecommerce_products.csv")

Pattern seperti ini sudah saya pakai di berbagai project dan terbukti solid. Tinggal kamu sesuaikan selector CSS-nya dengan situs target.


Tantangan Web Scraping di 2026 dan Cara Mengatasinya

Jujur saja, web scraping di 2026 makin menantang. Banyak situs yang sudah pakai teknologi anti-scraping canggih. Tapi bukan berarti nggak bisa diakali.

Cloudflare dan Anti-Bot

Banyak situs yang dilindungi Cloudflare. Pakai requests biasa nggak akan tembus. Solusinya:

  • Playwright/Scrapy-Playwright: Browser automation bisa menembus challenge halaman.
  • FlareSolverr: Proxy server yang bisa menyelesaikan Cloudflare challenge.
  • Respect delay: Kadang cukup dengan memberi delay yang wajar.

CAPTCHA

Ini musuh utama scraper. Beberapa pendekatan:

  • Hindari: Crawl dengan lambat supaya nggak trigger CAPTCHA.
  • Layanan solving: Ada layanan seperti 2Captcha atau Anti-Captcha.
  • Session management: Simpan cookies supaya kamu nggak dianggap visitor baru terus.

Rate Limiting

Server modern makin pintar mendeteksi pola request yang mencurigakan. Solusinya:

  • Gunakan proxy pool (rotating proxy)
  • Random delay antar request
  • Simulasi perilaku browsing manusia

Menyimpan dan Mengolah Data Hasil Scraping

Data mentah itu belum berguna. Kamu perlu membersihkan dan mengolahnya:

import pandas as pd

df = pd.read_csv("products.csv")

# Bersihkan harga: "Rp 1.500.000" -> 1500000
df["price_numeric"] = (
    df["price"]
    .str.replace("Rp", "")
    .str.replace(".", "")
    .str.strip()
    .astype(float)
)

# Filter produk di atas 500rb
expensive = df[df["price_numeric"] > 500000]

# Statistik sederhana
print(f"Rata-rata harga: Rp {df['price_numeric'].mean():,.0f}")
print(f"Produk termurah: Rp {df['price_numeric'].min():,.0f}")
print(f"Produk termahal: Rp {df['price_numeric'].max():,.0f}")

# Export ke Excel
expensive.to_excel("expensive_products.xlsx", index=False)

Kesimpulan

Web scraping dengan Python itu skill yang sangat bernilai di 2026. Dari riset pasar, pengumpulan dataset untuk AI, sampai monitoring harga — semuanya butuh scraping.

Ringkasan apa yang sudah kita bahas:

  1. BeautifulSoup + Requests → Untuk scraping sederhana dan situs statis.
  2. Playwright → Untuk situs yang di-render JavaScript.
  3. **Scrapy3. Scrapy → Untuk proyek scraping berskala besar.

Kalau lo butuh bantuan atau mau tanya lebih lanjut, langsung email ke [email protected]. Happy scraping!

FAQ

Q: Apakah web scraping legal? A: Tergantung situasi. Scraping data publik umumnya legal, tapi kalau situs tersebut punya kebijakan anti-scraping atau lo mengambil data privat, itu bisa bermasalah. Selalu baca robots.txt dan Terms of Service sebelum scraping.

Q: Apakah saya butuh proxy untuk scraping? A: Untuk scraping skala kecil biasanya tidak perlu. Tapi untuk scraping dalam jumlah besar, proxy sangat disarankan agar IP kamu tidak diblokir oleh target website.

Q: Apa perbedaan BeautifulSoup dan Scrapy? A: BeautifulSoup lebih ringan dan cocok untuk scraping sederhana. Scrapy adalah framework lengkap untuk proyek scraping berskala besar dengan fitur pipeline, middleware, dan parallelism built-in.