Qualche mese fa volevo creare un digest giornaliero delle storie più importanti di Hacker News per il team di Thunderbit. Il mio primo istinto è stato semplicemente aggiungere il sito ai preferiti e scorrerlo ogni mattina. È andata avanti per circa tre giorni, poi mi sono reso conto che stavo perdendo 20 minuti al giorno solo per leggere titoli e copiare-incollare link in un foglio di calcolo.
Hacker News è una delle fonti più ricche e concentrate di intelligence tecnologica su internet — circa , all’incirca 1.300 nuove storie inviate ogni giorno e circa 13.000 commenti generati quotidianamente. Che tu stia monitorando i trend tecnologici emergenti, controllando il tuo brand, costruendo un flusso di recruiting dalle discussioni “Who’s Hiring” o semplicemente cercando di restare al passo con ciò che conta per il mondo developer, tenerne traccia manualmente è una battaglia persa.
La buona notizia: estrarre dati da Hacker News con Python è sorprendentemente semplice. In questa guida ti mostrerò due metodi completi — il parsing HTML con BeautifulSoup e la Firebase API ufficiale di HN — insieme a paginazione, esportazione dei dati, pattern pronti per la produzione e una scorciatoia no-code per quando Python sembra eccessivo.
Perché estrarre Hacker News con Python?
Hacker News non è solo un altro aggregatore di link. È un feed curato dalla community in cui le storie tech più interessanti emergono grazie ai voti e alle discussioni attive. Il pubblico è fortemente orientato ai professionisti della tecnologia (circa ), e il 66% di traffico diretto del sito ti dice chiaramente che si tratta di una community fedele e abituale, non di visitatori occasionali.
Ecco perché molte persone estraggono dati da HN:
| Caso d’uso | Cosa ottieni |
|---|---|
| Digest tech giornaliero | Storie principali, punteggi e link consegnati via email o Slack |
| Monitoraggio brand/concorrenti | Avvisi quando viene citata la tua azienda o il tuo prodotto |
| Analisi dei trend | Traccia quali tecnologie, linguaggi o argomenti stanno guadagnando trazione nel tempo |
| Recruiting | Analizza i thread “Who’s Hiring” per offerte di lavoro, stack tecnologici e segnali salariali |
| Ricerca contenuti | Trova argomenti ad alte prestazioni su cui scrivere o da condividere |
| Analisi del sentiment | Valuta l’opinione della community su prodotti, lanci o cambiamenti del settore |
Aziende con una capitalizzazione complessiva superiore a 400 miliardi di dollari — Stripe, Dropbox, Airbnb — attribuiscono a Hacker News feedback iniziali cruciali e acquisizione di utenti. Drew Houston pubblicò la demo di Dropbox su HN nell’aprile 2007, arrivò al primo posto e la lista d’attesa beta passò da 5.000 a 75.000 utenti in un solo giorno. I dati di HN non sono solo interessanti: hanno valore commerciale.
I dati sono pubblici, ma la struttura del sito rende la raccolta manuale noiosa. L’automazione con Python è la soluzione pratica.
Due modi per estrarre Hacker News con Python: panoramica
Questa guida copre due approcci completi ed eseguibili:
- Parsing HTML con
requests+ BeautifulSoup — scarica l’HTML grezzo di news.ycombinator.com e analizzalo per estrarre i dati delle storie. Ottimo per imparare le basi dello scraping e prendere esattamente ciò che compare nella pagina. - La Firebase API ufficiale di Hacker News — interroga direttamente endpoint JSON, senza bisogno di parsing HTML. Più adatta a pipeline affidabili, all’accesso ai commenti e ai dati storici.
Ecco un confronto affiancato per aiutarti a scegliere la soluzione più adatta:
| Criterio | Scraping HTML (requests + BS4) | HN Firebase API | Thunderbit (No-Code) |
|---|---|---|---|
| Complessità iniziale | Media (selettori HTML da interpretare) | Bassa (endpoint JSON) | Nessuna (estensione Chrome in 2 clic) |
| Freschezza dei dati | Front page in tempo reale | Tempo reale (qualsiasi item tramite ID) | Tempo reale |
| Rischio di rate limit | Medio (robots.txt indica 30s di crawl delay) | Basso (API ufficiale, permissiva) | Gestito da Thunderbit |
| Accesso ai commenti | Difficile (HTML annidato) | Facile (ID degli item in modo ricorsivo) | Funzione di scraping delle sottopagine |
| Dati storici | Limitati | Tramite Algolia Search API | N/D |
| Ideale per | Imparare le basi dello scraping | Pipeline dati affidabili | Non sviluppatori, export rapidi |
Entrambi i metodi includono codice Python completo e funzionante. E se vuoi solo i dati senza scrivere codice, ti mostrerò anche quella strada.
Prima di iniziare
- Difficoltà: da principiante a intermedio
- Tempo richiesto: circa 15–20 minuti per ciascun metodo
- Cosa ti serve:
- Python 3.11+ installato
- Un terminale o un editor di codice
- Browser Chrome (se vuoi ispezionare l’HTML di HN o provare l’opzione no-code)
- (facoltativa, per il metodo no-code)

