Beberapa bulan lalu, saya ingin bikin ringkasan harian berisi cerita-cerita teratas di Hacker News untuk tim kami di Thunderbit. Awalnya saya pikir cukup bookmark situsnya, lalu tiap pagi buka dan baca satu per satu. Ide itu cuma tahan sekitar tiga hari, sebelum saya sadar kalau saya habiskan 20 menit setiap hari hanya buat baca headline dan copy-paste tautan ke spreadsheet.
Hacker News adalah salah satu sumber intelijen teknologi paling kaya dan paling padat di internet — sekitar 13 juta kunjungan bulanan, kurang lebih 1.300 cerita baru masuk tiap hari, dan sekitar 13.000 komentar dibuat setiap hari. Entah Anda lagi memantau tren teknologi baru, mengawasi brand Anda, membangun pipeline rekrutmen dari thread "Who's Hiring", atau sekadar mau tetap paham apa yang sedang jadi perhatian dunia developer, mengikuti semuanya secara manual jelas bukan strategi yang efisien.
Kabar baiknya: scraping Hacker News dengan Python ternyata cukup gampang. Di panduan ini, saya akan tunjukkan dua metode lengkap — scraping HTML dengan BeautifulSoup dan HN Firebase API resmi — lengkap dengan pagination, ekspor data, pola siap produksi, plus jalan pintas tanpa kode kalau Python terasa terlalu ribet.
Kenapa Scrape Hacker News dengan Python?
Hacker News bukan sekadar pengumpul tautan biasa. Ini feed kurasi dari komunitas, tempat cerita teknologi paling menarik naik ke atas lewat upvote dan diskusi aktif. Audiensnya sangat didominasi profesional teknologi (76% pria, usia utama 25–34 tahun), dan tingkat traffic langsung sebesar 66% menunjukkan bahwa pembacanya loyal dan rutin, bukan sekadar lewat doang.
Inilah alasan orang mengekstrak data HN:
| Use Case | Hasil yang Didapat |
|---|---|
| Ringkasan harian teknologi | Cerita teratas, skor, dan tautan dikirim ke email atau Slack |
| Pemantauan brand/kompetitor | Notifikasi saat perusahaan atau produk Anda disebut |
| Analisis tren | Melacak teknologi, bahasa, atau topik yang makin populer dari waktu ke waktu |
| Rekrutmen | Mengurai thread "Who's Hiring" untuk lowongan, stack teknologi, dan sinyal gaji |
| Riset konten | Menemukan topik yang performanya tinggi untuk dijadikan bahan tulisan atau dibagikan |
| Analisis sentimen | Mengukur opini komunitas terhadap produk, peluncuran, atau perubahan industri |
Perusahaan bernilai gabungan lebih dari $400 miliar — Stripe, Dropbox, Airbnb — menganggap Hacker News sebagai sumber feedback awal dan pengguna yang sangat penting. Drew Houston mem-post demo Dropbox di HN pada April 2007, dan demo itu langsung naik ke posisi #1; daftar tunggu beta melonjak dari 5.000 ke 75.000 pengguna hanya dalam satu hari. Artinya, data HN bukan cuma menarik — tapi juga punya nilai komersial.
Datanya memang terbuka untuk umum, tapi struktur situsnya bikin pengumpulan manual jadi capek. Automasi dengan Python adalah solusi yang paling masuk akal.
Dua Cara Scrape Hacker News dengan Python: Gambaran Umum
Panduan ini mencakup dua pendekatan lengkap yang bisa langsung dijalankan:
- Scraping HTML dengan
requests+ BeautifulSoup — mengambil HTML mentah dari news.ycombinator.com lalu mem-parse-nya untuk mengekstrak data cerita. Cocok untuk belajar dasar web scraping dan mengambil persis data yang tampil di halaman. - Hacker News Firebase API resmi — memanggil endpoint JSON secara langsung, jadi tidak perlu parsing HTML. Lebih cocok untuk pipeline data yang andal, akses komentar, dan data historis.
Berikut perbandingan berdampingan supaya Anda bisa pilih metode yang paling pas:
| Kriteria | Scraping HTML (requests + BS4) | HN Firebase API | Thunderbit (Tanpa Kode) |
|---|---|---|---|
| Kompleksitas setup | Sedang (parse selector HTML) | Rendah (endpoint JSON) | Tidak ada (ekstensi Chrome 2 klik) |
| Keterkinian data | Real-time halaman depan | Real-time (item apa pun berdasarkan ID) | Real-time |
| Risiko rate limit | Sedang (robots.txt meminta jeda crawl 30 detik) | Rendah (resmi, cukup longgar) | Dikelola oleh Thunderbit |
| Akses komentar | Sulit (HTML bertingkat) | Mudah (ID item rekursif) | Fitur scraping subpage |
| Data historis | Terbatas | Lewat Algolia Search API | Tidak tersedia |
| Paling cocok untuk | Belajar dasar scraping | Pipeline data yang andal | Non-developer, ekspor cepat |
Kedua metode ini dilengkapi kode Python lengkap yang bisa langsung Anda jalankan. Dan kalau Anda cuma mau datanya tanpa nulis kode sama sekali, saya juga akan bahas opsi itu.
Sebelum Mulai
- Tingkat kesulitan: Pemula hingga menengah
- Waktu yang dibutuhkan: ~15–20 menit per metode
- Yang Anda perlukan:
- Python 3.11+ terpasang
- Terminal atau code editor
- Browser Chrome (kalau ingin cek HTML HN atau coba opsi tanpa kode)
- Thunderbit Chrome Extension (opsional, untuk metode tanpa kode)

