Python

Web Scraping dengan Python dan BeautifulSoup - Tutorial Praktis

Pertama kali saya coba web scraping, tujuannya simpel: mau ambil data harga produk dari e-commerce buat dibandingin. Nggak nyangka ternyata seru banget, dan Python bikin semuanya jadi gampang. Sekarang web scraping udah jadi skill wajib buat data analyst, researcher, siapa aja yang butuh data dari internet.

Di tutorial ini, saya mau share cara pakai Python + BeautifulSoup buat scraping website. Nggak cuma teori, tapi contoh nyata yang bisa langsung kamu coba. Yuk mulai.

Kenalan dengan BeautifulSoup dan Requests

Sebelum mulai coding, kita perlu dua library utama: requests buat ambil halaman web, dan BeautifulSoup buat parsing HTML-nya. Kedua library ini udah jadi standar di dunia Python web scraping.

Requests handle HTTP request GET, POST, headers, cookies, semuanya. BeautifulSoup-nya yang kerja keras parsing HTML jadi object tree yang bisa kita navigate dan search dengan mudah.


# Install kedua library
pip install requests beautifulsoup4

# Cek versi yang terinstall
pip show requests beautifulsoup4

Kalau kamu pakai virtual environment (yang sebaiknya selalu dipakai), pastikan udah aktif sebelum install. Saya sering lupa hal ini dan heran kenapa library nggak ke-deteksi di VS Code.

Ambil Halaman Web Pertama Kali

Langkah paling dasar: ambil HTML dari sebuah URL. Pakai requests.get() dan cek status code-nya. Status 200 berarti sukses, 404 artinya halaman nggak ketemu, 403 berarti akses ditolak.


import requests
from bs4 import BeautifulSoup

# Ambil halaman web
url = "https://quotes.toscrape.com"
response = requests.get(url)

# Cek status
print(f"Status Code: {response.status_code}")
print(f"Content-Type: {response.headers['Content-Type']}")

# Parse HTML
soup = BeautifulSoup(response.text, 'html.parser')

# Lihat title halaman
print(f"Page Title: {soup.title.string}")

Website quotes.toscrape.com ini sengaja dibuat buat latihan scraping, jadi aman banget buat eksperimen. Nggak perlu khawatir kena blokir atau melanggar ToS.

Cari Element dengan find dan find_all

Dua method paling sering dipakai di BeautifulSoup: find() buat ambil element pertama yang cocok, dan find_all() buat ambil semua element yang cocok. Kita bisa search berdasarkan tag, class, id, atau atribut apapun.


# Ambil semua quotes di halaman
quotes = soup.find_all('div', class_='quote')

for quote in quotes:
    # Ambil teks quote
    text = quote.find('span', class_='text').get_text()
    
    # Ambil nama author
    author = quote.find('small', class_='author').get_text()
    
    # Ambil tags
    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("---")

Perhatikan penggunaan class_ dengan underscore. Ini karena class adalah reserved word di Python, jadi BeautifulSoup pakai class_ sebagai parameter. Saya pernah debug error ini setengah jam sebelum sadar.

Navigasi DOM Tree

BeautifulSoup bukan cuma buat search, tapi juga buat navigasi element secara hierarki. Kamu bisa naik ke parent, turun ke children, atau geser ke sibling. Ini berguna banget kalau structure HTML-nya nggak rapi.


# Ambil satu quote pertama
first_quote = soup.find('div', class_='quote')

# Parent element
print(f"Parent tag: {first_quote.parent.name}")

# Children langsung
for child in first_quote.children:
    if child.name:  # Skip text nodes
        print(f"Child: {child.name}, class={child.get('class')}")

# Next sibling (element setelahnya)
next_elem = first_quote.find_next_sibling('div', class_='quote')
if next_elem:
    print(f"Next quote: {next_elem.find('span', class_='text').get_text()[:50]}...")

# Cari element berdasarkan CSS selector (lebih fleksibel)
authors = soup.select('small.author')
print(f"Total authors found: {len(authors)}")

Method select() pakai syntax CSS selector yang familiar banget buat frontend developer. Kamu bisa pakai div.class > span, ul li:nth-child(2), dan selector lainnya.

Handle Headers dan User-Agent

Banyak website yang nge-block request tanpa User-Agent header. Ini cara pertama yang bisa kamu lakukan biar request kamu nggak ditolak mentah-mentah.


# Set custom headers
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.5',
}

response = requests.get(url, headers=headers)
print(f"Status with headers: {response.status_code}")

User-Agent bikin request kamu terlihat seperti browser biasa. Tapi ingat, ini bukan izin buat scraping masif yang bisa ganggu server orang. Tetap pakai rate limiting dan hormati robots.txt.

Scraping Multiple Pages

Seringkali data yang kamu butuh nggak ada di satu halaman aja. Kita perlu loop ke beberapa page. Ini contoh scraping dengan pagination:


import time

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

for page_num in range(1, 6):  # Scrape 5 halaman pertama
    url = base_url.format(page_num)
    response = requests.get(url, headers=headers)
    
    if response.status_code != 200:
        print(f"Failed on page {page_num}: {response.status_code}")
        break
    
    soup = BeautifulSoup(response.text, 'html.parser')
    quotes = soup.find_all('div', class_='quote')
    
    for quote in quotes:
        data = {
            'text': quote.find('span', class_='text').get_text(),
            'author': quote.find('small', class_='author').get_text(),
            'tags': [t.get_text() for t in quote.find_all('a', class_='tag')]
        }
        all_quotes.append(data)
    
    print(f"Page {page_num}: {len(quotes)} quotes scraped")
    
    # Delay antar request (penting!)
    time.sleep(1)

print(f"\nTotal quotes collected: {len(all_quotes)}")

