Hoe ik Hacker News scrape met Python (2 methoden, volledige code)

Laatst bijgewerkt op April 16, 2026
Hoe ik Hacker News scrape met Python (2 methoden, volledige code)

Een paar maanden geleden wilde ik voor ons team bij Thunderbit een dagelijkse samenvatting maken van de belangrijkste Hacker News-artikelen. Mijn eerste ingeving was gewoon de site bookmarken en er elke ochtend doorheen scrollen. Dat hield ik ongeveer drie dagen vol, totdat ik doorhad dat ik elke dag 20 minuten kwijt was aan alleen maar koppen lezen en links kopiëren naar een spreadsheet.

Hacker News is een van de rijkste en meest geconcentreerde bronnen van tech-informatie op het internet — ongeveer 13 miljoen bezoeken per maand, zo’n 1.300 nieuwe verhalen per dag en dagelijks rond de 13.000 reacties. Of je nu opkomende techtrends volgt, je merk monitort, een recruitmentpipeline bouwt uit "Who's Hiring"-threads, of gewoon wilt bijhouden waar de developerwereld zich druk om maakt: alles handmatig bijhouden is een verloren strijd.

Het goede nieuws: Hacker News scrapen met Python is verrassend eenvoudig. In deze gids laat ik je twee complete methoden zien — HTML-scraping met BeautifulSoup en de officiële HN Firebase API — inclusief paginering, dataverwerking, productieklare patronen en een no-code alternatief voor momenten waarop Python gewoon te veel gedoe is.

Waarom Hacker News scrapen met Python?

Hacker News is niet zomaar een linkaggregator. Het is een zorgvuldig samengesteld, community-gedreven platform waar de interessantste techverhalen naar boven komen via upvotes en actieve discussies. Het publiek bestaat vooral uit technologieprofessionals (ongeveer 76% mannen, hoofdzakelijk 25–34 jaar), en het directe verkeer van 66% laat zien dat dit een loyale, vaste lezersgroep is — geen toevallige bezoekers.

Daarom scrapen mensen HN-data:

GebruiksscenarioWat je krijgt
Dagelijkse techdigestTopverhalen, scores en links direct in je inbox of Slack
Merk- en concurrentiemonitoringMeldingen zodra je bedrijf of product genoemd wordt
TrendanalyseVolg welke technologieën, talen of onderwerpen aan populariteit winnen
RecruitmentHaal vacatures, tech stacks en salaris-signalen uit "Who's Hiring"-threads
ContentonderzoekVind onderwerpen met veel engagement om over te schrijven of te delen
SentimentanalysePeil de mening van de community over producten, lanceringen of verschuivingen in de sector

Bedrijven met een gezamenlijke waarde van meer dan 400 miljard dollar — Stripe, Dropbox en Airbnb — schrijven hun vroege feedback en gebruikers deels toe aan Hacker News. Drew Houston plaatste in april 2007 de Dropbox-demo op HN, die werd nummer 1, waarna de beta-wachtlijst in één dag explodeerde van 5.000 naar 75.000 gebruikers. HN-data is dus niet alleen interessant, maar ook commercieel waardevol.

De data is publiek beschikbaar, maar de structuur van de site maakt handmatige verzameling omslachtig. Automatisering met Python is hier de praktische oplossing.

Twee manieren om Hacker News met Python te scrapen: overzicht

Deze gids behandelt twee volledige, direct uitvoerbare aanpakken:

  1. HTML-scraping met requests + BeautifulSoup — haal de ruwe HTML van news.ycombinator.com op en parse die om storydata te extraheren. Ideaal om de basis van scraping te leren en precies te pakken wat op de pagina staat.
  2. De officiële Hacker News Firebase API — vraag JSON-endpoints direct op, zonder HTML-parsing. Beter voor betrouwbare datapijplijnen, toegang tot reacties en historische data.

Hier is een vergelijking naast elkaar om te bepalen wat het beste past bij jouw situatie:

