Vor ein paar Monaten wollte ich für unser Team bei Thunderbit einen täglichen Überblick über die wichtigsten Hacker-News-Storys aufsetzen. Mein erster Gedanke war, die Seite einfach zu bookmarken und mir jeden Morgen die Beiträge anzuschauen. Das hat ungefähr drei Tage funktioniert, bis mir klar wurde, dass ich jeden Tag rund 20 Minuten damit verbringe, Überschriften zu lesen und Links händisch in eine Tabelle zu kopieren.
Hacker News ist eine der ergiebigsten und zugleich am stärksten verdichteten Quellen für Tech-Intelligenz im Internet — mit rund 13 Millionen Besuchen pro Monat, etwa 1.300 neu eingereichten Stories pro Tag und ungefähr 13.000 Kommentaren täglich. Egal ob du neue Tech-Trends verfolgen, deine Marke im Blick behalten, aus "Who's Hiring"-Threads einen Recruiting-Stream bauen oder einfach nah dran bleiben willst, was in der Entwicklerwelt gerade abgeht: Alles manuell mitzulesen ist ein Kampf, den man auf Dauer nicht gewinnt.
Die gute Nachricht: Hacker News mit Python zu scrapen ist überraschend einfach. In diesem Leitfaden zeige ich dir zwei vollständige Wege — HTML-Scraping mit BeautifulSoup und die offizielle HN Firebase API — inklusive Pagination, Datenexport, produktionsreifen Mustern und einem No-Code-Shortcut für den Fall, dass Python dir zu viel des Guten erscheint.
Warum Hacker News mit Python scrapen?
Hacker News ist nicht bloß ein weiterer Link-Aggregator. Es ist ein kuratierter, von der Community getriebener Feed, bei dem die spannendsten Tech-Storys durch Upvotes und lebhafte Diskussionen nach oben gespült werden. Die Zielgruppe ist stark technikaffin (etwa 76 % männlich, Hauptalter 25–34), und die Direktzugriffsrate von 66 % zeigt: Hier liest eine treue Stammleserschaft mit — nicht zufällige Laufkundschaft.
Darum scrapen Menschen HN-Daten:
| Anwendungsfall | Was du bekommst |
|---|---|
| Tägliches Tech-Briefing | Top-Storys, Scores und Links direkt per E-Mail oder Slack |
| Marken- und Wettbewerbsmonitoring | Benachrichtigungen, wenn dein Unternehmen oder Produkt erwähnt wird |
| Trendanalyse | Verfolge, welche Technologien, Sprachen oder Themen im Zeitverlauf an Fahrt gewinnen |
| Recruiting | Analysiere "Who's Hiring"-Threads nach Stellenangeboten, Tech-Stacks und Gehaltsindikatoren |
| Content-Recherche | Finde Themen mit hoher Resonanz für neue Inhalte |
| Sentiment-Analyse | Ermittle die Stimmung der Community zu Produkten, Launches oder Branchenentwicklungen |
Unternehmen mit zusammen mehr als 400 Milliarden US-Dollar Marktkapitalisierung — darunter Stripe, Dropbox und Airbnb — haben Hacker News wichtige frühe Rückmeldungen und Nutzer zu verdanken. Drew Houston stellte im April 2007 die Dropbox-Demo auf HN ein, sie landete auf Platz 1, und die Beta-Warteliste sprang an einem einzigen Tag von 5.000 auf 75.000 Nutzer. HN-Daten sind also nicht nur spannend, sondern auch wirtschaftlich relevant.
Die Daten sind öffentlich verfügbar, aber die Struktur der Seite macht die manuelle Erfassung mühsam. Automatisierung mit Python ist hier die pragmatische Lösung.
Zwei Wege, Hacker News mit Python zu scrapen: Überblick
Dieser Leitfaden deckt zwei vollständige, direkt ausführbare Ansätze ab:
- HTML-Scraping mit
requests+ BeautifulSoup — lädt das rohe HTML von news.ycombinator.com und liest daraus die Story-Daten aus. Ideal, um die Grundlagen des Scrapings zu lernen und die Inhalte der Seite sehr genau zu erfassen. - Die offizielle Hacker News Firebase API — greift direkt auf JSON-Endpunkte zu, ganz ohne HTML-Parsing. Besser für robuste Datenpipelines, Kommentare und historische Daten.
Hier ein direkter Vergleich, damit du leichter entscheiden kannst, was zu deinem Bedarf passt:
| Kriterium | HTML-Scraping (requests + BS4) | HN Firebase API | Thunderbit (No-Code) |
|---|---|---|---|
| Einrichtungsaufwand | Mittel (HTML-Selektoren parsen) | Niedrig (JSON-Endpunkte) | Keiner (Chrome-Erweiterung in 2 Klicks) |
| Aktualität der Daten | Echtzeit-Startseite | Echtzeit (jedes Item per ID) | Echtzeit |
| Risiko durch Rate Limits | Mittel (robots.txt nennt 30 Sekunden Crawl-Delay) | Niedrig (offiziell, großzügig) | Von Thunderbit verwaltet |
| Zugriff auf Kommentare | Schwer (verschachteltes HTML) | Einfach (rekursive Item-IDs) | Subpage-Scraping-Funktion |
| Historische Daten | Begrenzt | Über die Algolia Search API | N/A |
| Am besten geeignet für | Scraping-Grundlagen lernen | Zuverlässige Datenpipelines | Nicht-Entwickler, schnelle Exporte |
Beide Methoden enthalten vollständigen, ausführbaren Python-Code. Und wenn du einfach nur die Daten willst, ohne überhaupt Code zu schreiben, zeige ich dir auch dafür eine Lösung.
Bevor du startest
- Schwierigkeitsgrad: Anfänger bis Fortgeschrittene
- Benötigte Zeit: ca. 15–20 Minuten pro Methode
- Was du brauchst:
- Python 3.11+ installiert
- Ein Terminal oder Code-Editor
- Chrome-Browser (wenn du HNs HTML ansehen oder die No-Code-Option ausprobieren möchtest)
- Thunderbit Chrome Extension (optional, für die No-Code-Methode)

