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

Laatst bijgewerkt op April 16, 2026

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 , 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 ), 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)
    • (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.

1# Maak een virtuele omgeving aan en activeer die
2python3 -m venv hn-scraper
3# macOS/Linux:
4source hn-scraper/bin/activate
5# Windows:
6hn-scraper\Scripts\activate
7# Installeer de pakketten die we voor beide methoden nodig hebben
8pip 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:

1pip 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:

1import requests
2from bs4 import BeautifulSoup
3> This paragraph contains content that cannot be parsed and has been skipped.
4print(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:

1<span class="titleline">
2  <a href="https://darkbloom.dev">DarkbloomPrivate inference on idle Macs</a>
3</span>

En daaronder de metadata-rij:

1<span class="score" id="score_47788542">118 points</span>
2by <a href="user?id=twapi" class="hnuser">twapi</a>
3<span class="age" title="2026-04-16T04:06:39 1776312399">
4  <a href="item?id=47788542">2 hours ago</a>
5</span>
6| <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.)

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.

1import requests
2from bs4 import BeautifulSoup
3from pprint import pprint
4> This paragraph contains content that cannot be parsed and has been skipped.
5stories = []
6story_rows = soup.select("tr.athing")
7for row in story_rows:
8    # Titel en URL uit de story-rij
9    title_tag = row.select_one("span.titleline > a")
10    if not title_tag:
11        continue
12    title = title_tag.get_text()
13    link = title_tag.get("href", "")
14    # Metadata uit de volgende sibling-rij
15    meta_row = row.find_next_sibling("tr")
16    score = 0
17    author = ""
18    comments = 0
19> This paragraph contains content that cannot be parsed and has been skipped.
20> This paragraph contains content that cannot be parsed and has been skipped.
21# Filter op stories met 50+ punten, gesorteerd op score
22top_stories = sorted(
23    [s for s in stories if s["score"] >= 50],
24    key=lambda x: x["score"],
25    reverse=True,
26)
27pprint(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:

1python scrape_hn_bs4.py

Je zou uitvoer moeten zien zoals:

1[{'author': 'twapi',
2  'comments': 65,
3  'score': 118,
4  'title': 'Darkbloom – Private inference on idle Macs',
5  'url': 'https://darkbloom.dev'},
6 {'author': 'sebg',
7  'comments': 203,
8  'score': 247,
9  'title': 'Show HN: I built an open-source Perplexity alternative',
10  'url': 'https://github.com/...'},
11 ...]

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:

This paragraph contains content that cannot be parsed and has been skipped.

Een story-item ziet er zo uit:

1{
2  "by": "twapi",
3  "descendants": 65,
4  "id": 47788542,
5  "kids": [47789171, 47788769, 47788762],
6  "score": 118,
7  "time": 1776312399,
8  "title": "Darkbloom – Private inference on idle Macs",
9  "type": "story",
10  "url": "https://darkbloom.dev"
11}

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:

1import requests
2import time
3from pprint import pprint
4API_BASE = "https://hacker-news.firebaseio.com/v0"
5# Haal de ID's van de topstories op
6response = requests.get(f"{API_BASE}/topstories.json")
7story_ids = response.json()
8print(f"Ontvangen: {len(story_ids)} topstory-ID's")
9# 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:

1def fetch_story(story_id):
2    """Haalt de details van één story op via de HN API."""
3    resp = requests.get(f"{API_BASE}/item/{story_id}.json")
4    return resp.json()
5> This paragraph contains content that cannot be parsed and has been skipped.
6# Sorteer op score, toon de top 10
7top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
8pprint(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.

1def fetch_comments(item_id, depth=0, max_depth=3):
2    """Haal reacties recursief op tot max_depth."""
3    item = requests.get(f"{API_BASE}/item/{item_id}.json").json()
4    if not item or item.get("type") != "comment":
5        return []
6> This paragraph contains content that cannot be parsed and has been skipped.
7    if depth < max_depth and item.get("kids"):
8        for kid_id in item["kids"]:
9            comments.extend(fetch_comments(kid_id, depth + 1, max_depth))
10            time.sleep(0.05)
11    return comments
12# Voorbeeld: reacties ophalen voor de topstory
13if stories:
14    top_story = stories[0]
15    top_story_full = requests.get(f"{API_BASE}/item/{top_story['id']}.json").json()
16    if top_story_full.get("kids"):
17        print(f"\nReacties voor: {top_story['title']}")
18        all_comments = []
19        for kid_id in top_story_full["kids"][:5]:  # Eerste 5 reacties op hoofdniveau
20            all_comments.extend(fetch_comments(kid_id, depth=0, max_depth=2))
21            time.sleep(0.1)
22        for c in all_comments[:15]:
23            indent = "  " * c["depth"]
24            preview = c["text"][:80].replace("\n", " ") if c["text"] else "[geen tekst]"
25            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

1python 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.

1import time
2def scrape_hn_pages(num_pages=5):
3    """Scrape meerdere pagina’s van de HN-frontpage."""
4    all_stories = []
5    for page in range(1, num_pages + 1):
6        url = f"https://news.ycombinator.com/news?p={page}"
7        response = requests.get(url, headers=headers)
8        soup = BeautifulSoup(response.text, "html.parser")
9        story_rows = soup.select("tr.athing")
10        if not story_rows:
11            print(f"Pagina {page}: geen stories gevonden, stoppen.")
12            break
13        for row in story_rows:
14            title_tag = row.select_one("span.titleline > a")
15            if not title_tag:
16                continue
17            meta_row = row.find_next_sibling("tr")
18            score = 0
19            if meta_row and (score_tag := meta_row.select_one("span.score")):
20                score = int(score_tag.get_text().replace(" points", ""))
21> This paragraph contains content that cannot be parsed and has been skipped.
22        print(f"Pagina {page}: {len(story_rows)} stories gescrapet")
23        # Respecteer de robots.txt crawl-delay van 30 seconden
24        if page < num_pages:
25            time.sleep(30)
26    return all_stories
27stories = scrape_hn_pages(5)
28print(f"\nTotaal aantal gescrapete stories: {len(stories)}")

Die time.sleep(30) is belangrijk. HN’s 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 click-based en infinite-scroll-paginering automatisch. Het klikt zonder configuratie op de "More"-knop onderaan HN-pagina’s.

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 nodig.

1import requests
2ALGOLIA_BASE = "https://hn.algolia.com/api/v1"
3> This paragraph contains content that cannot be parsed and has been skipped.
4# Voorbeeld: vind Python-scrapingstories met 10+ punten sinds jan 2024
5results = search_hn(
6    query="python scraping",
7    tags="story",
8)
9print(f"Totaal gevonden resultaten: {results['nbHits']}")
10for hit in results["hits"][:5]:
11    print(f"  [{hit.get('points', 0)} pts] {hit['title']}")

Gebruik voor datumfilters numericFilters:

1import calendar, datetime
2# Stories sinds 1 januari 2024
3start_date = datetime.datetime(2024, 1, 1)
4start_ts = int(calendar.timegm(start_date.timetuple()))
5> This paragraph contains content that cannot be parsed and has been skipped.
6De Algolia API is snel (59 ms serververwerkingstijd), vereist geen API-sleutel en ondersteunt paginering tot 500 pagina’s. Voor grootschalige historische analyse is dit de beste optie.
7## Gescrapete Hacker News-data exporteren naar CSV, Excel en Google Sheets
8Elke 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.
9### Exporteren naar CSV met Python
10```python
11import csv
12def export_to_csv(stories, filename="hn_stories.csv"):
13    """Sla gescrapete stories op in een CSV-bestand."""
14    fieldnames = ["title", "url", "score", "author", "comments"]
15    with open(filename, "w", newline="", encoding="utf-8") as f:
16        writer = csv.DictWriter(f, fieldnames=fieldnames)
17        writer.writeheader()
18        writer.writerows(stories)
19    print(f"Opgeslagen: {len(stories)} stories naar {filename}")
20export_to_csv(stories)

Exporteren naar Excel met Python

1import pandas as pd
2def export_to_excel(stories, filename="hn_stories.xlsx"):
3    """Sla gescrapete stories op in een Excel-bestand."""
4    df = pd.DataFrame(stories)
5    df.to_excel(filename, index=False, engine="openpyxl")
6    print(f"Opgeslagen: {len(stories)} stories naar {filename}")
7export_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):

1import gspread
2gc = gspread.service_account(filename="service_account.json")
3sh = gc.open("HN Daily Digest")
4worksheet = sh.sheet1
5# Zet stories om naar rijen
6header = list(stories[0].keys())
7rows = [list(s.values()) for s in stories]
8worksheet.clear()
9worksheet.update([header] + rows)
10print("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:

1from tenacity import retry, stop_after_attempt, wait_exponential_jitter
2import requests
3@retry(stop=stop_after_attempt(5), wait=wait_exponential_jitter(initial=1, max=60))
4def fetch_with_retry(url):
5    """Haal een URL op met automatische retries en exponentiële backoff."""
6    response = requests.get(url, timeout=10)
7    response.raise_for_status()
8    return response
9# Gebruik:
10try:
11    resp = fetch_with_retry("https://hacker-news.firebaseio.com/v0/topstories.json")
12    story_ids = resp.json()
13except Exception as e:
14    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:

1import requests_cache
2requests_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 , 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.

1import os, json
2def crawl_and_save(story_ids, output_dir="raw_data"):
3    """Haal storydata op en sla ruwe JSON op schijf op."""
4    os.makedirs(output_dir, exist_ok=True)
5    for sid in story_ids:
6        filepath = os.path.join(output_dir, f"{sid}.json")
7        if os.path.exists(filepath):
8            continue  # Sla al opgehaalde items over
9        resp = fetch_with_retry(f"{API_BASE}/item/{sid}.json")
10        with open(filepath, "w") as f:
11            json.dump(resp.json(), f)
12> This paragraph contains content that cannot be parsed and has been skipped.
13Deze tweefasenaanpak is vooral handig als je honderden items scrapt en snel wilt itereren op hoe je de data verwerkt.
14### Je scraper automatisch plannen
15Voor een dagelijkse HN-digest moet je scraper automatisch draaien. Twee veelgebruikte opties:
16**Optie 1: cron (Linux/Mac)**
17```bash
18# Draai elke dag om 8:30 UTC
1930 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1

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

1name: Scrape Hacker News
2on:
3  schedule:
4    - cron: '30 8 * * *'  # Dagelijks om 8:30 UTC
5  workflow_dispatch:        # Handmatige triggerknop
6jobs:
7  scrape:
8    runs-on: ubuntu-latest
9    steps:
10      - uses: actions/checkout@v4
11      - uses: actions/setup-python@v6
12        with:
13          python-version: '3.12'
14      - run: pip install requests beautifulsoup4 pandas openpyxl
15      - run: python scrape_hn.py
16      - run: |
17          git config user.name "GitHub Actions Bot"
18          git config user.email "actions@github.com"
19          git add -A
20          git diff --staged --quiet || git commit -m "Update HN data $(date -u +%Y-%m-%dT%H:%M:%SZ)"
21          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 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 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, .

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 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 , 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

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 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 .

Heeft Hacker News een officiële API?

Ja. De 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 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. 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 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 (volledig archief), (28 miljoen records) en (4 miljoen stories).

Meer lezen

Shuai Guan
Shuai Guan
Co-founder/CEO @ Thunderbit. Passionate about cross section of AI and Automation. He's a big advocate of automation and loves making it more accessible to everyone. Beyond tech, he channels his creativity through a passion for photography, capturing stories one picture at a time.
Inhoudsopgave

Probeer Thunderbit

Scrape leads en andere data in slechts 2 klikken. Aangedreven door AI.

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