Slik scraper jeg Hacker News med Python (2 metoder, komplett kode)

Sist oppdatert April 16, 2026
Slik scraper jeg Hacker News med Python (2 metoder, komplett kode)

For noen måneder siden ville jeg lage en daglig oversikt over de viktigste Hacker News-artiklene for teamet vårt i Thunderbit. Den første tanken min var bare å bokmerke siden og skrolle gjennom den hver morgen. Det holdt i omtrent tre dager, før jeg skjønte at jeg brukte 20 minutter om dagen på å lese overskrifter og kopiere lenker inn i et regneark.

Hacker News er en av de mest verdifulle og konsentrerte kildene til teknologiinformasjon på internett — rundt 13 millioner månedlige besøk, cirka 1 300 nye innlegg hver dag og om lag 13 000 kommentarer daglig. Enten du følger nye teknologitrender, overvåker merkevaren din, bygger en rekrutteringspipeline fra «Who's Hiring»-tråder, eller bare prøver å holde deg oppdatert på hva utviklermiljøet bryr seg om, blir det en tapt kamp å følge med manuelt.

Den gode nyheten er at det er overraskende enkelt å scrape Hacker News med Python, og i denne guiden viser jeg deg to komplette metoder — HTML-scraping med BeautifulSoup og den offisielle HN Firebase API-en — sammen med paginering, dataeksport, produksjonsklare mønstre og en no-code-snarvei for når Python føles som overkill.

Hvorfor scrape Hacker News med Python?

Hacker News er ikke bare enda en lenkeaggregator. Det er en kuratert, fellesskapsdrevet feed der de mest interessante teknologihistoriene løftes fram gjennom oppstemmer og aktiv diskusjon. Publikum består i stor grad av teknologifolk (omtrent 76 % menn, hovedalder 25–34), og nettstedets 66 % direkte trafikk viser at dette er en lojal, vaneorientert leserskare — ikke tilfeldige besøkende.

Her er noen grunner til at folk scraper HN-data:

BruksområdeHva du får
Daglige teknologinyheterToppartikler, poeng og lenker sendt til innboksen eller Slack
Overvåking av merkevare/konkurrenterVarsler når firmaet eller produktet ditt blir nevnt
TrendanalyseFølg hvilke teknologier, språk eller temaer som får mer oppmerksomhet over tid
RekrutteringPlukk ut stillingsannonser, tech stack og lønnssignaler fra «Who's Hiring»-tråder
InnholdsresearchFinn temaer som gjør det bra og skriv eller del innhold rundt dem
SentimentanalyseMål hva fellesskapet mener om produkter, lanseringer eller endringer i bransjen

Selskaper med en samlet verdi på over 400 milliarder dollar — Stripe, Dropbox, Airbnb — tilskriver Hacker News viktig tidlig tilbakemelding og brukere. Drew Houston la ut Dropbox-demoen på HN i april 2007, den nådde #1, og betaversjonslisten eksploderte fra 5 000 til 75 000 brukere på én dag. HN-data er ikke bare interessante — de har også kommersiell verdi.

Dataene er offentlig tilgjengelige, men strukturen på nettstedet gjør manuell innsamling tungvint. Automatisering med Python er den praktiske løsningen.

To måter å scrape Hacker News med Python: oversikt

Denne guiden dekker to komplette, kjørbare tilnærminger:

  1. HTML-scraping med requests + BeautifulSoup — henter rå HTML fra news.ycombinator.com og parser den for å trekke ut historiedata. Flott for å lære grunnleggende scraping og hente akkurat det som ligger på siden.
  2. Den offisielle Hacker News Firebase API-en — leser JSON-endepunkter direkte, uten behov for HTML-parsing. Bedre for pålitelige datapipelines, kommentarer og historiske data.

Her er en side-ved-side-sammenligning som hjelper deg å velge det som passer best:

KriteriumHTML-scraping (requests + BS4)HN Firebase APIThunderbit (no-code)
OppsettkompleksitetMiddels (HTML-selektorer må parses)Lav (JSON-endepunkter)Ingen (2-klikk Chrome-utvidelse)
DatanyhetSanntid på forsidenSanntid (valgfri item etter ID)Sanntid
Risiko for rate limitMiddels (robots.txt sier 30 s crawl-delay)Lav (offisiell, generøs)Håndteres av Thunderbit
Tilgang til kommentarerVanskelig (nestet HTML)Enkelt (rekursiv gjennomgang av item-ID-er)Scraping av undersider
Historiske dataBegrensetVia Algolia Search APIIkke relevant
Best forLære scraping-grunnprinsipperPålitelige datapipelinesIkke-utviklere, raske eksportjobber