Menyiapkan Environment Python Anda
Sebelum menyentuh data HN, mari siapkan environment-nya dulu. Saya sarankan bikin virtual environment supaya dependensi proyek tetap rapi.
# Buat dan aktifkan virtual environment
python3 -m venv hn-scraper
# macOS/Linux:
source hn-scraper/bin/activate
# Windows:
hn-scraper\Scripts\activate
# Instal paket yang dibutuhkan untuk kedua metode
pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5
Untuk pola produksi nanti (cache, retry), Anda juga akan butuh:
pip install requests-cache==1.3.1 tenacity==9.1.4
Tidak perlu API key khusus, tidak perlu token autentikasi. Data HN terbuka.
Metode 1: Scrape Hacker News dengan Python Menggunakan BeautifulSoup
Ini pendekatan klasik — ambil HTML, parse, lalu tarik data yang Anda butuhkan. Begini cara kebanyakan orang belajar web scraping, dan layout HN yang sederhana berbasis tabel bikin ini jadi tempat latihan yang ideal.
Langkah 1: Ambil Halaman Depan Hacker News
Buka editor Anda dan buat file bernama scrape_hn_bs4.py. Berikut kode awalnya:
import requests
from bs4 import BeautifulSoup
url = "https://news.ycombinator.com/news"
headers = {"User-Agent": "Mozilla/5.0 (educational HN scraper)"}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")
print(f"Status: {response.status_code}, Page length: {len(response.text)} chars")
Jalankan. Anda seharusnya melihat Status: 200 dan panjang halaman sekitar 40.000–50.000 karakter. Itu berarti HTML mentah halaman depan HN sudah ada di memori dan siap diparse.
Langkah 2: Pahami Struktur HTML-nya
HN memakai layout berbasis tabel — bukan CSS grid atau flex modern. Setiap cerita di halaman terdiri dari dua baris <tr> utama:
- Baris cerita (
<tr class="athing submission">): berisi peringkat, judul, dan tautan - Baris metadata (
<tr>berikutnya): berisi poin, penulis, waktu, dan jumlah komentar
Selector pentingnya:
span.titleline > a— judul cerita dan URLspan.score— jumlah vote (misalnya "118 points")a.hnuser— username penulisspan.age— waktu posting<a>terakhir di.subtextdengan teks yang berisi "comment" — jumlah komentar
Kalau Anda klik kanan pada judul cerita di Chrome lalu pilih "Inspect", Anda akan melihat sesuatu seperti ini:
<span class="titleline">
<a href="https://darkbloom.dev">Darkbloom – Private inference on idle Macs</a>
</span>
Dan baris metadata di bawahnya:
<span class="score" id="score_47788542">118 points</span>
by <a href="user?id=twapi" class="hnuser">twapi</a>
<span class="age" title="2026-04-16T04:06:39 1776312399">
<a href="item?id=47788542">2 hours ago</a>
</span>
| <a href="item?id=47788542">65 comments</a>
Memahami selector ini penting — kalau suatu saat markup HN berubah, Anda harus update selector-nya. (Spoiler: metode API menghindari masalah ini sepenuhnya.)
Langkah 3: Ekstrak Judul, Tautan, dan Skor
Sekarang masuk ke inti. Kita akan looping semua baris cerita, ambil judul dan tautannya dari baris cerita, lalu ambil skor dari baris metadata tepat di bawahnya.
import requests
from bs4 import BeautifulSoup
from pprint import pprint
url = "https://news.ycombinator.com/news"
headers = {"User-Agent": "Mozilla/5.0 (educational HN scraper)"}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")
stories = []
story_rows = soup.select("tr.athing")
for row in story_rows:
# Judul dan URL dari baris cerita
title_tag = row.select_one("span.titleline > a")
if not title_tag:
continue
title = title_tag.get_text()
link = title_tag.get("href", "")
# Metadata dari baris sibling berikutnya
meta_row = row.find_next_sibling("tr")
score = 0
author = ""
comments = 0
if meta_row:
if score_tag := meta_row.select_one("span.score"):
score = int(score_tag.get_text().replace(" points", ""))
if author_tag := meta_row.select_one("a.hnuser"):
author = author_tag.get_text()
# Jumlah komentar: <a> terakhir yang teksnya mengandung "comment"
for a_tag in meta_row.select("a"):
text = a_tag.get_text()
if "comment" in text:
comments = int(text.split("\xa0")[0])
stories.append({
"title": title,
"url": link,
"score": score,
"author": author,
"comments": comments,
})
# Filter cerita dengan 50+ poin, urutkan berdasarkan skor
top_stories = sorted(
[s for s in stories if s["score"] >= 50],
key=lambda x: x["score"],
reverse=True,
)
pprint(top_stories[:10])
Beberapa catatan dari kode ini:
- Operator walrus (
:=) tersedia di Python 3.8+. Ini memudahkan kita meng-assign dan mengecek sekaligus — berguna untuk elemen opsional sepertispan.scoreyang tidak selalu ada di setiap baris (misalnya job post tidak punya score). - HN memakai
\xa0(non-breaking space) antara angka dan kata "comments", jadi kita perlu split berdasarkan karakter itu. - Cerita yang mengarah ke halaman HN lain (seperti post "Ask HN") akan punya URL relatif yang dimulai dengan
item?id=. Anda mungkin ingin menambahkanhttps://news.ycombinator.com/untuk kasus seperti itu.
Langkah 4: Jalankan dan Lihat Hasilnya
Simpan lalu jalankan:
python scrape_hn_bs4.py
Anda akan melihat output seperti ini:
[{'author': 'twapi',
'comments': 65,
'score': 118,
'title': 'Darkbloom – Private inference on idle Macs',
'url': 'https://darkbloom.dev'},
{'author': 'sebg',
'comments': 203,
'score': 247,
'title': 'Show HN: I built an open-source Perplexity alternative',
'url': 'https://github.com/...'},
...]
Itu 30 cerita dari halaman 1. Tapi HN punya ratusan cerita aktif pada saat tertentu. Kita akan bahas pagination di bagian berikutnya.
Metode 2: Scrape Hacker News dengan Python Menggunakan API Resmi
HN Firebase API adalah cara resmi yang disediakan untuk mengakses data Hacker News. Tidak perlu autentikasi, tidak perlu API key, dan tidak perlu parsing HTML. Anda akan dapat respons JSON yang bersih. Saya pakai metode ini untuk semua kebutuhan yang harus jalan andal di production.
Endpoint API Utama yang Perlu Anda Tahu
Base URL-nya adalah https://hacker-news.firebaseio.com/v0/. Berikut endpoint yang relevan:
| Endpoint | Mengembalikan | Contoh |
|---|---|---|
/v0/topstories.json | Array hingga 500 ID cerita teratas | [47788542, 47787901, ...] |
/v0/newstories.json | Hingga 500 ID cerita terbaru | Format sama |
/v0/beststories.json | Hingga 500 ID cerita terbaik | Format sama |
/v0/askstories.json | Hingga 200 ID cerita "Ask HN" | Format sama |
/v0/showstories.json | Hingga 200 ID cerita "Show HN" | Format sama |
/v0/jobstories.json | Hingga 200 ID cerita lowongan | Format sama |
/v0/item/{id}.json | Detail lengkap untuk item apa pun (cerita, komentar, polling) | Objek JSON |
/v0/user/{username}.json | Profil pengguna | Objek JSON |
/v0/maxitem.json | ID item maksimum saat ini | Integer (misalnya 47789427) |
Contoh item cerita terlihat seperti ini:
{
"by": "twapi",
"descendants": 65,
"id": 47788542,
"kids": [47789171, 47788769, 47788762],
"score": 118,
"time": 1776312399,
"title": "Darkbloom – Private inference on idle Macs",
"type": "story",
"url": "https://darkbloom.dev"
}
Field kids berisi ID komentar anak langsung. Setiap komentar juga merupakan item yang bisa punya kids sendiri — itulah struktur pohon komentar HN.
Langkah 1: Ambil ID Cerita Teratas
Buat file bernama scrape_hn_api.py:
import requests
import time
from pprint import pprint
API_BASE = "https://hacker-news.firebaseio.com/v0"
# Ambil ID cerita teratas
response = requests.get(f"{API_BASE}/topstories.json")
story_ids = response.json()
print(f"Got {len(story_ids)} top story IDs")
# Output: Got 500 top story IDs
500 ID cerita dalam satu request — tanpa parsing, tanpa selector, cuma array JSON.
Langkah 2: Ambil Detail Cerita Berdasarkan ID
Sekarang kita butuh data cerita yang sebenarnya. Di sinilah masalah fan-out muncul: 500 cerita berarti 500 panggilan API terpisah. Berdasarkan pengujian saya, setiap request item memakan waktu sekitar 1,2 detik kalau dijalankan berurutan. Untuk 500 cerita, itu kira-kira 10 menit.
Untuk sebagian besar use case, Anda tidak butuh semua 500. Berikut kode untuk ambil 30 teratas:
def fetch_story(story_id):
"""Ambil detail satu cerita dari HN API."""
resp = requests.get(f"{API_BASE}/item/{story_id}.json")
return resp.json()
# Ambil detail untuk 30 cerita teratas
stories = []
for sid in story_ids[:30]:
story = fetch_story(sid)
if story and story.get("type") == "story":
stories.append({
"title": story.get("title", ""),
"url": story.get("url", ""),
"score": story.get("score", 0),
"author": story.get("by", ""),
"comments": story.get("descendants", 0),
"time": story.get("time", 0),
"id": story.get("id"),
})
time.sleep(0.1) # Tetap sopan — beri jeda kecil antar request
# Urutkan berdasarkan skor, tampilkan 10 teratas
top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
pprint(top)
time.sleep(0.1) menambahkan jeda kecil sebagai bentuk sopan santun. Firebase API memang tidak menyebut rate limit secara eksplisit, tapi membombardir API apa pun tanpa jeda bukan praktik yang bagus.
Langkah 3: Scrape Komentar (Tree Walk Rekursif)
Di sinilah API benar-benar unggul dibanding scraping HTML. Komentar di HN bisa bertingkat sangat dalam — balasan ke balasan ke balasan. Kalau pakai HTML, Anda harus mem-parse struktur tabel bersarang yang rumit. Dengan API, setiap komentar punya field kids berisi ID anak-anaknya, lalu kita tinggal menelusuri pohonnya secara rekursif.
def fetch_comments(item_id, depth=0, max_depth=3):
"""Ambil komentar secara rekursif hingga max_depth."""
item = requests.get(f"{API_BASE}/item/{item_id}.json").json()
if not item or item.get("type") != "comment":
return []
comments = [{
"author": item.get("by", "[deleted]"),
"text": item.get("text", ""),
"depth": depth,
"id": item.get("id"),
}]
if depth < max_depth and item.get("kids"):
for kid_id in item["kids"]:
comments.extend(fetch_comments(kid_id, depth + 1, max_depth))
time.sleep(0.05)
return comments
# Contoh: ambil komentar untuk cerita teratas
if stories:
top_story = stories[0]
top_story_full = requests.get(f"{API_BASE}/item/{top_story['id']}.json").json()
if top_story_full.get("kids"):
print(f"\nKomentar untuk: {top_story['title']}")
all_comments = []
for kid_id in top_story_full["kids"][:5]: # 5 komentar level atas pertama
all_comments.extend(fetch_comments(kid_id, depth=0, max_depth=2))
time.sleep(0.1)
for c in all_comments[:15]:
indent = " " * c["depth"]
preview = c["text"][:80].replace("\n", " ") if c["text"] else "[no text]"
print(f"{indent}[{c['author']}] {preview}...")
Pendekatan rekursif ini jauh lebih mudah dibanding mencoba mem-parse thread komentar HTML yang bertingkat. Kalau Anda butuh tree komentar penuh, API adalah pilihan terbaik.
Langkah 4: Jalankan dan Lihat Hasilnya
python scrape_hn_api.py
Anda akan melihat data cerita yang terstruktur, lalu pratinjau komentar bertingkat. Datanya lebih bersih, akses komentarnya mudah, dan tidak ada risiko scraper Anda rusak cuma karena HN mengganti nama class CSS.
Melangkah Lebih Jauh dari Halaman 1: Pagination dan Data Historis
Kebanyakan tutorial scraping HN berhenti di halaman 1 — 30 cerita. Itu cukup untuk demo cepat, tapi use case nyata sering butuh cakupan yang lebih dalam.
Scraping Banyak Halaman dengan BeautifulSoup
Pagination HN memakai pola URL sederhana: ?p=2, ?p=3, dan seterusnya. Setiap halaman memuat 30 cerita, dan situs ini menyediakan sampai sekitar halaman 20 (kurang lebih 600 cerita total). Setelah itu, Anda akan dapat halaman kosong.
import time
def scrape_hn_pages(num_pages=5):
"""Scrape beberapa halaman cerita front page HN."""
all_stories = []
for page in range(1, num_pages + 1):
url = f"https://news.ycombinator.com/news?p={page}"
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")
story_rows = soup.select("tr.athing")
if not story_rows:
print(f"Page {page}: no stories found, stopping.")
break
for row in story_rows:
title_tag = row.select_one("span.titleline > a")
if not title_tag:
continue
meta_row = row.find_next_sibling("tr")
score = 0
if meta_row and (score_tag := meta_row.select_one("span.score")):
score = int(score_tag.get_text().replace(" points", ""))
all_stories.append({
"title": title_tag.get_text(),
"url": title_tag.get("href", ""),
"score": score,
})
print(f"Page {page}: scraped {len(story_rows)} stories")
# Hormati crawl-delay 30 detik di robots.txt
if page < num_pages:
time.sleep(30)
return all_stories
stories = scrape_hn_pages(5)
print(f"\nTotal stories scraped: {len(stories)}")
time.sleep(30) itu penting. robots.txt HN secara eksplisit minta jeda crawl 30 detik. Abaikan itu dan Anda bisa kena rate limit (HTTP 429) atau diblokir sementara. Lima halaman dengan interval 30 detik butuh sekitar 2,5 menit — tidak instan, tapi tetap sopan.
Buat pengguna yang tidak mau mengelola kode pagination, Thunderbit menangani pagination berbasis klik dan infinite scroll secara otomatis. Ia mengklik tombol "More" di bagian bawah halaman HN tanpa konfigurasi apa pun.
Scrape Halaman Hacker News dengan AI
Mengakses Data Historis Hacker News dengan Algolia API
Firebase API memberi Anda data saat ini. Untuk analisis historis — "Apa cerita Python paling populer di 2023?" atau "Bagaimana cakupan AI berubah selama 5 tahun terakhir?" — Anda perlu HN Algolia Search API.
import requests
ALGOLIA_BASE = "https://hn.algolia.com/api/v1"
def search_hn(query, tags="story", page=0, hits_per_page=20):
"""Cari HN lewat Algolia API."""
params = {
"query": query,
"tags": tags,
"page": page,
"hitsPerPage": hits_per_page,
}
resp = requests.get(f"{ALGOLIA_BASE}/search", params=params)
return resp.json()
# Contoh: temukan cerita scraping Python dengan 10+ poin sejak Jan 2024
results = search_hn(
query="python scraping",
tags="story",
)
print(f"Found {results['nbHits']} total results")
for hit in results["hits"][:5]:
print(f" [{hit.get('points', 0)} pts] {hit['title']}")
Untuk kueri dengan filter tanggal, pakai numericFilters:
import calendar, datetime
# Cerita sejak 1 Januari 2024
start_date = datetime.datetime(2024, 1, 1)
start_ts = int(calendar.timegm(start_date.timetuple()))
params = {
"query": "python web scraping",
"tags": "story",
"numericFilters": f"created_at_i>{start_ts},points>10",
"hitsPerPage": 50,
}
resp = requests.get(f"{ALGOLIA_BASE}/search_by_date", params=params)
data = resp.json()
print(f"Found {data['nbHits']} stories about Python web scraping since 2024 with >10 points")
Algolia API cepat (waktu proses server 5–9 ms), tidak butuh API key, dan mendukung pagination sampai 500 halaman. Untuk analisis historis skala besar, ini opsi terbaik yang tersedia.
Mengekspor Data Hacker News ke CSV, Excel, dan Google Sheets
Setiap tutorial scraping HN yang pernah saya lihat biasanya berakhir dengan output pprint() di terminal. Itu oke buat debugging, tapi kalau Anda sedang bikin digest harian atau analisis tren, Anda butuh datanya dalam file. Berikut cara melakukannya.
Ekspor ke CSV dengan Python
import csv
def export_to_csv(stories, filename="hn_stories.csv"):
"""Simpan cerita hasil scrape ke file CSV."""
fieldnames = ["title", "url", "score", "author", "comments"]
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(stories)
print(f"Saved {len(stories)} stories to {filename}")
export_to_csv(stories)
Ekspor ke Excel dengan Python
import pandas as pd
def export_to_excel(stories, filename="hn_stories.xlsx"):
"""Simpan cerita hasil scrape ke file Excel."""
df = pd.DataFrame(stories)
df.to_excel(filename, index=False, engine="openpyxl")
print(f"Saved {len(stories)} stories to {filename}")
export_to_excel(stories)
Pastikan openpyxl sudah terpasang — pandas memakainya sebagai engine Excel. Kalau belum ada, Anda akan dapat ImportError.
Kirim ke Google Sheets (Opsional)
Untuk workflow otomatis, Anda mungkin mau kirim data langsung ke Google Sheets pakai library gspread. Ini butuh setup Google Cloud service account sekali saja:
import gspread
gc = gspread.service_account(filename="service_account.json")
sh = gc.open("HN Daily Digest")
worksheet = sh.sheet1
# Ubah stories menjadi baris
header = list(stories[0].keys())
rows = [list(s.values()) for s in stories]
worksheet.clear()
worksheet.update([header] + rows)
print("Pushed to Google Sheets")
Alternatif Ekspor Tanpa Kode
Kalau menyiapkan service account dan nulis kode ekspor terasa lebih ribet daripada scraping-nya sendiri, saya paham. Di Thunderbit, kami bikin ekspor data gratis yang memungkinkan Anda kirim hasil scrape langsung ke Excel, Google Sheets, Airtable, atau Notion — tanpa kode, tanpa kredensial, tanpa pipeline yang harus dirawat. Untuk pengambilan data sekali jalan, cara ini memang jauh lebih cepat. Saya bahas lebih lanjut di bawah.
Membuat Scraper Siap Produksi: Error Handling, Caching, dan Penjadwalan
Kalau Anda menjalankan scraper sekali buat iseng, kode di atas sudah cukup. Tapi kalau Anda menjalankannya setiap hari sebagai bagian dari workflow, Anda butuh beberapa komponen tambahan.
Error Handling dan Retry Logic
Jaringan bisa gagal. Server bisa membatasi akses. Satu request buruk seharusnya tidak bikin seluruh proses scrape berhenti. Berikut fungsi retry dengan exponential backoff:
from tenacity import retry, stop_after_attempt, wait_exponential_jitter
import requests
@retry(stop=stop_after_attempt(5), wait=wait_exponential_jitter(initial=1, max=60))
def fetch_with_retry(url):
"""Ambil URL dengan retry otomatis dan exponential backoff."""
response = requests.get(url, timeout=10)
response.raise_for_status()
return response
# Penggunaan:
try:
resp = fetch_with_retry("https://hacker-news.firebaseio.com/v0/topstories.json")
story_ids = resp.json()
except Exception as e:
print(f"Failed after retries: {e}")
Library tenacity menangani logika retry dengan rapi. Ia akan coba lagi sampai 5 kali dengan exponential backoff yang diberi jitter — mulai dari 1 detik sampai maksimal 60 detik. Ini cukup bagus untuk HTTP 429 (rate limited), 503 (service unavailable), dan error jaringan sementara.
Cache Respons untuk Menghindari Crawl Ulang
Selama pengembangan, Anda mungkin akan menjalankan scraper berkali-kali sambil menyempurnakan logika parsing. Tanpa cache, tiap run akan kembali membebani server HN dengan data yang sama. Library requests-cache bisa memperbaikinya hanya dalam dua baris:
import requests_cache
requests_cache.install_cache("hn_cache", expire_after=3600) # Cache selama 1 jam
Setelah baris itu ditaruh di bagian atas skrip, semua panggilan requests.get() otomatis disimpan dalam database SQLite lokal. Jalankan skrip 10 kali dalam satu jam, dan cuma run pertama yang benar-benar menyentuh jaringan. Ini tool yang sering direkomendasikan pengguna forum, dan alasannya jelas.
Memisahkan Proses Crawling dari Parsing
Pola yang sangat disukai scraper berpengalaman: unduh data mentah dulu, parse belakangan. Jadi kalau logika parsing Anda salah, Anda tinggal perbaiki dan parse ulang tanpa harus ambil data lagi.
import os, json
def crawl_and_save(story_ids, output_dir="raw_data"):
"""Ambil data cerita dan simpan JSON mentah ke disk."""
os.makedirs(output_dir, exist_ok=True)
for sid in story_ids:
filepath = os.path.join(output_dir, f"{sid}.json")
if os.path.exists(filepath):
continue # Lewati item yang sudah pernah diambil
resp = fetch_with_retry(f"{API_BASE}/item/{sid}.json")
with open(filepath, "w") as f:
json.dump(resp.json(), f)
def parse_saved_data(input_dir="raw_data"):
"""Parse file JSON yang disimpan menjadi daftar cerita terstruktur."""
stories = []
for filename in os.listdir(input_dir):
with open(os.path.join(input_dir, filename)) as f:
item = json.load(f)
if item and item.get("type") == "story":
stories.append({
"title": item.get("title", ""),
"url": item.get("url", ""),
"score": item.get("score", 0),
"author": item.get("by", ""),
"comments": item.get("descendants", 0),
})
return stories
Pendekatan dua tahap ini sangat berguna saat Anda memproses ratusan item dan ingin cepat eksperimen dengan cara pengolahan datanya.
Mengotomatisasi Scraper dengan Jadwal
Untuk daily digest HN, scraper Anda harus jalan otomatis. Ada dua opsi umum:
Opsi 1: cron (Linux/Mac)
# Jalankan setiap hari pukul 8:30 UTC
30 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1
Opsi 2: GitHub Actions (gratis, tanpa server)
name: Scrape Hacker News
on:
schedule:
- cron: '30 8 * * *' # Setiap hari pukul 8:30 UTC
workflow_dispatch: # Tombol trigger manual
jobs:
scrape:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- run: pip install requests beautifulsoup4 pandas openpyxl
- run: python scrape_hn.py
- run: |
git config user.name "GitHub Actions Bot"
git config user.email "actions@github.com"
git add -A
git diff --staged --quiet || git commit -m "Update HN data $(date -u +%Y-%m-%dT%H:%M:%SZ)"
git push
Ada beberapa hal yang perlu diperhatikan saat pakai jadwal GitHub Actions: semua waktu cron memakai UTC, delay 15–60 menit cukup umum terjadi (pakai waktu seperti :30 alih-alih :00), dan GitHub bisa menonaktifkan workflow terjadwal pada repo yang tidak aktif selama 60 hari. Selalu sertakan workflow_dispatch supaya Anda bisa menjalankannya manual saat testing.
Kalau Anda mau opsi yang lebih simpel, fitur Scheduled Scraper di Thunderbit memungkinkan Anda mendeskripsikan jadwal dengan bahasa biasa — misalnya "scrape setiap pagi jam 8" — tanpa perlu server atau setup cron.
Saat Python Terlalu Berlebihan: Cara Tanpa Kode untuk Scrape Hacker News
Saya jujur saja, meskipun saya suka Python dan tim saya membangun tools untuk developer. Kalau Anda cuma butuh 100 cerita teratas HN hari ini dalam spreadsheet — sekarang juga, sekali saja — menulis, debugging, dan menjalankan skrip Python itu sebenarnya kebangetan. Setup-nya saja (virtual environment, instal paket, menentukan selector) sering makan waktu lebih lama daripada proses ambil datanya sendiri.
Di sinilah Thunderbit sangat berguna. Alurnya seperti ini:
- Buka
news.ycombinator.comdi Chrome - Klik ikon ekstensi Thunderbit, lalu pilih "AI Suggest Fields"
- AI membaca halaman dan mengusulkan kolom: Title, URL, Score, Author, Comment Count, Time Posted
- Sesuaikan field kalau perlu (ganti nama, hapus, atau tambah field kustom — Anda bahkan bisa menambah prompt AI seperti "Categorize as AI/DevTools/Web/Other")
- Klik "Scrape" — data muncul dalam tabel terstruktur
- Ekspor ke Excel, Google Sheets, Airtable, atau Notion
Dua klik untuk dapat data terstruktur. Tanpa selector, tanpa kode, tanpa maintenance.
Salah satu keunggulan besar di sini: AI Thunderbit beradaptasi otomatis terhadap perubahan layout. Scraper tradisional berbasis selector CSS sering rusak saat situs mengubah markup — dan meskipun HTML HN cukup stabil, situs ini memang pernah berubah (class="athing submission" diperbarui, span.titleline menggantikan a.storylink yang lama). Scraper berbasis AI membaca halaman dari awal setiap kali, jadi tidak peduli kalau nama class berubah.

