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.
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.
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.
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.
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.
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.
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.
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")
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.
Web scraping itu powerful, tapi ada etika dan best practice yang harus kamu ikuti:
example.com/robots.txt buat lihat halaman mana yang boleh di-scraperequests.Session() lebih efisien karena reuse connection
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.
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']}")
Beberapa masalah yang sering muncul waktu scraping:
response.encoding = 'utf-8' sebelum akses response.text
# 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")
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.