Begge metodene inkluderer full, kjørbar Python-kode. Og hvis du bare vil ha dataene uten å skrive noe kode i det hele tatt, går jeg også gjennom det.

Før du starter

  • Vanskelighetsgrad: Nybegynner til middels
  • Tidsbruk: Ca. 15–20 minutter per metode
  • Dette trenger du:
    • Python 3.11+ installert
    • En terminal eller kodeeditor
    • Chrome-nettleser (hvis du vil inspisere HN sin HTML eller teste no-code-alternativet)
    • Thunderbit Chrome Extension (valgfritt, for no-code-metoden)

scrape-hacker-news-methods.webp

Slik setter du opp Python-miljøet

Før vi går løs på HN-dataene, la oss rydde opp i miljøet først. Jeg anbefaler å opprette et virtuelt miljø, så prosjektavhengighetene holder seg ryddige.

# Opprett og aktiver et virtuelt miljø
python3 -m venv hn-scraper
# macOS/Linux:
source hn-scraper/bin/activate
# Windows:
hn-scraper\Scripts\activate

# Installer pakkene vi trenger for begge metodene
pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5

For produksjonsmønstre senere (cache og retry) trenger du også:

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

Ingen spesielle API-nøkler, ingen autentiseringstokener. HN-dataene er åpne.

Metode 1: Scrape Hacker News med Python ved hjelp av BeautifulSoup

Dette er den klassiske tilnærmingen — hent HTML-en, parser den og plukk ut dataene du trenger. Det er slik de fleste lærer web scraping, og HNs enkle tabellbaserte layout gjør siden til et godt øvingsfelt.

Steg 1: Hent forsiden til Hacker News

Åpne editoren din og opprett en fil som heter scrape_hn_bs4.py. Her er startkoden:

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}, Sidestørrelse: {len(response.text)} tegn")

Kjør den. Du bør se Status: 200 og en sidelengde på rundt 40 000–50 000 tegn. Det er den rå HTML-en fra HNs forside, klar til å parses.

Steg 2: Forstå HTML-strukturen

HN bruker en tabellbasert layout — ingen moderne CSS grid eller flexbox. Hver historie på siden består av to viktige <tr>-rader:

  • Historieraden (<tr class="athing submission">): inneholder plassering, tittel og lenke
  • Metadata-raden (den neste <tr>): inneholder poeng, forfatter, tidspunkt og antall kommentarer

De viktigste selektorene:

  • span.titleline > a — tittelen og URL-en til historien
  • span.score — stemmeantallet (for eksempel «118 points»)
  • a.hnuser — brukernavnet til forfatteren
  • span.age — når innlegget ble publisert
  • Den siste <a> i .subtext med ordet «comment» i teksten — antall kommentarer

Hvis du høyreklikker på en hvilken som helst historietittel i Chrome og velger «Inspect», ser du noe slikt:

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

Og metadata-raden under:

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

Det er avgjørende å forstå disse selektorene — hvis HN noen gang endrer markupen sin, må du oppdatere dem. (Spoiler: API-metoden slipper dette problemet helt.)

Steg 3: Trekk ut titler, lenker og poeng

Nå kommer selve jobben. Vi går gjennom hver historiesrad, henter tittel og lenke fra historieraden, og plukker deretter poeng fra metadata-raden rett under.

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:
    # Tittel og URL fra historieraden
    title_tag = row.select_one("span.titleline > a")
    if not title_tag:
        continue
    title = title_tag.get_text()
    link = title_tag.get("href", "")

    # Metadata fra neste rad
    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()
        # Antall kommentarer: siste <a> med "comment" i teksten
        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,
    })

# Filtrer til historier med 50+ poeng, sortert etter score
top_stories = sorted(
    [s for s in stories if s["score"] >= 50],
    key=lambda x: x["score"],
    reverse=True,
)

pprint(top_stories[:10])