CriteriaHTML-scraping (requests + BS4)HN Firebase APIThunderbit (no-code)
Complexiteit van de setupGemiddeld (HTML-selectors parsen)Laag (JSON-endpoints)Geen (2-klik Chrome-extensie)
Actualiteit van de dataRealtime frontpageRealtime (elk item op ID)Realtime
Risico op rate limitsGemiddeld (robots.txt zegt 30s crawl-delay)Laag (officieel, ruimhartig)Beheerd door Thunderbit
Toegang tot reactiesMoeilijk (geneste HTML)Makkelijk (recursieve item-IDs)Subpagina-scrapingfunctie
Historische dataBeperktVia Algolia Search APIN.v.t.
Het meest geschikt voorScraping-basis lerenBetrouwbare datapijplijnenNiet-developers, snelle exports

Beide methoden bevatten volledige, uitvoerbare Python-code. En als je alleen de data wilt zonder ook maar één regel code te schrijven, behandel ik dat ook.

Voordat je begint

  • Moeilijkheidsgraad: Beginner tot gemiddeld
  • Benodigde tijd: ongeveer 15–20 minuten per methode
  • Wat je nodig hebt:
    • Python 3.11+ geïnstalleerd
    • Een terminal of code-editor
    • Chrome-browser (als je HN’s HTML wilt inspecteren of de no-code optie wilt proberen)
    • Thunderbit Chrome Extension (optioneel, voor de no-code methode)

scrape-hacker-news-methods.webp

Je Python-omgeving instellen

Voordat we ook maar iets met HN-data doen, zetten we eerst de omgeving klaar. Ik raad aan een virtuele omgeving te gebruiken, zodat je projectafhankelijkheden netjes gescheiden blijven.

# Maak een virtuele omgeving aan en activeer die
python3 -m venv hn-scraper
# macOS/Linux:
source hn-scraper/bin/activate
# Windows:
hn-scraper\Scripts\activate

# Installeer de pakketten die we voor beide methoden nodig hebben
pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5

Voor productiepatronen later (caching, retries) heb je ook dit nodig:

pip install requests-cache==1.3.1 tenacity==9.1.4

Geen speciale API-sleutels, geen authenticatietokens. HN-data is open toegankelijk.

Methode 1: Hacker News scrapen met Python via BeautifulSoup

Dit is de klassieke aanpak — HTML ophalen, parsen en de gewenste data eruit halen. Zo leren de meeste mensen webscraping, en de eenvoudige tabelstructuur van HN maakt het een uitstekende oefenomgeving.

Stap 1: Haal de Hacker News-frontpage op

Open je editor en maak een bestand aan met de naam scrape_hn_bs4.py. Dit is de startcode:

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}, Paginagrootte: {len(response.text)} tekens")

Voer het uit. Je zou Status: 200 moeten zien en een paginalengte van ongeveer 40.000–50.000 tekens. Dat is de ruwe HTML van de HN-frontpage, netjes in het geheugen en klaar om te parsen.

Stap 2: Begrijp de HTML-structuur

HN gebruikt een tabelgebaseerde lay-out — geen moderne CSS-grid of flexbox. Elke story op de pagina bestaat uit twee belangrijke <tr>-rijen:

  • De story-rij (<tr class="athing submission">): bevat de rang, titel en link
  • De metadata-rij (de volgende <tr>): bevat punten, auteur, tijd en aantal reacties

De belangrijkste selectors:

  • span.titleline > a — de titel en URL van het verhaal
  • span.score — het aantal punten (bijv. "118 points")
  • a.hnuser — de gebruikersnaam van de auteur
  • span.age — het moment van plaatsen
  • De laatste <a> in .subtext met "comment" in de tekst — het aantal reacties

Als je in Chrome op een storytitel rechtsklikt en kiest voor "Inspecteren", zie je iets als dit:

<span class="titleline">
  <a href="https://darkbloom.dev">Darkbloom – Private inference on idle Macs</a>
</span>

En daaronder de metadata-rij:

<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&nbsp;comments</a>

Het begrijpen van deze selectors is cruciaal — als HN ooit de markup wijzigt, moet je ze aanpassen. (Spoiler: de API-methode omzeilt dat probleem volledig.)