Python-Umgebung einrichten
Bevor wir uns an HN-Daten machen, richten wir die Umgebung ein. Ich empfehle eine virtuelle Umgebung, damit die Abhängigkeiten sauber getrennt bleiben.
# Virtuelle Umgebung erstellen und aktivieren
python3 -m venv hn-scraper
# macOS/Linux:
source hn-scraper/bin/activate
# Windows:
hn-scraper\Scripts\activate
# Benötigte Pakete für beide Methoden installieren
pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5
Für die späteren Produktionsmuster (Caching, Retries) brauchst du außerdem:
pip install requests-cache==1.3.1 tenacity==9.1.4
Keine API-Keys, keine Authentifizierungstokens. Die HN-Daten sind frei zugänglich.
Methode 1: Hacker News mit Python und BeautifulSoup scrapen
Das ist der klassische Weg — HTML abrufen, parsen und die gewünschten Daten herausziehen. So lernen die meisten Menschen Web Scraping, und das tabellenbasierte HN-Layout ist dafür ein gutes Übungsfeld.
Schritt 1: Die Hacker-News-Startseite abrufen
Öffne deinen Editor und erstelle eine Datei namens scrape_hn_bs4.py. Hier ist der 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}, Seitenlänge: {len(response.text)} Zeichen")
Führe das Skript aus. Du solltest Status: 200 und eine Seitenlänge von ungefähr 40.000–50.000 Zeichen sehen. Das ist das rohe HTML der HN-Startseite, das nun im Speicher liegt und geparst werden kann.
Schritt 2: Die HTML-Struktur verstehen
HN nutzt ein tabellenbasiertes Layout — kein modernes CSS Grid oder Flexbox. Jede Story auf der Seite besteht aus zwei wichtigen <tr>-Zeilen:
- Die Story-Zeile (
<tr class="athing submission">): enthält Rang, Titel und Link - Die Metadaten-Zeile (die nächste
<tr>): enthält Punkte, Autor, Zeitpunkt und Kommentaranzahl
Die wichtigsten Selektoren:
span.titleline > a— Titel und URL der Storyspan.score— die Punktzahl (z. B. "118 points")a.hnuser— der Benutzername des Autorsspan.age— der Veröffentlichungszeitpunkt- Das letzte
<a>in.subtextmit "comment" im Text — die Kommentaranzahl
Wenn du in Chrome auf einen Story-Titel rechtsklickst und "Untersuchen" wählst, siehst du etwa Folgendes:
<span class="titleline">
<a href="https://darkbloom.dev">Darkbloom – Private inference on idle Macs</a>
</span>
Und darunter die Metadaten-Zeile:
<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>
Diese Selektoren zu verstehen ist entscheidend — wenn HN sein Markup ändert, musst du sie anpassen. (Spoiler: Die API-Methode umgeht dieses Problem komplett.)
Schritt 3: Titel, Links und Scores extrahieren
Jetzt kommt der eigentliche Teil. Wir gehen jede Story-Zeile durch, holen Titel und Link aus der Story-Zeile und lesen dann den Score aus der direkt darunterliegenden Metadaten-Zeile aus.
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 und URL aus der Story-Zeile
title_tag = row.select_one("span.titleline > a")
if not title_tag:
continue
title = title_tag.get_text()
link = title_tag.get("href", "")
# Metadaten aus der nächsten Zeile
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()
# Kommentaranzahl: letztes <a> mit "comment" im Text
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,
})
# Stories mit 50+ Punkten filtern und nach Score sortieren
top_stories = sorted(
[s for s in stories if s["score"] >= 50],
key=lambda x: x["score"],
reverse=True,
)
pprint(top_stories[:10])
Ein paar Hinweise zum Code:
- Der Walrus-Operator (
:=) funktioniert ab Python 3.8. Damit kannst du zuweisen und prüfen zugleich — praktisch für optionale Elemente wiespan.score, die nicht in jeder Zeile existieren (z. B. bei Job-Posts ohne Punkte). - HN verwendet
\xa0(geschütztes Leerzeichen) zwischen Zahl und "comments", deshalb teilen wir an dieser Stelle. - Stories, die auf andere HN-Seiten verlinken (z. B. "Ask HN"-Beiträge), haben relative URLs, die mit
item?id=beginnen. Dafür kannst du bei Bedarfhttps://news.ycombinator.com/voranstellen.
Schritt 4: Ausführen und Ergebnisse ansehen
Speichere und führe aus:
python scrape_hn_bs4.py
Du solltest eine Ausgabe wie diese sehen:
[{'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/...'},
...]
Das sind 30 Stories von Seite 1. Hacker News hat aber zu jedem Zeitpunkt Hunderte aktive Stories. Pagination behandeln wir weiter unten.
Methode 2: Hacker News mit Python über die offizielle API scrapen
Die HN Firebase API ist der offiziell unterstützte Weg, um auf Hacker-News-Daten zuzugreifen. Keine Authentifizierung, keine API-Keys, kein HTML-Parsing. Du erhältst saubere JSON-Antworten. Ich nutze diese Methode immer dann, wenn etwas zuverlässig in Produktion laufen muss.
Wichtige API-Endpunkte, die du kennen solltest
Die Basis-URL lautet https://hacker-news.firebaseio.com/v0/. Diese Endpunkte sind relevant:
| Endpunkt | Liefert | Beispiel |
|---|---|---|
/v0/topstories.json | Array mit bis zu 500 Top-Story-IDs | [47788542, 47787901, ...] |
/v0/newstories.json | Bis zu 500 neueste Story-IDs | Gleiches Format |
/v0/beststories.json | Bis zu 500 beste Story-IDs | Gleiches Format |
/v0/askstories.json | Bis zu 200 "Ask HN"-Story-IDs | Gleiches Format |
/v0/showstories.json | Bis zu 200 "Show HN"-Story-IDs | Gleiches Format |
/v0/jobstories.json | Bis zu 200 Job-Story-IDs | Gleiches Format |
/v0/item/{id}.json | Vollständige Details zu jedem Item (Story, Kommentar, Poll) | JSON-Objekt |
/v0/user/{username}.json | Benutzerprofil | JSON-Objekt |
/v0/maxitem.json | Aktuelle höchste Item-ID | Integer (z. B. 47789427) |
Ein Story-Objekt sieht so aus:
{
"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"
}
Das Feld kids enthält die IDs der direkten Unterkommentare. Jeder Kommentar ist wiederum ein eigenes Item, das ebenfalls kids haben kann — so ist der Kommentarbaum aufgebaut.
Schritt 1: Top-Story-IDs abrufen
Erstelle eine Datei namens scrape_hn_api.py:
import requests
import time
from pprint import pprint
API_BASE = "https://hacker-news.firebaseio.com/v0"
# Top-Story-IDs abrufen
response = requests.get(f"{API_BASE}/topstories.json")
story_ids = response.json()
print(f"{len(story_ids)} Top-Story-IDs erhalten")
# Ausgabe: 500 Top-Story-IDs
500 Story-IDs in einer einzigen Anfrage — kein Parsen, keine Selektoren, nur ein JSON-Array.
Schritt 2: Story-Details per ID abrufen
Jetzt brauchen wir die eigentlichen Story-Daten. Hier zeigt sich das Fan-out-Problem: 500 Stories bedeuten 500 einzelne API-Aufrufe. In meinen Tests dauert jede Item-Anfrage nacheinander ungefähr 1,2 Sekunden. Bei 500 Stories sind das rund 10 Minuten.
Für die meisten Anwendungsfälle brauchst du ohnehin nicht alle 500. Hier ist Code, der die Top 30 holt:
def fetch_story(story_id):
"""Ruft die Details einer einzelnen Story über die HN-API ab."""
resp = requests.get(f"{API_BASE}/item/{story_id}.json")
return resp.json()
# Details für die Top 30 Stories abrufen
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) # Höflich bleiben — kleine Pause zwischen den Anfragen
# Nach Score sortieren, Top 10 anzeigen
top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
pprint(top)
time.sleep(0.1) sorgt für eine kleine Pause. Die Firebase API nennt zwar kein explizites Rate Limit, aber jede API ohne Pausen zu belasten ist keine gute Praxis.
Schritt 3: Kommentare scrapen (rekursiver Baumdurchlauf)
Hier spielt die API ihren größten Vorteil gegenüber HTML-Scraping aus. Kommentare auf HN sind tief verschachtelt — Antworten auf Antworten auf Antworten. Im HTML bedeutet das, komplexe verschachtelte Tabellen zu parsen. Über die API liefert das kids-Feld jeder Kommentar-ID einfach die Kind-IDs, und du läufst rekursiv durch den Baum.
def fetch_comments(item_id, depth=0, max_depth=3):
"""Ruft Kommentare rekursiv bis max_depth ab."""
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", "[gelöscht]"),
"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
# Beispiel: Kommentare für die Top-Story abrufen
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"\nKommentare zu: {top_story['title']}")
all_comments = []
for kid_id in top_story_full["kids"][:5]: # Erste 5 Top-Level-Kommentare
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 "[kein Text]"
print(f"{indent}[{c['author']}] {preview}...")
Dieser rekursive Ansatz ist deutlich einfacher, als verschachtelte Kommentar-Threads im HTML zu parsen. Wenn du vollständige Kommentarbäume brauchst, ist die API der richtige Weg.
Schritt 4: Ausführen und Ergebnisse ansehen
python scrape_hn_api.py
Du siehst strukturierte Story-Daten und darunter eine verschachtelte Kommentarvorschau. Die Daten sind sauberer, der Kommentarzugriff ist trivial, und du läufst nicht Gefahr, dass dein Scraper scheitert, nur weil HN eine CSS-Klasse umbenannt hat.
Mehr als nur Seite 1: Pagination und historische Daten
Die meisten HN-Scraping-Tutorials hören nach Seite 1 auf — also nach 30 Stories. Für einen schnellen Demo-Case reicht das, aber echte Anwendungsfälle brauchen oft mehr Tiefe.
Mehrere Seiten mit BeautifulSoup scrapen
Die Pagination von HN nutzt ein einfaches URL-Muster: ?p=2, ?p=3 usw. Jede Seite liefert 30 Stories, und die Seite stellt ungefähr bis Seite 20 Inhalte bereit (insgesamt rund 600 Stories). Danach kommen leere Seiten.
import time
def scrape_hn_pages(num_pages=5):
"""Mehrere Seiten der HN-Startseite scrapen."""
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"Seite {page}: keine Stories gefunden, Abbruch.")
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"Seite {page}: {len(story_rows)} Stories gescraped")
# robots.txt-Crawl-Delay von 30 Sekunden respektieren
if page < num_pages:
time.sleep(30)
return all_stories
stories = scrape_hn_pages(5)
print(f"\nInsgesamt gescrapete Stories: {len(stories)}")
time.sleep(30) ist wichtig. HNs robots.txt verlangt ausdrücklich ein Crawl-Delay von 30 Sekunden. Wer das ignoriert, riskiert Rate Limits (HTTP 429) oder eine vorübergehende Sperre. Fünf Seiten mit 30-Sekunden-Abständen dauern also etwa 2,5 Minuten — nicht blitzschnell, aber fair.
Wer die Pagination nicht selbst programmieren will, kann mit Thunderbit klickbasierte und Infinite-Scroll-Pagination automatisch verarbeiten. Das Tool klickt den "More"-Button am Seitenende ohne jede Konfiguration.
Hacker-News-Seiten mit KI scrapen
Historische Hacker-News-Daten mit der Algolia API abrufen
Die Firebase API liefert aktuelle Daten. Für historische Analysen — "Welche Python-Storys waren 2023 am wichtigsten?" oder "Wie hat sich die AI-Berichterstattung in den letzten 5 Jahren verändert?" — brauchst du die 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):
"""Durchsucht HN über die 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()
# Beispiel: Python-Scraping-Stories mit 10+ Punkten seit Januar 2024 finden
results = search_hn(
query="python scraping",
tags="story",
)
print(f"Insgesamt {results['nbHits']} Ergebnisse gefunden")
for hit in results["hits"][:5]:
print(f" [{hit.get('points', 0)} Pkt.] {hit['title']}")
Für datumsbasierte Filter nutze numericFilters:
import calendar, datetime
# Stories seit dem 1. Januar 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"Seit 2024 mit >10 Punkten {data['nbHits']} Stories über Python Web Scraping gefunden")
Die Algolia API ist schnell (5–9 ms Server-Verarbeitungszeit), braucht keinen API-Key und unterstützt Pagination bis zu 500 Seiten. Für umfangreiche historische Analysen ist sie die beste verfügbare Option.
Gescrapete Hacker-News-Daten nach CSV, Excel und Google Sheets exportieren
Jedes HN-Scraping-Tutorial, das ich gesehen habe, endet mit pprint()-Ausgabe im Terminal. Zum Debuggen ist das okay, aber wenn du einen täglichen Digest baust oder Trends analysierst, brauchst du die Daten in einer Datei. So bekommst du sie dorthin.
Export nach CSV mit Python
import csv
def export_to_csv(stories, filename="hn_stories.csv"):
"""Gescrapte Stories in eine CSV-Datei schreiben."""
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"{len(stories)} Stories in {filename} gespeichert")
export_to_csv(stories)
Export nach Excel mit Python
import pandas as pd
def export_to_excel(stories, filename="hn_stories.xlsx"):
"""Gescrapte Stories in eine Excel-Datei schreiben."""
df = pd.DataFrame(stories)
df.to_excel(filename, index=False, engine="openpyxl")
print(f"{len(stories)} Stories in {filename} gespeichert")
export_to_excel(stories)
Stelle sicher, dass openpyxl installiert ist — pandas nutzt es als Excel-Engine. Fehlt es, bekommst du einen ImportError.
Optional: In Google Sheets übertragen
Für automatisierte Workflows möchtest du die Daten vielleicht direkt mit der gspread-Bibliothek in Google Sheets schreiben. Dafür musst du einmalig einen Google-Cloud-Service-Account einrichten:
import gspread
gc = gspread.service_account(filename="service_account.json")
sh = gc.open("HN Daily Digest")
worksheet = sh.sheet1
# Stories in Zeilen umwandeln
header = list(stories[0].keys())
rows = [list(s.values()) for s in stories]
worksheet.clear()
worksheet.update([header] + rows)
print("In Google Sheets übertragen")
Die No-Code-Export-Alternative
Wenn das Einrichten von Service Accounts und Export-Code mehr Aufwand klingt als das eigentliche Scraping, kann ich das gut nachvollziehen. Bei Thunderbit haben wir einen kostenlosen Datenexport entwickelt, mit dem du gescrapte Daten direkt nach Excel, Google Sheets, Airtable oder Notion schicken kannst — ohne Code, ohne Zugangsdaten, ohne Wartungsaufwand. Für einen einmaligen Datenabruf ist das tatsächlich schneller. Mehr dazu weiter unten.
Deinen Scraper produktionsreif machen: Fehlerbehandlung, Caching und Scheduling
Wenn du ein Skript nur einmal zum Spaß laufen lässt, reicht der Code oben völlig aus. Wenn es aber täglich Teil eines Workflows sein soll, brauchst du noch ein paar Bausteine.
Fehlerbehandlung und Retry-Logik
Netzwerke fallen aus. Server drosseln Anfragen. Eine einzelne fehlerhafte Anfrage sollte nicht deinen gesamten Scrape zu Fall bringen. Hier ist eine Retry-Funktion mit exponentiellem 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):
"""Lädt eine URL mit automatischen Retries und exponentiellem Backoff."""
response = requests.get(url, timeout=10)
response.raise_for_status()
return response
# Verwendung:
try:
resp = fetch_with_retry("https://hacker-news.firebaseio.com/v0/topstories.json")
story_ids = resp.json()
except Exception as e:
print(f"Nach Retries fehlgeschlagen: {e}")
Die Bibliothek tenacity übernimmt die Retry-Logik sauber. Sie versucht es bis zu 5-mal mit zufälligem exponentiellem Backoff — beginnend bei 1 Sekunde, maximal 60 Sekunden. Das hilft bei HTTP 429 (Rate Limit), 503 (Service nicht verfügbar) und vorübergehenden Netzwerkfehlern.
Antworten cachen, um erneutes Crawlen zu vermeiden
In der Entwicklung führst du dein Skript oft mehrfach aus, während du die Parsing-Logik anpasst. Ohne Caching wird bei jedem Lauf HN erneut für dieselben Daten angefragt. Die Bibliothek requests-cache löst das in zwei Zeilen:
import requests_cache
requests_cache.install_cache("hn_cache", expire_after=3600) # 1 Stunde Cache
Nach diesen Zeilen am Anfang deines Skripts werden alle requests.get()-Aufrufe automatisch in einer lokalen SQLite-Datenbank gecacht. Wenn du dein Skript innerhalb einer Stunde zehnmal startest, trifft nur der erste Lauf tatsächlich das Netzwerk. Das ist ein Tool, das Forum-Nutzer häufig empfehlen — und das aus gutem Grund.
Crawling und Parsing trennen
Ein Muster, auf das erfahrene Scraper schwören: zuerst die Rohdaten herunterladen, danach parsen. So kannst du, falls die Parsing-Logik einen Fehler hat, nur den Parser korrigieren und die Daten erneut verarbeiten, ohne alles neu abzurufen.
import os, json
def crawl_and_save(story_ids, output_dir="raw_data"):
"""Story-Daten abrufen und als rohes JSON auf der Festplatte speichern."""
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 # Bereits abgerufene Items überspringen
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"):
"""Gespeicherte JSON-Dateien in eine strukturierte Story-Liste umwandeln."""
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
Dieser Zweiphasen-Ansatz ist besonders wertvoll, wenn du Hunderte Items scrapen und die Verarbeitung schnell iterieren möchtest.
Deinen Scraper nach Zeitplan automatisieren
Für einen täglichen HN-Digest soll dein Scraper automatisch laufen. Zwei gängige Optionen:
Option 1: cron (Linux/Mac)
# Jeden Tag um 8:30 Uhr UTC ausführen
30 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1
Option 2: GitHub Actions (kostenlos, kein Server nötig)
name: Scrape Hacker News
on:
schedule:
- cron: '30 8 * * *' # Täglich um 8:30 Uhr UTC
workflow_dispatch: # Manueller Start-Button
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
Ein paar Stolpersteine bei GitHub-Actions-Schedules: Alle Cron-Zeiten sind in UTC, Verzögerungen von 15–60 Minuten sind normal (nutze also Zeiten wie :30 statt :00), und GitHub kann geplante Workflows in Repos ohne Aktivität für 60 Tage deaktivieren. Füge immer workflow_dispatch hinzu, damit du für Tests manuell starten kannst.
Für eine einfachere Lösung lässt sich mit Thunderbits Scheduled Scraper ein Zeitplan in normaler Sprache beschreiben — etwa "jeden Morgen um 8 Uhr scrapen" — ganz ohne Server oder Cron-Setup.
Wenn Python überdimensioniert ist: Der No-Code-Weg für Hacker News
Ich bin hier ehrlich, auch wenn ich Python mag und mein Team Entwickler-Tools baut. Wenn du nur die heutigen Top 100 HN-Storys einmalig und sofort in einer Tabelle brauchst, ist ein Python-Skript oft unnötiger Aufwand. Schon das Setup — virtuelle Umgebung, Pakete installieren, Selektoren herausfinden — dauert länger als das eigentliche Sammeln der Daten.
Hier kommt Thunderbit ins Spiel. So funktioniert der Ablauf:
- Öffne
news.ycombinator.comin Chrome - Klicke auf das Thunderbit-Icon und dann auf "AI Suggest Fields"
- Die KI liest die Seite und schlägt Spalten vor: Title, URL, Score, Author, Comment Count, Time Posted
- Passe die Felder bei Bedarf an (umbenennen, entfernen oder eigene hinzufügen — du kannst sogar einen KI-Prompt ergänzen wie "Als AI/DevTools/Web/Other kategorisieren")
- Klicke auf "Scrape" — die Daten erscheinen als strukturierte Tabelle
- Exportiere nach Excel, Google Sheets, Airtable oder Notion
Zwei Klicks bis zu strukturierten Daten. Keine Selektoren, kein Code, keine Wartung.
Ein echter Vorteil: Thunderbits KI passt sich automatisch an Layout-Änderungen an. Klassische Scraper mit CSS-Selektoren brechen, wenn sich das Markup einer Seite ändert — und obwohl HNs HTML recht stabil ist, hat es sich bereits geändert (class="athing submission" wurde angepasst, span.titleline ersetzte das ältere a.storylink). Ein KI-gestützter Scraper liest die Seite jedes Mal neu und ist daher unempfindlich gegenüber geänderten Klassennamen.

Thunderbit übernimmt außerdem Pagination (klickt den "More"-Button auf HN automatisch) und das Scrapen von Unterseiten (öffnet die Kommentarseite jeder Story, um Diskussionsdaten zu holen). Für den Anwendungsfall Kommentar-Anreicherung entspricht das dem rekursiven API-Code aus Methode 2 — nur eben ohne eine einzige Zeile zu schreiben.
Die Abwägung ist simpel: Python ist die richtige Wahl, wenn du eigene Logik, komplexe Transformationen, automatisierte Pipeline-Prozesse oder Lernziele hast. Thunderbit ist die richtige Wahl, wenn du schnell Daten brauchst, keinen Code warten willst oder kein Entwickler bist. Nimm das Werkzeug, das zu deiner Situation passt.
Python vs. API vs. No-Code: Welche Methode solltest du wählen?
Hier ist die komplette Entscheidungsübersicht:
| Kriterium | BeautifulSoup (HTML) | Firebase API | Algolia API | Thunderbit (No-Code) |
|---|---|---|---|---|
| Benötigtes technisches Wissen | Python auf mittlerem Niveau | Python für Anfänger | Python für Anfänger | Keines |
| Einrichtungszeit | 10–15 Min. | 5–10 Min. | 5–10 Min. | 2 Min. |
| Wartungsaufwand | Mittel (Selektoren können brechen) | Niedrig (stabiles JSON) | Niedrig (stabiles JSON) | Keiner |
| Datentiefe | Nur Startseite | Jedes Item, Nutzer | Suche + Historie | Startseite + Unterseiten |
| Kommentare | Schwer | Einfach (rekursiv) | Einfach (verschachtelter Baum) | Subpage-Scraping |
| Historische Daten | Nein | Nein | Ja (vollständiges Archiv) | Nein |
| Exportoptionen | Selbst programmieren | Selbst programmieren | Selbst programmieren | Integriert (Excel, Sheets usw.) |
| Scheduling | cron / GitHub Actions | cron / GitHub Actions | cron / GitHub Actions | Integrierter Scheduler |
| Am besten für | Scraping lernen | Zuverlässige Pipelines | Recherche & Analyse | Schnelle Datenzüge |
Wenn du Python lernst oder etwas Eigenes baust, nimm Methode 1 oder 2. Wenn du historische Analysen brauchst, ergänze die Algolia API. Wenn du einfach nur die Daten ohne Code willst, probier Thunderbit aus.
Thunderbit für Hacker-News-Scraping testen
Fazit und wichtigste Erkenntnisse
Jetzt hast du in deinem Werkzeugkasten:
- Zwei vollständige Python-Methoden zum Scrapen von Hacker News — BeautifulSoup für HTML-Parsing und die Firebase API für saubere JSON-Daten
- Pagination-Techniken für mehr als Seite 1, inklusive der Algolia API für historische Daten bis zurück ins Jahr 2007
- Export-Code für CSV, Excel und Google Sheets — denn Daten im Terminal helfen sonst niemandem im Team
- Produktionsmuster — Retry-Logik, Caching, Trennung von Crawling und Parsing sowie geplante Automatisierung per cron oder GitHub Actions
- Eine No-Code-Alternative für den Fall, dass Python mehr Werkzeug ist, als du gerade brauchst
Meine Empfehlung: Starte für die meisten Fälle mit der Firebase API (Methode 2). Sie ist sauberer, zuverlässiger und bietet Kommentarzugriff, ohne dass du verschachteltes HTML mühsam parsen musst. Ergänze die Algolia API, wenn du historische Daten brauchst. Und behalte Thunderbit als Lesezeichen, wenn du nur schnell eine Tabelle brauchst und kein komplettes Python-Projekt aufsetzen willst.
Wenn du tiefer einsteigen willst, versuche HN-Kommentare für Sentiment-Analyse zu scrapen, baue mit GitHub Actions eine tägliche Digest-Pipeline oder nutze die Algolia API, um zu verfolgen, wie sich Technologietrends im letzten Jahrzehnt verändert haben.
Thunderbit für schnelles Hacker-News-Scraping testen Get Started Free
FAQs
Ist es legal, Hacker News zu scrapen?
HN-Daten sind öffentlich zugänglich, und Y Combinator stellt sogar eine offizielle API für den programmgesteuerten Zugriff bereit. Die robots.txt der Seite erlaubt das Scrapen von lesbaren Inhalten (Startseite, Item-Seiten, Benutzerseiten), verlangt aber ein Crawl-Delay von 30 Sekunden. Wenn du diese Pause respektierst und keine interaktiven Endpunkte (Voting, Login) abfragst, bist du auf der sicheren Seite. Mehr zu den rechtlichen Aspekten findest du in unserem Leitfaden zu den rechtlichen Implikationen von Web Scraping.
Hat Hacker News eine offizielle API?
Ja. Die HN Firebase API unter hacker-news.firebaseio.com/v0/ ist kostenlos, erfordert keine Authentifizierung und bietet Zugriff auf Stories, Kommentare, Nutzerprofile und alle Feed-Typen (Top, New, Best, Ask, Show, Jobs). Sie liefert sauberes JSON und hat kein ausdrücklich genanntes Rate Limit, obwohl ein höflicher Umgang mit der Anfragedichte immer empfehlenswert ist.
Wie scrape ich Hacker-News-Kommentare mit Python?
Mit der Firebase API rufst du ein Story-Item ab und erhältst sein kids-Feld (ein Array der IDs der Top-Level-Kommentare). Jeder Kommentar ist wiederum ein Item mit eigenem kids-Feld für Antworten. Laufe den Baum rekursiv mit einer Funktion durch, die jeden Kommentar und seine Kinder abruft. Im Abschnitt "Kommentare scrapen (rekursiver Baumdurchlauf)" oben findest du den vollständigen Code. Alternativ liefert der Algolia-API-Endpunkt /items/<id> den gesamten verschachtelten Kommentarbaum in einer einzigen Anfrage — deutlich schneller bei kommentarstarken Stories.
Kann ich Hacker News auch ohne Code scrapen?
Ja. Thunderbits KI-Webscraper funktioniert als Chrome-Erweiterung — öffne HN, klicke auf "AI Suggest Fields", und das Tool erkennt automatisch Spalten wie Titel, URL, Score und Autor. Klicke auf "Scrape" und exportiere direkt nach Excel, Google Sheets, Airtable oder Notion. Es unterstützt Pagination und kann sogar Unterseiten besuchen, um Kommentardaten zu holen. Kein Python, keine Selektoren, keine Wartung.
Wie bekomme ich historische Hacker-News-Daten?
Die HN Algolia Search API ist dafür das beste Werkzeug. Verwende den Endpunkt search_by_date mit numericFilters=created_at_i>TIMESTAMP, um nach Zeitraum zu filtern. Du kannst nach Stichwort suchen, nach Story-Typ filtern und durch bis zu 500 Ergebnisseiten blättern. Für umfangreiche historische Analysen sind außerdem öffentliche Datensätze verfügbar auf Google BigQuery (vollständiges Archiv), ClickHouse (28 Millionen Datensätze) und Hugging Face (4 Millionen Stories).
Mehr erfahren