Noen korte kommentarer til koden:

  • Walrus-operatoren (:=) fungerer i Python 3.8+. Den lar oss tildele og sjekke samtidig — praktisk for valgfrie elementer som span.score, som ikke finnes på alle rader (for eksempel har jobbinnlegg ingen score).
  • HN bruker \xa0 (ikke-brytende mellomrom) mellom tallet og ordet «comments», så vi splitter på det.
  • Historier som lenker til andre HN-sider (som «Ask HN»-innlegg) har relative URL-er som starter med item?id=. Du vil kanskje legge til https://news.ycombinator.com/ foran disse.

Steg 4: Kjør og se resultatet

Lagre og kjør:

python scrape_hn_bs4.py

Du bør se et resultat som dette:

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

Det er 30 historier fra side 1. Men HN har hundrevis av aktive historier til enhver tid. Vi kommer tilbake til paginering i en senere del.

Metode 2: Scrape Hacker News med Python ved hjelp av den offisielle API-en

HN Firebase API er den offisielle og godkjente måten å hente data fra Hacker News på. Ingen autentisering, ingen API-nøkler, ingen HTML-parsing. Du får rene JSON-svar. Jeg bruker denne metoden for alt som må kjøre stabilt i produksjon.

Viktige API-endepunkter du bør kjenne til

Base-URL-en er https://hacker-news.firebaseio.com/v0/. Her er endepunktene som betyr noe:

EndepunktReturnererEksempel
/v0/topstories.jsonArray med opptil 500 ID-er for topphistorier[47788542, 47787901, ...]
/v0/newstories.jsonOpptil 500 ID-er for nyeste historierSamme format
/v0/beststories.jsonOpptil 500 ID-er for beste historierSamme format
/v0/askstories.jsonOpptil 200 ID-er for «Ask HN»-innleggSamme format
/v0/showstories.jsonOpptil 200 ID-er for «Show HN»-innleggSamme format
/v0/jobstories.jsonOpptil 200 ID-er for jobbinnleggSamme format
/v0/item/{id}.jsonFull detalj for et hvilket som helst item (historie, kommentar, poll)JSON-objekt
/v0/user/{username}.jsonBrukerprofilJSON-objekt
/v0/maxitem.jsonNåværende høyeste item-IDHeltall (f.eks. 47789427)

Et story-item ser slik ut:

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

Feltet kids inneholder ID-ene til de direkte underkommentarene. Hver kommentar er selv et item som kan ha egne kids — slik er kommentar-treet bygget opp.

Steg 1: Hent ID-er for topphistorier

Opprett en fil som heter scrape_hn_api.py:

import requests
import time
from pprint import pprint

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

# Hent ID-er for topphistorier
response = requests.get(f"{API_BASE}/topstories.json")
story_ids = response.json()

print(f"Fant {len(story_ids)} ID-er for topphistorier")
# Output: Fant 500 ID-er for topphistorier

500 historie-ID-er i én forespørsel — ingen parsing, ingen selektorer, bare et JSON-array.

Steg 2: Hent historiedetaljer per ID

Nå trenger vi selve historiedataene. Her dukker fan-out-problemet opp: 500 historier betyr 500 separate API-kall. I min måling tar hvert item-kall omtrent 1,2 sekunder sekvensielt. For 500 historier blir det rundt 10 minutter.

For de fleste bruksområder trenger du ikke alle 500. Her er kode for å hente de 30 øverste:

def fetch_story(story_id):
    """Hent detaljene for én historie fra HN API."""
    resp = requests.get(f"{API_BASE}/item/{story_id}.json")
    return resp.json()

# Hent detaljene for de 30 øverste historiene
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)  # Vær hensynsfull — liten pause mellom forespørsler

# Sorter etter score, vis topp 10
top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
pprint(top)

time.sleep(0.1) legger inn en liten høflig pause. Firebase API-et har ikke en oppgitt rate limit, men det er dårlig praksis å hamre løs på et API uten pauser.

Steg 3: Scrape kommentarer (rekursiv tre-gjennomgang)

Det er her API-et virkelig skinner sammenlignet med HTML-scraping. Kommentarer på HN er dypt nestet — svar på svar på svar. I HTML betyr det at du må parse kompliserte, nestede tabellstrukturer. Med API-et får hver kommentar et kids-felt med ID-ene til barna sine, og du kan bare gå gjennom treet rekursivt.