Thunderbit juga menangani pagination secara otomatis (mengklik tombol "More" HN) dan scraping subpage (mengunjungi halaman komentar tiap cerita untuk ambil data diskusi). Untuk use case comment enrichment, itu setara dengan kode API rekursif pada Metode 2 — tapi tanpa nulis satu baris pun.
Trade-off-nya cukup jelas: Python adalah pilihan tepat saat Anda butuh logika kustom, transformasi data yang kompleks, pipeline automasi terjadwal, atau sedang belajar coding. Thunderbit lebih cocok saat Anda butuh data cepat, tidak ingin merawat kode, atau bukan developer. Pilih alat yang paling sesuai dengan kebutuhan Anda.
Python vs. API vs. Tanpa Kode: Metode Mana yang Sebaiknya Dipilih?
Berikut kerangka keputusan lengkapnya:
| Kriteria | BeautifulSoup (HTML) | Firebase API | Algolia API | Thunderbit (Tanpa Kode) |
|---|---|---|---|---|
| Skill teknis yang dibutuhkan | Python menengah | Python pemula | Python pemula | Tidak ada |
| Waktu setup | 10–15 menit | 5–10 menit | 5–10 menit | 2 menit |
| Beban maintenance | Sedang (selector bisa rusak) | Rendah (JSON stabil) | Rendah (JSON stabil) | Tidak ada |
| Kedalaman data | Hanya halaman depan | Item apa pun, user | Pencarian + historis | Halaman depan + subpage |
| Komentar | Sulit | Mudah (rekursif) | Mudah (tree bertingkat) | Scraping subpage |
| Data historis | Tidak | Tidak | Ya (arsip lengkap) | Tidak |
| Opsi ekspor | Buat sendiri dengan kode | Buat sendiri dengan kode | Buat sendiri dengan kode | Bawaan (Excel, Sheets, dll.) |
| Penjadwalan | cron / GitHub Actions | cron / GitHub Actions | cron / GitHub Actions | Scheduler bawaan |
| Paling cocok untuk | Belajar scraping | Pipeline andal | Riset & analisis | Pengambilan data cepat |
Kalau Anda lagi belajar Python atau membangun sesuatu yang kustom, pilih Metode 1 atau 2. Kalau Anda butuh analisis historis, tambahkan Algolia API. Kalau Anda cuma mau datanya tanpa kode, coba Thunderbit.
Coba Thunderbit untuk Scraping Hacker News
Kesimpulan dan Poin Penting
Sekarang Anda punya beberapa alat di kotak perkakas Anda:
- Dua metode Python lengkap untuk scrape Hacker News — BeautifulSoup untuk parsing HTML dan Firebase API untuk data JSON yang bersih
- Teknik pagination untuk scraping di luar halaman 1, termasuk Algolia API untuk data historis sampai 2007
- Kode ekspor ke CSV, Excel, dan Google Sheets — karena data di terminal tidak berguna buat orang lain di tim Anda
- Pola siap produksi — retry logic, caching, pemisahan crawl/parse, dan automasi terjadwal via cron atau GitHub Actions
- Alternatif tanpa kode saat Python terasa lebih besar daripada kebutuhan Anda
Rekomendasi saya: mulai dari Firebase API (Metode 2) untuk sebagian besar use case. Lebih bersih, lebih andal, dan memberi akses komentar tanpa repot parsing HTML bertingkat. Tambahkan Algolia API saat Anda butuh data historis. Dan simpan Thunderbit di bookmark untuk saat Anda cuma perlu spreadsheet cepat tanpa ingin bikin proyek Python penuh.
Kalau mau lebih dalam, coba scrape komentar HN untuk analisis sentimen, bangun pipeline ringkasan harian dengan GitHub Actions, atau jelajahi Algolia API untuk melacak bagaimana tren teknologi berubah selama satu dekade terakhir.
Coba Thunderbit untuk Scraping Hacker News yang Cepat Get Started Free
FAQ
Apakah legal scrape Hacker News?
Data HN tersedia untuk umum, dan Y Combinator menyediakan API resmi khusus untuk akses programatik. robots.txt situs ini mengizinkan scraping konten baca-saja (halaman depan, halaman item, halaman user), tetapi meminta jeda crawl 30 detik. Patuhi jedanya, jangan scrape endpoint interaktif (voting, login), dan Anda berada di jalur yang aman. Untuk pembahasan lebih lanjut tentang etika scraping, lihat panduan implikasi hukum web scraping kami.
Apakah Hacker News punya API resmi?
Ya. HN Firebase API di hacker-news.firebaseio.com/v0/ gratis, tidak memerlukan autentikasi, dan menyediakan akses ke cerita, komentar, profil pengguna, serta semua tipe feed (top, new, best, ask, show, jobs). API ini mengembalikan JSON yang bersih dan tidak punya rate limit yang dinyatakan secara eksplisit, meski tetap disarankan untuk menjaga frekuensi request agar sopan.
Bagaimana cara scrape komentar Hacker News dengan Python?
Dengan Firebase API, ambil item cerita untuk mendapatkan field kids (array ID komentar level atas). Setiap komentar juga merupakan item yang punya field kids sendiri untuk balasan. Telusuri pohonnya secara rekursif dengan fungsi yang mengambil setiap komentar beserta anak-anaknya. Lihat bagian "Scrape Komentar (Tree Walk Rekursif)" di atas untuk kode lengkap. Sebagai alternatif, endpoint Algolia API /items/<id> mengembalikan seluruh tree komentar bertingkat dalam satu request — jauh lebih cepat untuk cerita dengan komentar banyak.
Bisakah saya scrape Hacker News tanpa menulis kode?
Bisa. AI web scraper Thunderbit bekerja sebagai ekstensi Chrome — buka HN, klik "AI Suggest Fields," dan sistem akan otomatis mengenali kolom seperti title, URL, score, dan author. Klik "Scrape" lalu ekspor langsung ke Excel, Google Sheets, Airtable, atau Notion. Tool ini menangani pagination dan bahkan bisa mengunjungi subpage untuk mengambil data komentar. Tanpa Python, tanpa selector, tanpa maintenance.
Bagaimana cara mendapatkan data historis Hacker News?
HN Algolia Search API adalah tool terbaik untuk ini. Gunakan endpoint search_by_date dengan numericFilters=created_at_i>TIMESTAMP untuk memfilter berdasarkan rentang tanggal. Anda bisa mencari berdasarkan keyword, memfilter berdasarkan tipe cerita, dan melakukan pagination hingga 500 halaman hasil. Untuk analisis historis skala besar, dataset publik juga tersedia di Google BigQuery (arsip lengkap), ClickHouse (28 juta record), dan Hugging Face (4 juta cerita).
Pelajari Lebih Lanjut