Stap 3: Titel, links en scores extraheren

Nu het echte werk. We lopen langs elke story-rij, halen titel en link uit de story-rij en pakken vervolgens de score uit de metadata-rij er direct onder.

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:
    # Titel en URL uit de story-rij
    title_tag = row.select_one("span.titleline > a")
    if not title_tag:
        continue
    title = title_tag.get_text()
    link = title_tag.get("href", "")

    # Metadata uit de volgende sibling-rij
    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()
        # Aantal reacties: laatste <a> met "comment" in de tekst
        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 op stories met 50+ punten, gesorteerd op score
top_stories = sorted(
    [s for s in stories if s["score"] >= 50],
    key=lambda x: x["score"],
    reverse=True,
)

pprint(top_stories[:10])

Een paar opmerkingen bij de code:

  • De walrusoperator (:=) werkt in Python 3.8+. Hiermee kun je tegelijk toewijzen en controleren — handig voor optionele elementen zoals span.score, die niet in elke rij voorkomt (bijv. jobposts hebben geen score).
  • HN gebruikt \xa0 (non-breaking space) tussen het getal en "comments", dus we splitsen daarop.
  • Stories die naar andere HN-pagina’s linken (zoals "Ask HN") hebben relatieve URL’s die beginnen met item?id=. Je kunt daar eventueel https://news.ycombinator.com/ voor zetten.

Stap 4: Uitvoeren en resultaat bekijken

Opslaan en uitvoeren:

python scrape_hn_bs4.py

Je zou uitvoer moeten zien zoals:

[{'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/...'},
 ...]

Dat zijn 30 verhalen van pagina 1. Maar HN heeft op elk moment honderden actieve verhalen. Paginering behandelen we later in een apart deel.

Methode 2: Hacker News scrapen met Python via de officiële API

De HN Firebase API is de officieel goedgekeurde manier om Hacker News-data op te vragen. Geen authenticatie, geen API-sleutels, geen HTML-parsing. Je krijgt schone JSON-responses. Ik gebruik deze methode voor alles wat betrouwbaar in productie moet draaien.

Belangrijke API-endpoints die je moet kennen

De basis-URL is https://hacker-news.firebaseio.com/v0/. Dit zijn de relevante endpoints:

EndpointGeeft terugVoorbeeld
/v0/topstories.jsonArray met maximaal 500 topstory-ID’s[47788542, 47787901, ...]
/v0/newstories.jsonTot 500 nieuwste story-ID’sZelfde formaat
/v0/beststories.jsonTot 500 beste story-ID’sZelfde formaat
/v0/askstories.jsonTot 200 "Ask HN" story-ID’sZelfde formaat
/v0/showstories.jsonTot 200 "Show HN" story-ID’sZelfde formaat
/v0/jobstories.jsonTot 200 job story-ID’sZelfde formaat
/v0/item/{id}.jsonVolledige details van elk item (story, reactie, poll)JSON-object
/v0/user/{username}.jsonGebruikersprofielJSON-object
/v0/maxitem.jsonHuidige hoogste item-IDGeheel getal (bijv. 47789427)

Een story-item ziet er zo uit:

{
  "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"
}

Het kids-veld bevat de ID’s van directe onderliggende reacties. Elke reactie is zelf ook een item en kan opnieuw eigen kids hebben — zo is de reactiethread opgebouwd.

Stap 1: Haal de topstory-ID’s op

Maak een bestand aan met de naam scrape_hn_api.py:

import requests
import time
from pprint import pprint

API_BASE = "https://hacker-news.firebaseio.com/v0"

# Haal de ID's van de topstories op
response = requests.get(f"{API_BASE}/topstories.json")
story_ids = response.json()

print(f"Ontvangen: {len(story_ids)} topstory-ID's")
# Uitvoer: Ontvangen: 500 topstory-ID's

500 story-ID’s in één request — geen parsing, geen selectors, gewoon een JSON-array.

Stap 2: Storydetails per ID ophalen