def fetch_comments(item_id, depth=0, max_depth=3):
    """Hent kommentarer rekursivt opp til 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

# Eksempel: hent kommentarer til topphistorien
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"\nKommentarer for: {top_story['title']}")
        all_comments = []
        for kid_id in top_story_full["kids"][:5]:  # Første 5 toppnivåkommentarer
            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 "[ingen tekst]"
            print(f"{indent}[{c['author']}] {preview}...")

Denne rekursive tilnærmingen er betydelig enklere enn å prøve å parse nestede HTML-kommentartråder. Hvis du trenger komplette kommentartre, er API-et veien å gå.

Steg 4: Kjør og se resultatet

python scrape_hn_api.py

Du vil se strukturert historiedata etterfulgt av et nestet kommentarglimt. Dataene er renere, kommentarene er enkle å hente, og du slipper risikoen for at scraperen din ryker fordi HN endret et CSS-klassenavn.

Gå videre enn side 1: paginering og historiske data

De fleste HN-scraping-guider stopper ved side 1 — 30 historier. Det er greit for en rask demo, men reelle bruksområder trenger ofte mer dybde.

Scrape flere sider med BeautifulSoup

HN bruker et enkelt URL-mønster for paginering: ?p=2, ?p=3, osv. Hver side returnerer 30 historier, og nettstedet serverer opptil omtrent side 20 (rundt 600 historier totalt). Etter det får du tomme sider.

import time

def scrape_hn_pages(num_pages=5):
    """Scrape flere sider med historier fra HN-forsiden."""
    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"Side {page}: fant ingen historier, stopper.")
            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"Side {page}: hentet {len(story_rows)} historier")

        # Respekter robots.txt sitt crawl-delay på 30 sekunder
        if page < num_pages:
            time.sleep(30)

    return all_stories

stories = scrape_hn_pages(5)
print(f"\nTotalt antall historier hentet: {len(stories)}")

time.sleep(30) er viktig. HNs robots.txt ber uttrykkelig om 30 sekunders crawl-delay. Ignorerer du det, kan du bli rate-limited (HTTP 429) eller midlertidig blokkert. Fem sider med 30 sekunders intervaller tar omtrent 2,5 minutter — ikke øyeblikkelig, men respektfullt.

For brukere som ikke vil håndtere pagineringskode selv, tar Thunderbit seg av klikkbasert paginering og infinite scroll automatisk. Den klikker «More»-knappen nederst på HN-sidene uten at du trenger å konfigurere noe.

Scrape Hacker News-sider med AI

Hent historiske Hacker News-data med Algolia API

Firebase API-et gir deg nåværende data. For historisk analyse — «Hva var de viktigste Python-artiklene i 2023?» eller «Hvordan har AI-dekningen endret seg de siste 5 årene?» — trenger du 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):
    """Søk i HN via 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()

# Eksempel: finn Python-scraping-historier med 10+ poeng siden januar 2024
results = search_hn(
    query="python scraping",
    tags="story",
)
print(f"Fant {results['nbHits']} treff totalt")

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

For datofiltrerte søk bruker du numericFilters:

import calendar, datetime

# Historier siden 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"Fant {data['nbHits']} historier om Python web scraping siden 2024 med >10 poeng")

Algolia API-et er raskt (5–9 ms serverbehandlingstid), krever ingen API-nøkkel og støtter paginering opptil 500 sider. For stor historisk analyse er det det beste alternativet.

Eksport av scraped Hacker News-data til CSV, Excel og Google Sheets

Hver HN-scraping-guide jeg har sett, ender med pprint() i terminalen. Det er greit for feilsøking, men hvis du bygger en daglig oversikt eller gjør trendanalyse, trenger du dataene i en fil. Her er hvordan du får det til.

Eksporter til CSV med Python

import csv

def export_to_csv(stories, filename="hn_stories.csv"):
    """Lagre scrape-de historier til en CSV-fil."""
    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"Laget {len(stories)} historier i {filename}")

export_to_csv(stories)

Eksporter til Excel med Python

import pandas as pd

def export_to_excel(stories, filename="hn_stories.xlsx"):
    """Lagre scrape-de historier til en Excel-fil."""
    df = pd.DataFrame(stories)
    df.to_excel(filename, index=False, engine="openpyxl")
    print(f"Laget {len(stories)} historier i {filename}")

export_to_excel(stories)

Sørg for at openpyxl er installert — pandas bruker den som Excel-motor. Hvis den mangler, får du en ImportError.

Send til Google Sheets (valgfritt)