Delay itu wajib. Tanpa delay, request kamu bakal terlihat seperti bot dan kemungkinan besar kena rate limit atau IP block. time.sleep(1) udah cukup buat kebanyakan kasus.

Simpan Data ke CSV

Setelah data terkumpul, langkah selanjutnya adalah menyimpannya. Format CSV paling universal dan bisa dibuka di Excel, Google Sheets, atau di-load ke pandas.


import csv

# Simpan ke CSV
with open('quotes_data.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames=['text', 'author', 'tags'])
    writer.writeheader()
    
    for quote in all_quotes:
        # Convert tags list ke string
        quote_copy = quote.copy()
        quote_copy['tags'] = ', '.join(quote_copy['tags'])
        writer.writerow(quote_copy)

print("Data saved to quotes_data.csv")

Kalau kamu mau format JSON juga gampang:


import json

with open('quotes_data.json', 'w', encoding='utf-8') as f:
    json.dump(all_quotes, f, ensure_ascii=False, indent=2)

print("Data saved to quotes_data.json")

Handle JavaScript-Rendered Pages

Beberapa website pakai JavaScript buat render konten. BeautifulSoup nggak bisa handle ini karena dia cuma parsing HTML statis. Solusinya pakai Selenium atau Playwright.


# Install Selenium
pip install selenium webdriver-manager

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from bs4 import BeautifulSoup

# Setup Chrome headless
options = Options()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')

driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install()),
    options=options
)

# Buka halaman
driver.get("https://example.com/dynamic-page")

# Tunggu sampai element muncul
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CLASS_NAME, "target-class"))
)

# Ambil page source setelah JS render
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')

# Sekarang bisa pakai BeautifulSoup seperti biasa
data = soup.find_all('div', class_='target-class')
print(f"Found {len(data)} elements")

driver.quit()

Selenium lebih berat daripada requests, tapi kadang nggak ada pilihan lain kalau targetnya SPA (Single Page Application) yang render di client-side.

Praktik Terbaik Web Scraping

Web scraping itu powerful, tapi ada etika dan best practice yang harus kamu ikuti:

  • Cek robots.txt dulu: Buka example.com/robots.txt buat lihat halaman mana yang boleh di-scrape
  • Kasih delay antar request: Jangan bombardir server dengan ratusan request per detik
  • Pakai session: requests.Session() lebih efisien karena reuse connection
  • Handle error gracefully: Selalu cek status code dan handle exception
  • Simpan raw HTML: Buat debugging, simpan HTML mentah sebelum parsing
  • Respect rate limits: Kalau kena 429 (Too Many Requests), tambah delay

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

# Setup session dengan auto-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)

# Pakai session untuk semua request
response = session.get(url, headers=headers, timeout=10)

Konfigurasi retry di atas bikin scraper kamu lebih resilient. Kalau server error (5xx), dia otomatis retry sampai 3 kali dengan exponential backoff.

Kasus Nyata: Scraping Berita

Ini contoh lebih real-world: scraping headline berita dari sebuah portal. Pola ini bisa kamu adaptasi buat website apapun.


import requests
from bs4 import BeautifulSoup
from datetime import datetime

def scrape_news(url, headers):
    """Scrape news headlines from a portal."""
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
    except requests.RequestException as e:
        print(f"Request failed: {e}")
        return []
    
    soup = BeautifulSoup(response.text, 'html.parser')
    articles = []
    
    # Contoh selector - sesuaikan dengan target website
    for item in soup.find_all('article'):
        title_elem = item.find(['h2', 'h3'])
        link_elem = item.find('a')
        time_elem = item.find('time')
        
        if title_elem and link_elem:
            article = {
                'title': title_elem.get_text(strip=True),
                'url': link_elem.get('href', ''),
                'date': time_elem.get('datetime', '') if time_elem else '',
                'scraped_at': datetime.now().isoformat()
            }
            articles.append(article)
    
    return articles

# Jalankan
news = scrape_news("https://example-news.com", headers)
for n in news[:5]:
    print(f"- {n['title']}")

Troubleshooting Umum

Beberapa masalah yang sering muncul waktu scraping:

  • 403 Forbidden: Tambahkan User-Agent header, atau coba pakai session dengan cookies
  • Content kosong: Kemungkinan konten di-render pakai JavaScript, cek pakai Selenium
  • Encoding error: Set response.encoding = 'utf-8' sebelum akses response.text
  • Element nggak ketemu: Cek apakah ada iframe, atau class name berubah dynamic
  • IP kena block: Pakai proxy rotation atau kurangi frequency request

# Handle encoding masalah
response = requests.get(url, headers=headers)
response.encoding = response.apparent_encoding  # Auto-detect encoding
soup = BeautifulSoup(response.text, 'html.parser')

# Debug: simpan HTML ke file
with open('debug_page.html', 'w', encoding='utf-8') as f:
    f.write(response.text)
print("HTML saved for debugging")

Kesimpulan

Web scraping dengan Python itu skill yang sangat berguna dan relatif mudah dipelajari. Dengan requests + BeautifulSoup, kamu udah bisa handle 80% kasus scraping. Selenium buat sisanya yang butuh JavaScript rendering.

Yang penting: selalu hormati website yang kamu scrape. Pakai delay, cek robots.txt, dan jangan overload server mereka. Web scraping yang baik itu yang nggak ganggu orang lain.

Mau coba langsung? Mulai dari quotes.toscrape.com, lalu coba scrape website favorit kamu. Dari situ, kemungkinannya unlimited price monitoring, sentiment analysis, research data collection, dan banyak lagi.


You may also like


0 Comments


Leave a Reply

Comments with links or spam keywords will be rejected.
Scroll to Top