Nu hebben we de daadwerkelijke storydata nodig. Hier zie je direct het fan-outprobleem: 500 stories betekent 500 losse API-calls. In mijn benchmark duurt elk item-verzoek sequentieel ongeveer 1,2 seconden. Voor 500 stories kom je dan uit op ongeveer 10 minuten.

Voor de meeste use-cases heb je niet alle 500 nodig. Hier is code om de top 30 op te halen:

def fetch_story(story_id):
    """Haalt de details van één story op via de HN API."""
    resp = requests.get(f"{API_BASE}/item/{story_id}.json")
    return resp.json()

# Haal de details van de top 30 stories op
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)  # Wees beleefd — kleine pauze tussen verzoeken

# Sorteer op score, toon de top 10
top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
pprint(top)

De time.sleep(0.1) voegt een kleine pauze uit beleefdheid toe. De Firebase API heeft geen expliciete rate limit, maar elke API zonder pauzes te hard aanspreken is geen goede praktijk.

Stap 3: Reacties scrapen (recursieve boomdoorloop)

Hier schittert de API echt in vergelijking met HTML-scraping. Reacties op HN zijn diep genest — antwoorden op antwoorden op antwoorden. In HTML betekent dat complexe, geneste tabelstructuren parsen. Met de API geeft elk reactie-item via het kids-veld de ID’s van zijn kinderen, en kun je de boom gewoon recursief doorlopen.

def fetch_comments(item_id, depth=0, max_depth=3):
    """Haal reacties recursief op tot 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

# Voorbeeld: reacties ophalen voor de topstory
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"\nReacties voor: {top_story['title']}")
        all_comments = []
        for kid_id in top_story_full["kids"][:5]:  # Eerste 5 reacties op hoofdniveau
            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 "[geen tekst]"
            print(f"{indent}[{c['author']}] {preview}...")

Deze recursieve aanpak is veel eenvoudiger dan proberen geneste HTML-reactiethreads te parsen. Als je volledige reactiebomen nodig hebt, is de API de beste keuze.

Stap 4: Uitvoeren en resultaat bekijken

python scrape_hn_api.py

Je ziet gestructureerde storydata gevolgd door een geneste reactie-preview. De data is schoner, toegang tot reacties is eenvoudig en er is geen risico dat je scraper breekt omdat HN een CSS-klassennaam wijzigt.

Verder dan pagina 1: paginering en historische data

De meeste HN-scrapingtutorials blijven steken bij pagina 1 — 30 stories. Prima voor een snelle demo, maar in echte use-cases heb je vaak meer diepte nodig.

Meerdere pagina’s scrapen met BeautifulSoup

HN gebruikt een eenvoudig URL-patroon voor paginering: ?p=2, ?p=3, enzovoort. Elke pagina bevat 30 stories, en de site levert tot ongeveer pagina 20 (in totaal zo’n 600 stories). Daarna krijg je lege pagina’s.

import time

def scrape_hn_pages(num_pages=5):
    """Scrape meerdere pagina’s van de HN-frontpage."""
    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"Pagina {page}: geen stories gevonden, stoppen.")
            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"Pagina {page}: {len(story_rows)} stories gescrapet")

        # Respecteer de robots.txt crawl-delay van 30 seconden
        if page < num_pages:
            time.sleep(30)

    return all_stories

stories = scrape_hn_pages(5)
print(f"\nTotaal aantal gescrapete stories: {len(stories)}")

Die time.sleep(30) is belangrijk. HN’s robots.txt vraagt expliciet om een crawl-delay van 30 seconden. Negeer je dat, dan kun je rate-limiting (HTTP 429) of een tijdelijke blokkade krijgen. Vijf pagina’s met 30 seconden tussenpoos duurt ongeveer 2,5 minuut — niet instant, maar wel netjes.

Voor gebruikers die geen pagineringscode willen beheren, regelt Thunderbit click-based en infinite-scroll-paginering automatisch. Het klikt zonder configuratie op de "More"-knop onderaan HN-pagina’s.

Scrape Hacker News-pagina’s met AI

Historische Hacker News-data via de Algolia API

De Firebase API geeft je actuele data. Voor historische analyse — "Wat waren de beste Python-verhalen in 2023?" of "Hoe is de AI-aandacht de afgelopen 5 jaar veranderd?" — heb je de HN Algolia Search API nodig.

import requests

ALGOLIA_BASE = "https://hn.algolia.com/api/v1"

def search_hn(query, tags="story", page=0, hits_per_page=20):
    """Zoek op HN via de 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()