For automatiserte arbeidsflyter kan det være nyttig å sende data direkte til Google Sheets med gspread-biblioteket. Dette krever oppsett av en Google Cloud service account (en engangsprosess):

import gspread

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

# Gjør stories om til rader
header = list(stories[0].keys())
rows = [list(s.values()) for s in stories]

worksheet.clear()
worksheet.update([header] + rows)
print("Sendt til Google Sheets")

No-code-alternativet for eksport

Hvis oppsett av service accounts og eksportkode føles som mer jobb enn selve scrapingen, skjønner jeg det. Hos Thunderbit bygde vi gratis dataeksport som lar deg sende data rett til Excel, Google Sheets, Airtable eller Notion — uten kode, uten credentials og uten en pipeline du må vedlikeholde. For en enkelt datauthenting er det faktisk raskere. Mer om det under.

Gjør scraperen produksjonsklar: feilhåndtering, caching og planlegging

Hvis du bare kjører en scraper én gang for moro skyld, er koden over helt fin. Hvis du kjører den daglig som del av en arbeidsflyt, trenger du et par ting til.

Feilhåndtering og retry-logikk

Nettverk feiler. Servere struper trafikk. Én dårlig forespørsel bør ikke velte hele scrapingen. Her er en retry-funksjon med eksponentiell 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):
    """Hent en URL med automatisk retry og eksponentiell backoff."""
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    return response

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

Biblioteket tenacity håndterer retry-logikken ryddig. Det prøver opptil 5 ganger med jitter-basert eksponentiell backoff — starter på 1 sekund og går opp til maks 60 sekunder. Dette håndterer HTTP 429 (rate limit), 503 (tjenesten er utilgjengelig) og midlertidige nettverksfeil på en god måte.

Cache svar for å slippe å crawl på nytt

Under utvikling kjører du scraperen mange ganger mens du justerer parserlogikken. Uten cache treffer hver kjøring HN-serverne på nytt for de samme dataene. Biblioteket requests-cache fikser dette med to linjer:

import requests_cache

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

Etter at du har lagt disse linjene øverst i skriptet, blir alle requests.get()-kall automatisk cached i en lokal SQLite-database. Kjør skriptet 10 ganger i løpet av en time, og bare første kjøring går faktisk ut på nett. Dette er et verktøy forumbrukere ofte anbefaler, og med god grunn.

Skill crawling fra parsing

Et mønster erfarne scrapers sverger til: last ned rådata først, parse dem etterpå. Da kan du fikse parserfeil og parse på nytt uten å hente dataene igjen.

import os, json