Configurare l’ambiente Python
Prima di toccare i dati di HN, prepariamo l’ambiente. Ti consiglio di creare un ambiente virtuale, così le dipendenze del progetto restano pulite.
1# Crea e attiva un ambiente virtuale
2python3 -m venv hn-scraper
3# macOS/Linux:
4source hn-scraper/bin/activate
5# Windows:
6hn-scraper\Scripts\activate
7# Installa i pacchetti necessari per entrambi i metodi
8pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5
Per i pattern più adatti alla produzione più avanti (cache, retry), ti serviranno anche:
1pip install requests-cache==1.3.1 tenacity==9.1.4
Nessuna chiave API speciale, nessun token di autenticazione. I dati di HN sono pubblici.
Metodo 1: estrarre Hacker News con Python usando BeautifulSoup
Questo è l’approccio classico — scarichi l’HTML, lo analizzi e recuperi i dati che ti servono. È il modo con cui la maggior parte delle persone impara il web scraping, e il layout semplice di HN basato su tabelle lo rende un ottimo banco di prova.
Passo 1: recuperare la front page di Hacker News
Apri l’editor e crea un file chiamato scrape_hn_bs4.py. Ecco il codice iniziale:
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}, Page length: {len(response.text)} chars")
Eseguilo. Dovresti vedere Status: 200 e una lunghezza della pagina intorno ai 40.000–50.000 caratteri. È l’HTML grezzo della front page di HN già in memoria, pronto per essere analizzato.
Passo 2: capire la struttura HTML
HN usa un layout basato su tabelle — niente moderni grid CSS o flexbox. Ogni storia nella pagina è composta da due righe <tr> chiave:
- La riga della storia (
<tr class="athing submission">): contiene posizione, titolo e link - La riga dei metadati (la
<tr>successiva): contiene punti, autore, orario e numero di commenti
I selettori importanti:
span.titleline > a— titolo e URL della storiaspan.score— punteggio/voti (ad esempio “118 points”)a.hnuser— username dell’autorespan.age— orario di pubblicazione- L’ultimo
<a>in.subtextcon la parola “comment” nel testo — numero di commenti
Se fai clic destro sul titolo di una storia in Chrome e scegli “Ispeziona”, vedrai qualcosa del genere:
1<span class="titleline">
2 <a href="https://darkbloom.dev">Darkbloom – Private inference on idle Macs</a>
3</span>
E sotto, la riga dei metadati:
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 comments</a>
Capire questi selettori è fondamentale: se HN cambiasse il markup, dovresti aggiornarli. (Spoiler: il metodo API evita del tutto questo problema.)
Passo 3: estrarre titoli, link e punteggi
Ora entra in gioco il lavoro vero. Scorreremo ogni riga delle storie, prenderemo titolo e link dalla riga principale, poi il punteggio dalla riga dei metadati subito sotto.
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 # Titolo e URL dalla riga della storia
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 # Metadati dalla riga successiva
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# Filtra le storie con almeno 50 punti, ordinate per punteggio
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])
Qualche nota sul codice:
- L’operatore walrus (
:=) funziona da Python 3.8 in poi. Permette di assegnare e verificare nella stessa riga — utile per elementi opzionali comespan.scoreche potrebbero non esistere in ogni riga (ad esempio i post di lavoro non hanno punteggio). - HN usa
\xa0(spazio non separabile) tra il numero e “comments”, quindi lo separiamo su quel carattere. - Le storie che puntano ad altre pagine HN (come i post “Ask HN”) avranno URL relativi che iniziano con
item?id=. Potresti voler anteporrehttps://news.ycombinator.com/in quei casi.
Passo 4: eseguirlo e vedere i risultati
Salva ed esegui:
1python scrape_hn_bs4.py
Dovresti vedere un output simile a questo:
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 ...]
Quelle sono 30 storie della pagina 1. Ma HN ha centinaia di storie attive in qualsiasi momento. La paginazione la vedremo in una sezione successiva.
Metodo 2: estrarre Hacker News con Python usando l’API ufficiale
La Firebase API di HN è il metodo ufficialmente supportato per accedere ai dati di Hacker News. Nessuna autenticazione, nessuna chiave API, nessun parsing HTML. Ottieni risposte JSON pulite. Io uso questo metodo per tutto ciò che deve funzionare in modo affidabile in produzione.
Endpoint API principali da conoscere
L’URL base è https://hacker-news.firebaseio.com/v0/. Ecco gli endpoint importanti:
This paragraph contains content that cannot be parsed and has been skipped.
Un item di tipo story appare così:
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}
Il campo kids contiene gli ID dei commenti figli diretti. Ogni commento è a sua volta un item che può avere i propri kids: è così che si struttura l’albero dei commenti.
Passo 1: recuperare gli ID delle top story
Crea un file chiamato scrape_hn_api.py:
1import requests
2import time
3from pprint import pprint
4API_BASE = "https://hacker-news.firebaseio.com/v0"
5# Recupera gli ID delle top story
6response = requests.get(f"{API_BASE}/topstories.json")
7story_ids = response.json()
8print(f"Got {len(story_ids)} top story IDs")
9# Output: Got 500 top story IDs
500 ID di storie in una sola richiesta — niente parsing, niente selettori, solo un array JSON.
Passo 2: recuperare i dettagli delle storie tramite ID
Ora ci servono i dati reali delle storie. Qui emerge il problema del fan-out: 500 storie significano 500 chiamate API individuali. Nei miei benchmark, ogni richiesta per un item richiede circa 1,2 secondi in sequenza. Per 500 storie, si parla di circa 10 minuti.
Per la maggior parte dei casi, non ti servono tutte e 500. Ecco il codice per prendere le prime 30:
1def fetch_story(story_id):
2 """Recupera i dettagli di una singola storia dall'API HN."""
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# Ordina per punteggio, mostra le prime 10
7top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
8pprint(top)
Il time.sleep(0.1) aggiunge una piccola pausa di cortesia. La Firebase API non dichiara un rate limit esplicito, ma martellare qualsiasi API senza pause è una cattiva pratica.
Passo 3: estrarre i commenti (visita ricorsiva dell’albero)
Qui l’API brilla davvero rispetto allo scraping HTML. I commenti su HN sono profondamente annidati — risposte a risposte a risposte. In HTML, questo significa interpretare strutture di tabelle annidate molto complesse. Con l’API, invece, il campo kids di ogni commento ti dà gli ID dei figli e puoi semplicemente attraversare l’albero in modo ricorsivo.
1def fetch_comments(item_id, depth=0, max_depth=3):
2 """Recupera ricorsivamente i commenti fino a 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# Esempio: recupera i commenti della top story
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"\nComments for: {top_story['title']}")
18 all_comments = []
19 for kid_id in top_story_full["kids"][:5]: # Primi 5 commenti di primo livello
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 "[no text]"
25 print(f"{indent}[{c['author']}] {preview}...")
Questo approccio ricorsivo è molto più semplice che cercare di interpretare i thread di commenti HTML annidati. Se ti servono alberi completi dei commenti, l’API è la scelta giusta.
Passo 4: esegui e visualizza i risultati
1python scrape_hn_api.py
Vedrai i dati strutturati delle storie seguiti da un’anteprima dei commenti annidati. I dati sono più puliti, l’accesso ai commenti è banale e non rischi che il tuo scraper si rompa perché HN ha cambiato il nome di una classe CSS.
Andare oltre la pagina 1: paginazione e dati storici
La maggior parte dei tutorial su HN si ferma alla pagina 1 — 30 storie. Va bene per una demo veloce, ma i casi d’uso reali spesso richiedono più profondità.
Estrarre più pagine con BeautifulSoup
La paginazione di HN usa un pattern URL semplice: ?p=2, ?p=3, ecc. Ogni pagina restituisce 30 storie, e il sito arriva fino a circa la pagina 20 (circa 600 storie totali). Oltre, ottieni pagine vuote.
1import time
2def scrape_hn_pages(num_pages=5):
3 """Estrae più pagine di storie della front page di HN."""
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"Page {page}: no stories found, stopping.")
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"Page {page}: scraped {len(story_rows)} stories")
23 # Rispetta il crawl-delay di 30 secondi indicato in robots.txt
24 if page < num_pages:
25 time.sleep(30)
26 return all_stories
27stories = scrape_hn_pages(5)
28print(f"\nTotal stories scraped: {len(stories)}")
Quel time.sleep(30) è importante. Il di HN richiede esplicitamente un crawl delay di 30 secondi. Ignorarlo può portare a rate limit (HTTP 429) o a un blocco temporaneo. Cinque pagine a intervalli di 30 secondi richiedono circa 2,5 minuti — non istantaneo, ma rispettoso.
Per chi non vuole gestire il codice della paginazione, gestisce automaticamente la paginazione a clic e quella infinite-scroll. Fa clic sul pulsante “More” in fondo alle pagine di HN senza alcuna configurazione.
Accedere ai dati storici di Hacker News con l’API Algolia
La Firebase API ti dà i dati attuali. Per analisi storiche — “Quali erano le migliori storie su Python nel 2023?” oppure “Come è cambiata la copertura sull’AI negli ultimi 5 anni?” — ti serve la .
1import requests
2ALGOLIA_BASE = "https://hn.algolia.com/api/v1"
3> This paragraph contains content that cannot be parsed and has been skipped.
4# Esempio: trova storie su Python scraping con 10+ punti da gennaio 2024
5results = search_hn(
6 query="python scraping",
7 tags="story",
8)
9print(f"Found {results['nbHits']} total results")
10for hit in results["hits"][:5]:
11 print(f" [{hit.get('points', 0)} pts] {hit['title']}")
Per query filtrate per data, usa numericFilters:
1import calendar, datetime
2# Storie dal 1° gennaio 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.
6L’API Algolia è veloce (5–9 ms di tempo di elaborazione lato server), non richiede chiave API e supporta la paginazione fino a 500 pagine. Per analisi storiche su larga scala è la migliore opzione disponibile.
7## Esportare i dati estratti da Hacker News in CSV, Excel e Google Sheets
8Ogni tutorial sullo scraping di HN che ho visto finisce con un output `pprint()` nel terminale. È utile per il debug, ma se stai costruendo un digest giornaliero o facendo analisi dei trend, i dati devono finire in un file. Ecco come farlo.
9### Esportare in CSV con Python
10```python
11import csv
12def export_to_csv(stories, filename="hn_stories.csv"):
13 """Salva le storie estratte in un file CSV."""
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"Saved {len(stories)} stories to {filename}")
20export_to_csv(stories)
Esportare in Excel con Python
1import pandas as pd
2def export_to_excel(stories, filename="hn_stories.xlsx"):
3 """Salva le storie estratte in un file Excel."""
4 df = pd.DataFrame(stories)
5 df.to_excel(filename, index=False, engine="openpyxl")
6 print(f"Saved {len(stories)} stories to {filename}")
7export_to_excel(stories)
Assicurati che openpyxl sia installato — pandas lo usa come motore per Excel. Se manca, riceverai un errore ImportError.
Inviare a Google Sheets (opzionale)
Per workflow automatizzati, potresti voler inviare i dati direttamente su Google Sheets usando la libreria gspread. Questo richiede la configurazione di un service account Google Cloud (un processo una tantum):
1import gspread
2gc = gspread.service_account(filename="service_account.json")
3sh = gc.open("HN Daily Digest")
4worksheet = sh.sheet1
5# Converti le storie in righe
6header = list(stories[0].keys())
7rows = [list(s.values()) for s in stories]
8worksheet.clear()
9worksheet.update([header] + rows)
10print("Pushed to Google Sheets")
L’alternativa no-code per l’esportazione
Se configurare service account e scrivere codice di esportazione ti sembra più lavoro dello scraping stesso, ti capisco. In Thunderbit abbiamo creato un’esportazione dati gratuita che ti permette di inviare i dati estratti direttamente a Excel, Google Sheets, Airtable o Notion — senza codice, senza credenziali, senza pipeline da mantenere. Per un’estrazione occasionale, è davvero più veloce. Più avanti ti spiego meglio.
Rendere lo scraper pronto per la produzione: gestione errori, cache e scheduling
Se esegui uno scraper una sola volta per curiosità, il codice sopra va benissimo. Se invece lo usi ogni giorno all’interno di un flusso di lavoro, servono alcuni componenti in più.
Gestione errori e logica di retry
Le reti falliscono. I server limitano le richieste. Una singola richiesta sbagliata non dovrebbe mandare in crash tutto lo scraping. Ecco una funzione di retry con backoff esponenziale:
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 """Recupera un URL con retry automatici e backoff esponenziale."""
6 response = requests.get(url, timeout=10)
7 response.raise_for_status()
8 return response
9# Uso:
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"Failed after retries: {e}")
La libreria tenacity gestisce bene la logica di retry. Ritenta fino a 5 volte con backoff esponenziale e jitter — partendo da 1 secondo fino a un massimo di 60 secondi. Questo gestisce in modo elegante HTTP 429 (rate limit), 503 (servizio non disponibile) ed errori di rete transitori.
Mettere in cache le risposte per evitare di ricrawlarle
Durante lo sviluppo eseguirai lo scraper molte volte mentre modifichi la logica di parsing. Senza cache, ogni esecuzione colpisce di nuovo i server di HN per gli stessi dati. La libreria requests-cache risolve tutto in due righe:
1import requests_cache
2requests_cache.install_cache("hn_cache", expire_after=3600) # Cache per 1 ora
Dopo aver aggiunto queste righe all’inizio dello script, tutte le chiamate requests.get() vengono automaticamente memorizzate in una base dati SQLite locale. Riesegui lo script 10 volte in un’ora, e solo la prima esecuzione andrà davvero in rete. È uno strumento che , e per ottime ragioni.
Separare il crawling dal parsing
Un pattern che gli scraper esperti consigliano sempre: prima scarica i dati grezzi, poi esegui il parsing. In questo modo, se la logica di parsing ha un bug, puoi correggerla e riprocessare senza rifare il fetch.
1import os, json
2def crawl_and_save(story_ids, output_dir="raw_data"):
3 """Recupera i dati delle storie e salva il JSON grezzo su disco."""
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 # Salta gli elementi già recuperati
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.
13Questo approccio in due fasi è particolarmente utile quando estrai centinaia di elementi e vuoi iterare rapidamente sul modo in cui processi i dati.
14### Automatizzare lo scraper con una pianificazione
15Per un digest giornaliero di HN, lo scraper deve girare automaticamente. Due opzioni comuni:
16**Opzione 1: cron (Linux/Mac)**
17```bash
18# Esegui ogni giorno alle 8:30 UTC
1930 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1
Opzione 2: GitHub Actions (gratis, senza server)
1name: Scrape Hacker News
2on:
3 schedule:
4 - cron: '30 8 * * *' # Ogni giorno alle 8:30 UTC
5 workflow_dispatch: # Pulsante di esecuzione manuale
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
Alcuni dettagli da tenere a mente con la pianificazione su GitHub Actions: tutti gli orari cron sono in UTC, sono comuni ritardi di 15–60 minuti (usa orari non “tondi” come :30 invece di :00) e GitHub può disattivare i workflow pianificati nei repository inattivi per 60 giorni. Includi sempre workflow_dispatch così puoi avviare tutto manualmente durante i test.
Per un’opzione più semplice, la funzione Scheduled Scraper di Thunderbit ti consente di descrivere la pianificazione in linguaggio naturale — qualcosa come “estrai ogni mattina alle 8” — senza server né configurazione cron.
Quando Python è eccessivo: il modo no-code per estrarre Hacker News
Voglio essere onesto, anche se sono un appassionato di Python e il mio team costruisce strumenti per sviluppatori. Se ti servono solo le prime 100 storie di HN di oggi in un foglio di calcolo — subito, una sola volta — scrivere, debuggare ed eseguire uno script Python è un overhead inutile. Solo la configurazione iniziale (ambiente virtuale, installazione dei pacchetti, individuazione dei selettori) richiede più tempo della raccolta dati vera e propria.
È qui che entra in gioco . Ecco il flusso di lavoro:
- Apri
news.ycombinator.comin Chrome - Clicca l’icona dell’estensione Thunderbit, poi “AI Suggest Fields”
- L’AI legge la pagina e propone le colonne: Titolo, URL, Punteggio, Autore, Numero commenti, Orario di pubblicazione
- Modifica i campi se vuoi (rinomina, rimuovi o aggiungi campi personalizzati — puoi anche aggiungere un prompt AI come “Classifica come AI/DevTools/Web/Altro”)
- Clicca “Scrape” — i dati compaiono in una tabella strutturata
- Esporta in Excel, Google Sheets, Airtable o Notion
Due clic per arrivare ai dati strutturati. Nessun selettore, nessun codice, nessuna manutenzione.
Un vantaggio concreto: l’AI di Thunderbit si adatta automaticamente ai cambiamenti di layout. I classici scraper basati su selettori CSS si rompono quando un sito modifica il markup — e anche se l’HTML di HN è abbastanza stabile, è cambiato nel tempo (la classe class="athing submission" è stata aggiornata, span.titleline ha sostituito il vecchio a.storylink). Uno scraper AI legge la pagina ogni volta da zero, quindi non dipende dai nomi delle classi.

Thunderbit gestisce anche la paginazione (cliccando automaticamente il pulsante “More” di HN) e lo scraping delle sottopagine (visitando la pagina dei commenti di ogni storia per raccogliere i dati della discussione). Per il caso d’uso di , è l’equivalente del codice API ricorsivo del Metodo 2 — ma senza scrivere una sola riga.
I compromessi sono semplici: Python è la scelta giusta quando hai bisogno di logiche personalizzate, trasformazioni complesse dei dati, pipeline automatizzate pianificate o stai imparando a programmare. Thunderbit è la scelta giusta quando ti servono dati subito, non vuoi mantenere codice o non sei uno sviluppatore. Scegli lo strumento che corrisponde alla tua situazione.
Python vs API vs No-Code: quale metodo scegliere?
Ecco il quadro decisionale completo:
| Criterio | BeautifulSoup (HTML) | Firebase API | Algolia API | Thunderbit (No-Code) |
|---|---|---|---|---|
| Competenze tecniche richieste | Python intermedio | Python base | Python base | Nessuna |
| Tempo di configurazione | 10–15 min | 5–10 min | 5–10 min | 2 min |
| Manutenzione | Media (i selettori possono rompersi) | Bassa (JSON stabile) | Bassa (JSON stabile) | Nessuna |
| Profondità dei dati | Solo front page | Qualsiasi item, utenti | Ricerca + storico | Front page + sottopagine |
| Commenti | Difficile | Facile (ricorsivo) | Facile (albero annidato) | Scraping delle sottopagine |
| Dati storici | No | No | Sì (archivio completo) | No |
| Opzioni di export | Da programmare | Da programmare | Da programmare | Integrato (Excel, Sheets, ecc.) |
| Scheduling | cron / GitHub Actions | cron / GitHub Actions | cron / GitHub Actions | Pianificatore integrato |
| Ideale per | Imparare lo scraping | Pipeline affidabili | Ricerca e analisi | Estrazioni rapide |
Se stai imparando Python o stai costruendo qualcosa di personalizzato, scegli il Metodo 1 o 2. Se hai bisogno di analisi storiche, aggiungi l’Algolia API. Se vuoi solo i dati senza codice, .
Conclusione e punti chiave
Ecco cosa hai ora nel tuo toolkit:
- Due metodi Python completi per estrarre Hacker News — BeautifulSoup per il parsing HTML e la Firebase API per dati JSON puliti
- Tecniche di paginazione per andare oltre la pagina 1, inclusa l’Algolia API per dati storici dal 2007 in poi
- Codice di export per CSV, Excel e Google Sheets — perché i dati nel terminale non servono a nessuno nel team
- Pattern pronti per la produzione — retry logic, cache, separazione tra crawling e parsing e automazione pianificata con cron o GitHub Actions
- Un’alternativa no-code per i casi in cui Python è più strumento del necessario
Il mio consiglio: parti dalla Firebase API (Metodo 2) per la maggior parte dei casi d’uso. È più pulita, più affidabile e ti permette di accedere ai commenti senza dover affrontare il parsing dell’HTML annidato. Aggiungi l’Algolia API quando hai bisogno di dati storici. E tieni tra i preferiti per quei momenti in cui ti serve solo un foglio di calcolo veloce e non vuoi avviare un intero progetto Python.
Se vuoi andare più a fondo, prova a estrarre i commenti di HN per , crea una pipeline di digest giornaliero con GitHub Actions o esplora l’Algolia API per capire come sono cambiati i trend tecnologici nell’ultimo decennio.
FAQ
È legale estrarre dati da Hacker News?
I dati di HN sono pubblicamente disponibili e Y Combinator offre un’API ufficiale proprio per l’accesso programmatico. Il del sito consente lo scraping dei contenuti di sola lettura (front page, pagine item, pagine utente) ma richiede un crawl delay di 30 secondi. Rispetta il delay, non estrarre endpoint interattivi (votazioni, login) e sei in una posizione solida. Per approfondire l’etica dello scraping, consulta la nostra guida sulle .
Hacker News ha un’API ufficiale?
Sì. La su hacker-news.firebaseio.com/v0/ è gratuita, non richiede autenticazione e offre accesso a storie, commenti, profili utente e a tutti i tipi di feed (top, new, best, ask, show, jobs). Restituisce JSON pulito e non ha un rate limit dichiarato, anche se è sempre consigliabile usare una frequenza di richieste ragionevole.
Come faccio a estrarre i commenti di Hacker News con Python?
Usando la Firebase API, recupera un item di tipo story per ottenere il campo kids (un array con gli ID dei commenti di primo livello). Ogni commento è a sua volta un item con il proprio campo kids per le risposte. Attraversa l’albero in modo ricorsivo con una funzione che recupera ogni commento e i suoi figli. Vedi sopra la sezione “Estrarre i commenti (visita ricorsiva dell’albero)” per il codice completo. In alternativa, l’endpoint /items/<id> della restituisce l’intero albero annidato dei commenti in una sola richiesta — molto più veloce per le storie con molti commenti.
Posso estrarre Hacker News senza scrivere codice?
Sì. L’ funziona come estensione Chrome: apri HN, clicca “AI Suggest Fields” e identifica automaticamente colonne come titolo, URL, punteggio e autore. Clicca “Scrape” ed esporta direttamente su Excel, Google Sheets, Airtable o Notion. Gestisce anche la paginazione e può visitare le sottopagine per raccogliere i dati dei commenti. Niente Python, niente selettori, niente manutenzione.
Come ottengo dati storici di Hacker News?
La è lo strumento migliore per questo. Usa l’endpoint search_by_date con numericFilters=created_at_i>TIMESTAMP per filtrare per intervallo di date. Puoi cercare per parola chiave, filtrare per tipo di storia e paginare fino a 500 pagine di risultati. Per analisi storiche su larga scala, sono disponibili anche dataset pubblici su (archivio completo), (28 milioni di record) e (4 milioni di storie).
Scopri di più