# Voorbeeld: vind Python-scrapingstories met 10+ punten sinds jan 2024
results = search_hn(
    query="python scraping",
    tags="story",
)
print(f"Totaal gevonden resultaten: {results['nbHits']}")

for hit in results["hits"][:5]:
    print(f"  [{hit.get('points', 0)} pts] {hit['title']}")

Gebruik voor datumfilters numericFilters:

import calendar, datetime

# Stories sinds 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"Gevonden: {data['nbHits']} stories over Python web scraping sinds 2024 met >10 punten")

De Algolia API is snel (5–9 ms serververwerkingstijd), vereist geen API-sleutel en ondersteunt paginering tot 500 pagina’s. Voor grootschalige historische analyse is dit de beste optie.

Gescrapete Hacker News-data exporteren naar CSV, Excel en Google Sheets

Elke HN-scrapinggids eindigt die ik heb gezien met pprint()-output in de terminal. Prima voor debugging, maar als je een dagelijkse digest bouwt of trendanalyse doet, wil je de data in een bestand. Zo zet je dat op.

Exporteren naar CSV met Python

import csv

def export_to_csv(stories, filename="hn_stories.csv"):
    """Sla gescrapete stories op in een CSV-bestand."""
    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"Opgeslagen: {len(stories)} stories naar {filename}")

export_to_csv(stories)

Exporteren naar Excel met Python

import pandas as pd

def export_to_excel(stories, filename="hn_stories.xlsx"):
    """Sla gescrapete stories op in een Excel-bestand."""
    df = pd.DataFrame(stories)
    df.to_excel(filename, index=False, engine="openpyxl")
    print(f"Opgeslagen: {len(stories)} stories naar {filename}")

export_to_excel(stories)

Zorg dat openpyxl geïnstalleerd is — pandas gebruikt dit als Excel-engine. Als het ontbreekt, krijg je een ImportError.

Naar Google Sheets pushen (optioneel)

Voor geautomatiseerde workflows wil je data misschien direct naar Google Sheets sturen met de gspread-bibliotheek. Daarvoor moet je een Google Cloud service account instellen (een eenmalige handeling):

import gspread

gc = gspread.service_account(filename="service_account.json")
sh = gc.open("HN Daily Digest")
worksheet = sh.sheet1

# Zet stories om naar rijen
header = list(stories[0].keys())
rows = [list(s.values()) for s in stories]

worksheet.clear()
worksheet.update([header] + rows)
print("Verstuurd naar Google Sheets")

Het no-code alternatief voor export

Als het instellen van service accounts en het schrijven van exportcode meer werk lijkt dan het eigenlijke scrapen, begrijp ik dat. Bij Thunderbit hebben we gratis data-export gebouwd waarmee je gescrapete data rechtstreeks naar Excel, Google Sheets, Airtable of Notion kunt sturen — zonder code, zonder inloggegevens, zonder te onderhouden pipeline. Voor een eenmalige datamarkt is het oprecht sneller. Meer daarover hieronder.

Je scraper productieklare maken: foutafhandeling, caching en planning

Als je een scraper één keer voor de lol draait, is de code hierboven prima. Als je hem dagelijks als onderdeel van een workflow laat draaien, heb je nog een paar extra onderdelen nodig.

Foutafhandeling en retry-logica

Netwerken vallen uit. Servers begrenzen verkeer. Eén foute request mag je hele scrape niet laten crashen. Hier is een retryfunctie met exponentiële 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):
    """Haal een URL op met automatische retries en exponentiële backoff."""
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    return response

# Gebruik:
try:
    resp = fetch_with_retry("https://hacker-news.firebaseio.com/v0/topstories.json")
    story_ids = resp.json()