def crawl_and_save(story_ids, output_dir="raw_data"):
    """Hent historiedata og lagre rå JSON på disk."""
    os.makedirs(output_dir, exist_ok=True)
    for sid in story_ids:
        filepath = os.path.join(output_dir, f"{sid}.json")
        if os.path.exists(filepath):
            continue  # Hopp over elementer som allerede er hentet
        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 lagrede JSON-filer til en strukturert historieliste."""
    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

Denne totrinnsmodellen er spesielt nyttig når du scraper hundrevis av elementer og vil iterere raskt på hvordan du behandler dataene.

Automatiser scraperen på en tidsplan

For en daglig HN-oversikt må scraperen kjøre automatisk. To vanlige alternativer:

Alternativ 1: cron (Linux/Mac)

# Kjør hver dag kl. 08:30 UTC
30 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1

Alternativ 2: GitHub Actions (gratis, ingen server nødvendig)

name: Scrape Hacker News

on:
  schedule:
    - cron: '30 8 * * *'  # Daglig kl. 08:30 UTC
  workflow_dispatch:        # Manuell startknapp

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 "Oppdater HN-data $(date -u +%Y-%m-%dT%H:%M:%SZ)"
          git push

Noen fallgruver med planlegging i GitHub Actions: all cron-timing er i UTC, forsinkelser på 15–60 minutter er vanlige (bruk tider som :30 i stedet for :00), og GitHub kan deaktivere planlagte workflows i repos uten aktivitet i 60 dager. Inkluder alltid workflow_dispatch slik at du kan trigge manuelt under testing.

For et enklere alternativ kan Thunderbits Scheduled Scraper-funksjon la deg beskrive planen i vanlig språk — noe som «scrape hver morgen kl. 8» — uten server eller cron-oppsett.

Når Python er overkill: no-code-måten å scrape Hacker News på

Jeg skal være ærlig her, selv om jeg er Python-entusiast og teamet mitt bygger utviklerverktøy. Hvis du bare trenger dagens topp 100 HN-historier i et regneark — akkurat nå, én gang — er det unødvendig mye arbeid å skrive, feilsøke og kjøre et Python-skript. Bare oppsettet (virtuelt miljø, installasjon av pakker, finne selektorer) tar lenger tid enn selve datainnhentingen.

Det er her Thunderbit passer inn. Slik ser arbeidsflyten ut:

  1. Åpne news.ycombinator.com i Chrome
  2. Klikk på Thunderbit-utvidelsen, deretter «AI Suggest Fields»
  3. AI-en leser siden og foreslår kolonner: Title, URL, Score, Author, Comment Count, Time Posted
  4. Juster feltene om du vil (gi dem nytt navn, fjern noen eller legg til egne — du kan til og med legge inn en AI-prompt som «Kategoriser som AI/DevTools/Web/Other»)
  5. Klikk «Scrape» — dataene vises i en strukturert tabell
  6. Eksporter til Excel, Google Sheets, Airtable eller Notion

To klikk til strukturerte data. Ingen selektorer, ingen kode, ingen vedlikehold.

En reell fordel her er at Thunderbits AI tilpasser seg layoutendringer automatisk. Tradisjonelle scrapers som bruker CSS-selektorer ryker når et nettsted endrer markup — og selv om HNs HTML er ganske stabil, har den endret seg (klassen class="athing submission" ble oppdatert, og span.titleline erstattet den eldre a.storylink). En AI-drevet scraper leser siden på nytt hver gang, så den bryr seg ikke om klasseendringer.

python-vs-thunderbit-comparison.webp

Thunderbit håndterer også paginering (ved å klikke HNs «More»-knapp automatisk) og scraping av undersider (ved å besøke kommentarsiden til hver historie og hente inn diskusjonsdata). For brukstilfellet kommentarberikelse tilsvarer det den rekursive API-koden i Metode 2 — bare uten én eneste linje å skrive.

Avveiningene er ganske enkle: Python er riktig valg når du trenger egendefinert logikk, komplekse datatransformasjoner, planlagte automasjonsflyter eller vil lære å kode. Thunderbit er riktig valg når du trenger data raskt, ikke vil vedlikeholde kode, eller ikke er utvikler. Velg verktøyet som passer situasjonen din.

Python vs. API vs. no-code: hvilken metode bør du velge?

Her er hele beslutningsrammeverket:

KriteriumBeautifulSoup (HTML)Firebase APIAlgolia APIThunderbit (no-code)
Teknisk nivå som krevesMiddels PythonNybegynner-PythonNybegynner-PythonIngen
Oppsettstid10–15 min5–10 min5–10 min2 min
VedlikeholdsbyrdeMiddels (selektorer kan ryke)Lav (stabil JSON)Lav (stabil JSON)Ingen
Dybde i dataBare forsidenHvilket som helst item, brukereSøk + historikkForside + undersider
KommentarerVanskeligEnkelt (rekursivt)Enkelt (nestet tre)Scraping av undersider
Historiske dataNeiNeiJa (fullt arkiv)Nei
EksportalternativerDu koder det selvDu koder det selvDu koder det selvInnebygd (Excel, Sheets osv.)
Planleggingcron / GitHub Actionscron / GitHub Actionscron / GitHub ActionsInnebygd scheduler
Best forLære scrapingPålitelige pipelinesForskning og analyseRaske datauthentinger

Hvis du lærer Python eller bygger noe skreddersydd, velg Metode 1 eller 2. Hvis du trenger historisk analyse, legg til Algolia API-et. Hvis du bare vil ha dataene uten koden, prøv Thunderbit.

Prøv Thunderbit for scraping av Hacker News

Konklusjon og viktigste læringspunkter

Dette har du nå i verktøykassen:

  • To komplette Python-metoder for å scrape Hacker News — BeautifulSoup for HTML-parsing og Firebase API for rene JSON-data
  • Teknikker for paginering slik at du kan scrape mer enn side 1, inkludert Algolia API for historiske data tilbake til 2007
  • Eksportkode for CSV, Excel og Google Sheets — fordi data i terminalen ikke hjelper resten av teamet ditt
  • Produksjonsmønstre — retry-logikk, caching, deling mellom crawling og parsing, og planlagt automatisering via cron eller GitHub Actions
  • Et no-code-alternativ for når Python er mer verktøy enn du egentlig trenger

Min anbefaling er å starte med Firebase API-et (Metode 2) for de fleste bruksområder. Det er renere, mer pålitelig og gir deg tilgang til kommentarer uten bryet med å parse nestet HTML. Legg til Algolia API-et når du trenger historiske data. Og ha Thunderbit bokmerket for de gangene du bare trenger et raskt regneark og ikke vil starte et helt Python-prosjekt.

Hvis du vil gå dypere, kan du prøve å scrape HN-kommentarer for sentimentanalyse, bygge en daglig oversiktspipeline med GitHub Actions, eller utforske Algolia API-et for å se hvordan teknologitrender har endret seg det siste tiåret.

Prøv Thunderbit for rask scraping av Hacker News Get Started Free

Vanlige spørsmål

Er det lov å scrape Hacker News?

HN-dataene er offentlig tilgjengelige, og Y Combinator tilbyr en offisiell API nettopp for programmatisk tilgang. Nettstedets robots.txt tillater scraping av skrivebeskyttet innhold (forside, itemsider, brukersider), men ber om 30 sekunders crawl-delay. Følg denne pausen, ikke scrape interaktive endepunkter (stemming, innlogging), så er du på trygg grunn. For mer om scraping og etikk, se guiden vår om juridiske konsekvenser ved web scraping.

Har Hacker News en offisiell API?

Ja. HN Firebase APIhacker-news.firebaseio.com/v0/ er gratis, krever ingen autentisering og gir tilgang til historier, kommentarer, brukerprofiler og alle feed-typer (top, new, best, ask, show, jobs). Den returnerer ren JSON og har ingen oppgitt rate limit, selv om det alltid anbefales å være hensynsfull med forespørsler.

Hvordan scraper jeg Hacker News-kommentarer med Python?

Ved hjelp av Firebase API-et henter du et story-item for å få kids-feltet (en array med ID-er til toppnivåkommentarer). Hver kommentar er selv et item med eget kids-felt for svar. Gå gjennom treet rekursivt med en funksjon som henter hver kommentar og barna dens. Se delen «Scrape kommentarer (rekursiv tre-gjennomgang)» over for komplett kode. Alternativt returnerer Algolia API sitt /items/<id>-endepunkt hele det nestede kommentartreet i én forespørsel — mye raskere for historier med mange kommentarer.

Kan jeg scrape Hacker News uten å skrive kode?

Ja. Thunderbits AI web scraper fungerer som en Chrome-utvidelse — åpne HN, klikk «AI Suggest Fields», og den identifiserer automatisk kolonner som tittel, URL, score og forfatter. Klikk «Scrape» og eksporter direkte til Excel, Google Sheets, Airtable eller Notion. Den håndterer paginering og kan til og med besøke undersider for å hente kommentar-data. Ingen Python, ingen selektorer, ingen vedlikehold.

Hvordan får jeg historiske Hacker News-data?

HN Algolia Search API er det beste verktøyet for dette. Bruk search_by_date-endepunktet med numericFilters=created_at_i>TIMESTAMP for å filtrere på datointervall. Du kan søke med nøkkelord, filtrere på type historie og paginere gjennom opptil 500 sider med resultater. For større historiske analyser finnes det også offentlige datasett på Google BigQuery (fullt arkiv), ClickHouse (28 millioner poster) og Hugging Face (4 millioner historier).

Les mer

Shuai Guan
Shuai Guan
CEO i Thunderbit | Ekspert på AI-drevet dataautomatisering Shuai Guan er CEO i Thunderbit og utdannet ingeniør fra University of Michigan. Med nærmere ti års erfaring innen teknologi og SaaS-arkitektur, spesialiserer han seg på å gjøre avanserte AI-modeller om til praktiske verktøy for datauttrekk uten koding. På denne bloggen deler han ærlige, velprøvde innsikter om webskraping og automatiseringsstrategier som hjelper deg å bygge smartere, datadrevne arbeidsflyter. Når han ikke optimaliserer dataflyt, bruker han det samme skarpe blikket for detaljer i sin lidenskap for fotografi.

Prøv Thunderbit

Hent leads og andre data med bare 2 klikk. Drevet av AI.

Få Thunderbit Det er gratis
Hent data med AI
Overfør enkelt data til Google Sheets, Airtable eller Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week