except Exception as e:
    print(f"Mislukt na retries: {e}")

De tenacity-bibliotheek regelt de retry-logica netjes. Hij probeert maximaal 5 keer opnieuw met jittered exponentiële backoff — startend op 1 seconde en oplopend tot maximaal 60 seconden. Dit vangt HTTP 429 (rate limited), 503 (service unavailable) en tijdelijke netwerkfouten netjes af.

Responses cachen om opnieuw crawlen te voorkomen

Tijdens ontwikkeling draai je je scraper vaak opnieuw terwijl je de parsinglogica aanpast. Zonder caching raak je bij elke run opnieuw HN’s servers voor dezelfde data. De requests-cache-bibliotheek lost dat in twee regels op:

import requests_cache

requests_cache.install_cache("hn_cache", expire_after=3600)  # Cache 1 uur

Zodra je die regels bovenaan je script zet, worden alle requests.get()-aanroepen automatisch gecachet in een lokale SQLite-database. Draai je script tien keer binnen een uur, dan raakt alleen de eerste run echt het netwerk. Dit is een tool die forumgebruikers vaak aanraden, en terecht.

Crawlen scheiden van parsen

Een patroon waar ervaren scrapers bij zweren: download eerst de ruwe data, parse daarna pas. Als je parsinglogica een bug heeft, kun je die fixen en opnieuw parsen zonder opnieuw te hoeven ophalen.

import os, json

def crawl_and_save(story_ids, output_dir="raw_data"):
    """Haal storydata op en sla ruwe JSON op schijf op."""
    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  # Sla al opgehaalde items over
        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 opgeslagen JSON-bestanden naar een gestructureerde lijst met stories."""
    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

Deze tweefasenaanpak is vooral handig als je honderden items scrapt en snel wilt itereren op hoe je de data verwerkt.

Je scraper automatisch plannen

Voor een dagelijkse HN-digest moet je scraper automatisch draaien. Twee veelgebruikte opties:

Optie 1: cron (Linux/Mac)

# Draai elke dag om 8:30 UTC
30 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1

Optie 2: GitHub Actions (gratis, geen server nodig)

name: Scrape Hacker News

on:
  schedule:
    - cron: '30 8 * * *'  # Dagelijks om 8:30 UTC
  workflow_dispatch:        # Handmatige triggerknop

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

Een paar aandachtspunten bij GitHub Actions-scheduling: alle cron-tijden zijn in UTC, vertragingen van 15–60 minuten komen vaak voor (gebruik dus liever tijden op het halve uur zoals :30 dan :00), en GitHub kan geplande workflows uitschakelen op repos zonder activiteit gedurende 60 dagen. Voeg altijd workflow_dispatch toe zodat je handmatig kunt triggeren voor tests.

Voor een simpelere optie laat Thunderbit’s Scheduled Scraper-feature je het schema in gewone taal beschrijven — iets als "scrape elke ochtend om 8 uur" — zonder server of cron-opzet.

Wanneer Python overdreven is: de no-code manier om Hacker News te scrapen

Ik ga hier eerlijk in zijn, ook al ben ik een Python-liefhebber en bouwt mijn team ontwikkeltools. Als je alleen de top 100 HN-stories van vandaag in een spreadsheet nodig hebt — meteen, eenmalig — dan is het schrijven, debuggen en draaien van een Python-script onnodige overhead. Alleen al de setup (virtuele omgeving, pakketten installeren, selectors uitzoeken) kost meer tijd dan het daadwerkelijke verzamelen van de data.

Daar komt Thunderbit om de hoek kijken. Dit is de workflow:

  1. Open news.ycombinator.com in Chrome
  2. Klik op het Thunderbit-extensie-icoon en daarna op "AI Suggest Fields"
  3. De AI leest de pagina en stelt kolommen voor: Titel, URL, Score, Auteur, Aantal reacties, Gepubliceerd op
  4. Pas de velden aan als je wilt (hernoemen, verwijderen of eigen velden toevoegen — je kunt zelfs een AI-prompt toevoegen zoals "Categoriseer als AI/DevTools/Web/Overig")
  5. Klik op "Scrape" — de data verschijnt in een gestructureerde tabel
  6. Exporteer naar Excel, Google Sheets, Airtable of Notion

Twee klikken naar gestructureerde data. Geen selectors, geen code, geen onderhoud.

Een echt voordeel hier: Thunderbit’s AI past zich automatisch aan lay-outwijzigingen aan. Traditionele scrapers met CSS-selectors breken wanneer een site zijn markup wijzigt — en hoewel HN’s HTML vrij stabiel is, is die wel veranderd (de class athing submission werd aangepast, span.titleline verving de oudere a.storylink). Een AI-gestuurde scraper leest de pagina telkens opnieuw, dus het maakt niets uit als classnamen veranderen.

python-vs-thunderbit-comparison.webp

Thunderbit regelt ook paginering (door automatisch op HN’s "More"-knop te klikken) en subpaginacrawls (door elke storycommentpagina te bezoeken om discussiedata op te halen). Voor het gebruiksscenario van commentverrijking is dat het equivalent van de recursieve API-code in Methode 2 — maar dan zonder ook maar één regel te schrijven.

De afweging is eenvoudig: Python is de juiste keuze als je aangepaste logica, complexe datatransformaties, geplande automatiseringspijplijnen of leerdoeleinden hebt. Thunderbit is de juiste keuze als je snel data nodig hebt, geen code wilt onderhouden of geen developer bent. Kies het hulpmiddel dat bij jouw situatie past.

Python vs. API vs. no-code: welke methode moet je kiezen?

Hier is het volledige besliskader:

CriteriaBeautifulSoup (HTML)Firebase APIAlgolia APIThunderbit (no-code)
Benodigde technische kennisGemiddeld PythonBeginnend PythonBeginnend PythonGeen
Setup-tijd10–15 min5–10 min5–10 min2 min
OnderhoudslastGemiddeld (selectors breken)Laag (stabiele JSON)Laag (stabiele JSON)Geen
Diepte van de dataAlleen frontpageElk item, gebruikersZoeken + historieFrontpage + subpagina’s
ReactiesMoeilijkMakkelijk (recursief)Makkelijk (geneste boom)Subpaginacrawling
Historische dataNeeNeeJa (volledig archief)Nee
ExportoptiesZelf coderenZelf coderenZelf coderenIngebouwd (Excel, Sheets, etc.)
Planningcron / GitHub Actionscron / GitHub Actionscron / GitHub ActionsIngebouwde scheduler
Het meest geschikt voorScraping lerenBetrouwbare pipelinesOnderzoek & analyseSnelle dataverzoeken

Als je Python leert of iets maatwerk bouwt, kies dan Methode 1 of 2. Als je historische analyse nodig hebt, voeg de Algolia API toe. Als je gewoon de data wilt zonder code, probeer Thunderbit.

Probeer Thunderbit voor Hacker News-scraping

Conclusie en belangrijkste lessen

Dit heb je nu in je toolkit:

  • Twee complete Python-methoden om Hacker News te scrapen — BeautifulSoup voor HTML-parsing en de Firebase API voor schone JSON-data
  • Pagineringstechnieken om verder te gaan dan pagina 1, inclusief de Algolia API voor historische data vanaf 2007
  • Exportcode voor CSV, Excel en Google Sheets — want data in een terminal is voor niemand anders in je team bruikbaar
  • Productiepatronen — retry-logica, caching, scheiding van crawlen en parsen, en geplande automatisering via cron of GitHub Actions
  • Een no-code alternatief voor wanneer Python meer tool is dan je nodig hebt

Mijn advies: begin voor de meeste use-cases met de Firebase API (Methode 2). Die is schoner, betrouwbaarder en geeft je toegang tot reacties zonder de hoofdpijn van geneste HTML-parsing. Voeg de Algolia API toe wanneer je historische data nodig hebt. En bewaar Thunderbit als bladwijzer voor die momenten waarop je gewoon snel een spreadsheet nodig hebt en geen heel Python-project wilt opzetten.

Als je verder wilt gaan, probeer dan HN-reacties te scrapen voor sentimentanalyse, bouw een dagelijkse digest-pijplijn met GitHub Actions, of verken de Algolia API om te volgen hoe technologische trends zich de afgelopen tien jaar hebben ontwikkeld.

Probeer Thunderbit voor snel Hacker News-scrapen Get Started Free

Veelgestelde vragen

Is het legaal om Hacker News te scrapen?

HN-data is publiek beschikbaar en Y Combinator biedt een officiële API speciaal voor programmatische toegang. De robots.txt van de site staat het scrapen van alleen-lezen content toe (frontpage, itempagina’s, gebruikerspagina’s), maar vraagt wel om een crawl-delay van 30 seconden. Houd je daaraan, scrape geen interactieve endpoints (zoals stemmen of inloggen), en je zit goed. Meer over de juridische aspecten lees je in onze gids over juridische implicaties van webscraping.

Heeft Hacker News een officiële API?

Ja. De HN Firebase API op hacker-news.firebaseio.com/v0/ is gratis, vereist geen authenticatie en geeft toegang tot stories, reacties, gebruikersprofielen en alle feedtypes (top, new, best, ask, show, jobs). De API levert schone JSON en heeft geen opgegeven rate limit, al blijft het altijd verstandig om beleefd met de requestfrequentie om te gaan.

Hoe scrape ik Hacker News-reacties met Python?

Gebruik de Firebase API om een story-item op te halen en krijg het kids-veld terug (een array met ID’s van top-level reacties). Elke reactie is zelf weer een item met een eigen kids-veld voor antwoorden. Doorloop de boom recursief met een functie die elke reactie en zijn kinderen ophaalt. Zie hierboven de sectie "Reacties scrapen (recursieve boomdoorloop)" voor de volledige code. Als alternatief geeft het Algolia API /items/<id>-endpoint de volledige geneste reactiebom in één request terug — veel sneller voor stories met veel reacties.

Kan ik Hacker News scrapen zonder code te schrijven?

Ja. Thunderbit’s AI-webscraper werkt als Chrome-extensie — open HN, klik op "AI Suggest Fields" en het herkent automatisch kolommen zoals titel, URL, score en auteur. Klik op "Scrape" en exporteer direct naar Excel, Google Sheets, Airtable of Notion. Het verwerkt paginering en kan zelfs subpagina’s bezoeken om reactiedata op te halen. Geen Python, geen selectors, geen onderhoud.

Hoe kom ik aan historische Hacker News-data?

De HN Algolia Search API is hiervoor de beste tool. Gebruik het search_by_date-endpoint met numericFilters=created_at_i>TIMESTAMP om op datumbereik te filteren. Je kunt zoeken op trefwoord, filteren op storytype en door maximaal 500 resultaatpagina’s bladeren. Voor grootschalige historische analyse zijn openbare datasets ook beschikbaar op Google BigQuery (volledig archief), ClickHouse (28 miljoen records) en Hugging Face (4 miljoen stories).

Meer lezen

Shuai Guan
Shuai Guan
CEO bij Thunderbit | Expert in AI-databautomatisering Shuai Guan is CEO van Thunderbit en alumnus van de University of Michigan Engineering. Met bijna tien jaar ervaring in tech en SaaS-architectuur specialiseert hij zich in het omzetten van complexe AI-modellen naar praktische no-code tools voor data-extractie. Op deze blog deelt hij ongefilterde, in de praktijk bewezen inzichten over webscraping en automatiseringsstrategieën, zodat je slimmere, datagedreven workflows kunt bouwen. Wanneer hij niet bezig is met het optimaliseren van databewerkingsflows, zet hij zijn oog voor detail in voor zijn passie: fotografie.
Inhoudsopgave

Vraag het en scrape een webpagina

Zeg gewoon in gewone taal wat je nodig hebt. Of nog beter: zeg helemaal niets.

Probeer Thunderbit gratis
Data extraheren met AI
Gegevens eenvoudig overzetten naar Google Sheets, Airtable of Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week