Hace unos meses, quise montar un resumen diario con las noticias más destacadas de Hacker News para nuestro equipo en Thunderbit. Mi primera idea fue simplemente guardar la página en favoritos y revisarla cada mañana. Eso me aguantó unos tres días, hasta que me di cuenta de que estaba perdiendo 20 minutos al día solo leyendo titulares y copiando enlaces a una hoja de cálculo.
Hacker News es una de las fuentes más densas y valiosas de inteligencia tecnológica en internet: recibe alrededor de , se publican unas 1,300 historias nuevas al día y se generan cerca de 13,000 comentarios diarios. Tanto si sigues tendencias tecnológicas emergentes, monitorizas tu marca, montas un pipeline de reclutamiento a partir de hilos de "Who's Hiring" o simplemente quieres estar al tanto de lo que le importa al mundo developer, intentar hacerlo todo a mano es una batalla perdida.
La buena noticia: extraer datos de Hacker News con Python es más fácil de lo que parece. En esta guía te mostraré dos métodos completos: scraping HTML con BeautifulSoup y la API oficial de HN Firebase. También verás paginación, exportación de datos, patrones listos para producción y una alternativa sin código para cuando Python se te queda grande.
¿Por qué extraer Hacker News con Python?
Hacker News no es un agregador de enlaces más. Es un feed curado por la comunidad, donde las historias tecnológicas más interesantes suben gracias a los votos y a la conversación activa. Su audiencia está muy orientada a profesionales de tecnología (aproximadamente ), y el 66% de tráfico directo del sitio deja claro que se trata de una comunidad fiel y habitual, no de visitantes de paso.
Estas son algunas razones por las que la gente extrae datos de HN:
| Caso de uso | Lo que obtienes |
|---|---|
| Resumen diario de tecnología | Historias destacadas, puntuaciones y enlaces enviados a tu correo o Slack |
| Monitoreo de marca/competencia | Alertas cuando se menciona tu empresa o producto |
| Análisis de tendencias | Seguimiento de tecnologías, lenguajes o temas que ganan tracción con el tiempo |
| Reclutamiento | Extracción de hilos de "Who's Hiring" para vacantes, stacks tecnológicos y señales salariales |
| Investigación de contenido | Temas que funcionan bien para escribir o compartir |
| Análisis de sentimiento | Medición de la opinión de la comunidad sobre productos, lanzamientos o cambios del sector |
Empresas con un valor conjunto superior a 400 mil millones de dólares — Stripe, Dropbox y Airbnb — atribuyen a Hacker News una parte clave de sus primeros usuarios y comentarios. Drew Houston publicó la demo de Dropbox en HN en abril de 2007, llegó al puesto #1 y la lista de espera de la beta pasó de 5,000 a 75,000 usuarios en un solo día. Los datos de HN no solo son interesantes: también tienen valor comercial.
Los datos son públicos, pero la estructura del sitio hace que recogerlos a mano sea un trabajo pesado. La automatización con Python es la salida práctica.
Dos formas de extraer Hacker News con Python: resumen
Esta guía cubre dos enfoques completos y ejecutables:
- Scraping HTML con
requests+ BeautifulSoup — baja el HTML bruto de news.ycombinator.com y lo analiza para sacar los datos de cada historia. Ideal para aprender los fundamentos del scraping y capturar exactamente lo que aparece en la página. - La API oficial de Hacker News Firebase — consulta directamente endpoints JSON, sin necesidad de parsear HTML. Es mejor para canales de datos confiables, acceso a comentarios y datos históricos.
Aquí tienes una comparación lado a lado para ayudarte a elegir el enfoque que mejor se adapta a tus necesidades:
| Criterio | Scraping HTML (requests + BS4) | API de HN Firebase | Thunderbit (sin código) |
|---|---|---|---|
| Complejidad de configuración | Media (parsear selectores HTML) | Baja (endpoints JSON) | Ninguna (extensión de Chrome en 2 clics) |
| Actualización de datos | Tiempo real en la portada | Tiempo real (cualquier elemento por ID) | Tiempo real |
| Riesgo de límites de tasa | Medio (robots.txt indica 30 s de espera) | Bajo (oficial, generoso) | Gestionado por Thunderbit |
| Acceso a comentarios | Difícil (HTML anidado) | Fácil (IDs de elementos recursivos) | Función de scraping de subpáginas |
| Datos históricos | Limitados | A través de la API de búsqueda de Algolia | N/A |
| Mejor para | Aprender fundamentos del scraping | Canales de datos confiables | Personas no técnicas, exportaciones rápidas |
Ambos métodos incluyen código Python completo y ejecutable. Y si solo quieres los datos sin escribir nada de código, también te enseño cómo hacerlo.
Antes de empezar
- Nivel: Principiante a intermedio
- Tiempo necesario: ~15–20 minutos por método
- Lo que vas a necesitar:
- Python 3.11+ instalado
- Una terminal o editor de código
- Navegador Chrome (si quieres inspeccionar el HTML de HN o probar la opción sin código)
- (opcional, para el método sin código)

Configurar tu entorno de Python
Antes de tocar datos de HN, vamos a dejar listo el entorno. Te recomiendo crear un entorno virtual para mantener limpias las dependencias del proyecto.
1# Crear y activar un entorno virtual
2python3 -m venv hn-scraper
3# macOS/Linux:
4source hn-scraper/bin/activate
5# Windows:
6hn-scraper\Scripts\activate
7# Instalar los paquetes que necesitaremos para ambos métodos
8pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5
Para los patrones de producción más adelante (caché, reintentos), también te conviene instalar:
1pip install requests-cache==1.3.1 tenacity==9.1.4
Sin claves API especiales, sin tokens de autenticación. Los datos de HN son abiertos.
Método 1: extraer Hacker News con Python usando BeautifulSoup
Este es el enfoque clásico: descargar el HTML, analizarlo y sacar los datos que necesitas. Es la forma en que la mayoría aprende web scraping, y la estructura sencilla de HN basada en tablas lo convierte en un excelente campo de práctica.
Paso 1: obtener la portada de Hacker News
Abre tu editor y crea un archivo llamado scrape_hn_bs4.py. Este es el código inicial:
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")
Ejecuta el script. Deberías ver Status: 200 y una longitud de página de unas 40,000–50,000 caracteres. Ese es el HTML crudo de la portada de HN cargado en memoria y listo para analizar.
Paso 2: entender la estructura HTML
HN usa una estructura basada en tablas, no un grid moderno ni flexbox. Cada historia de la página se compone de dos filas <tr> clave:
- La fila de la historia (
<tr class="athing submission">): contiene la posición, el título y el enlace - La fila de metadatos (la siguiente fila
<tr>): contiene puntos, autor, hora y número de comentarios
Los selectores importantes son:
span.titleline > a— título y URL de la historiaspan.score— cantidad de votos (por ejemplo, "118 points")a.hnuser— nombre de usuario del autorspan.age— hora de publicación- El último
<a>en.subtextque contenga la palabra "comment" — número de comentarios
Si haces clic derecho sobre cualquier título de HN en Chrome y eliges "Inspect", verás algo así:
1<span class="titleline">
2 <a href="https://darkbloom.dev">Darkbloom – Private inference on idle Macs</a>
3</span>
Y debajo, la fila de metadatos:
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>
Entender estos selectores es fundamental: si HN cambia su marcado, tendrás que ajustarlos. (Spoiler: el método con API evita ese problema por completo.)
Paso 3: extraer títulos, enlaces y puntuaciones
Ahora viene el trabajo real. Vamos a recorrer cada fila de historia, sacar el título y el enlace de la fila principal, y después obtener la puntuación de la fila de metadatos justo debajo.
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 # Título y URL desde la fila de la historia
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 # Metadatos desde la siguiente fila hermana
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# Filtrar historias con 50+ puntos, ordenadas por puntuación
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])
Algunas notas sobre el código:
- El operador walrus (
:=) funciona en Python 3.8+. Te permite asignar y comprobar en una sola línea, algo muy útil para elementos opcionales comospan.score, que pueden no existir en todas las filas (por ejemplo, las publicaciones de empleo no tienen puntuación). - HN usa
\xa0(espacio de no separación) entre el número y la palabra "comments", así que lo separamos por ahí. - Las historias que enlazan a otras páginas de HN (como los posts "Ask HN") tendrán URLs relativas que empiezan con
item?id=. Quizá quieras anteponerhttps://news.ycombinator.com/en esos casos.
Paso 4: ejecutarlo y ver los resultados
Guarda y ejecuta:
1python scrape_hn_bs4.py
Deberías ver una salida como esta:
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 ...]
Eso corresponde a 30 historias de la página 1. Pero HN tiene cientos de historias activas en cualquier momento. Más adelante veremos cómo manejar la paginación.
Método 2: extraer Hacker News con Python usando la API oficial
La API de HN Firebase es la forma oficialmente admitida de acceder a los datos de Hacker News. Sin autenticación, sin claves API y sin parsing de HTML. Obtienes respuestas JSON limpias. Yo uso este método para cualquier cosa que tenga que funcionar de forma fiable en producción.
Endpoints clave de la API que debes conocer
La URL base es https://hacker-news.firebaseio.com/v0/. Estos son los endpoints importantes:
This paragraph contains content that cannot be parsed and has been skipped.
Un elemento de historia se ve así:
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}
El campo kids contiene los IDs de los comentarios hijos directos. Cada comentario es a su vez un elemento que puede tener sus propios kids; así se estructura el árbol de comentarios.
Paso 1: obtener los IDs de las historias destacadas
Crea un archivo llamado scrape_hn_api.py:
1import requests
2import time
3from pprint import pprint
4API_BASE = "https://hacker-news.firebaseio.com/v0"
5# Obtener los IDs de las historias destacadas
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 IDs en una sola petición: sin parsing, sin selectores, solo una matriz JSON.
Paso 2: obtener los detalles de cada historia por ID
Ahora necesitamos los datos reales. Aquí aparece el problema de dispersión: 500 historias implican 500 llamadas API individuales. En mis pruebas, cada solicitud de elemento tarda unos 1.2 segundos de forma secuencial. Para 500 historias, eso ronda los 10 minutos.
Para la mayoría de los casos no necesitas las 500. Aquí tienes código para obtener las 30 primeras:
1def fetch_story(story_id):
2 """Obtiene los detalles de una historia desde la API de 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# Ordenar por puntuación y mostrar las 10 mejores
7top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
8pprint(top)
El time.sleep(0.1) añade una pequeña pausa de cortesía. La API Firebase no declara un límite de tasa específico, pero bombardear cualquier API sin pausas es mala práctica.
Paso 3: extraer comentarios (recorrido recursivo del árbol)
Aquí es donde la API realmente brilla frente al scraping HTML. Los comentarios de HN están profundamente anidados: respuestas a respuestas a respuestas. En HTML, eso significa analizar estructuras de tablas complejas. Con la API, cada comentario tiene un campo kids con los IDs de sus hijos, y simplemente recorres el árbol de forma recursiva.
1def fetch_comments(item_id, depth=0, max_depth=3):
2 """Obtiene comentarios recursivamente hasta 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# Ejemplo: obtener comentarios de la historia principal
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]: # Primeros 5 comentarios de nivel superior
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}...")
Este enfoque recursivo es mucho más sencillo que intentar parsear hilos de comentarios HTML anidados. Si necesitas árboles completos de comentarios, la API es el camino correcto.
Paso 4: ejecutar y ver resultados
1python scrape_hn_api.py
Verás datos estructurados de las historias seguidos de una vista previa de comentarios anidados. Los datos son más limpios, el acceso a comentarios es trivial y no existe el riesgo de que tu scraper falle porque HN cambió el nombre de una clase CSS.
Ir más allá de la página 1: paginación y datos históricos
La mayoría de los tutoriales sobre scraping de HN se quedan en la página 1, con 30 historias. Eso está bien para una demo rápida, pero en casos reales a menudo se necesita más profundidad.
Extraer varias páginas con BeautifulSoup
La paginación de HN usa un patrón de URL muy simple: ?p=2, ?p=3, etc. Cada página devuelve 30 historias, y el sitio ofrece hasta la página 20 aproximadamente (unos 600 resultados en total). Más allá de eso, obtendrás páginas vacías.
1import time
2def scrape_hn_pages(num_pages=5):
3 """Extrae varias páginas de historias de la portada de 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 # Respetar el crawl-delay de 30 segundos indicado en 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)}")
Ese time.sleep(30) es importante. El de HN pide explícitamente un retraso de rastreo de 30 segundos. Si lo ignoras, podrías recibir limitación de tasa (HTTP 429) o un bloqueo temporal. Cinco páginas con intervalos de 30 segundos tardan unos 2.5 minutos: no es instantáneo, pero sí respetuoso.
Para quienes no quieran gestionar código de paginación, maneja automáticamente la paginación por clic y el scroll infinito. Hace clic en el botón "More" al final de las páginas de HN sin que tengas que configurar nada.
Acceder a datos históricos de Hacker News con la API de Algolia
La API de Firebase te da datos actuales. Para análisis históricos — "¿Cuáles fueron las mejores historias sobre Python en 2023?" o "¿Cómo cambió la cobertura sobre IA en los últimos 5 años?" — necesitas 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# Ejemplo: encontrar historias sobre scraping en Python con 10+ puntos desde enero de 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']}")
Para consultas filtradas por fecha, usa numericFilters:
1import calendar, datetime
2# Historias desde el 1 de enero de 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.
6La API de Algolia es rápida (5–9 ms de tiempo de procesamiento en el servidor), no requiere clave API y admite paginación de hasta 500 páginas. Para análisis histórico a gran escala, es la mejor opción disponible.
7## Exportar datos de Hacker News a CSV, Excel y Google Sheets
8Todos los tutoriales de scraping de HN que he visto terminan con salida `pprint()` en la terminal. Eso sirve para depurar, pero si estás montando un resumen diario o analizando tendencias, necesitas los datos en un archivo. Así es como puedes hacerlo.
9### Exportar a CSV con Python
10```python
11import csv
12def export_to_csv(stories, filename="hn_stories.csv"):
13 """Guarda las historias extraídas en un archivo 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)
Exportar a Excel con Python
1import pandas as pd
2def export_to_excel(stories, filename="hn_stories.xlsx"):
3 """Guarda las historias extraídas en un archivo de 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)
Asegúrate de tener instalado openpyxl, porque pandas lo usa como motor para Excel. Si falta, te saldrá un ImportError.
Enviar a Google Sheets (opcional)
Para flujos de trabajo automatizados, quizá quieras enviar los datos directamente a Google Sheets usando la librería gspread. Esto requiere configurar una cuenta de servicio de Google Cloud, pero solo una vez:
1import gspread
2gc = gspread.service_account(filename="service_account.json")
3sh = gc.open("HN Daily Digest")
4worksheet = sh.sheet1
5# Convertir stories a filas
6header = list(stories[0].keys())
7rows = [list(s.values()) for s in stories]
8worksheet.clear()
9worksheet.update([header] + rows)
10print("Pushed to Google Sheets")
La alternativa sin código para exportar
Si montar cuentas de servicio y escribir código de exportación te parece más trabajo que el propio scraping, te entiendo. En Thunderbit, creamos exportación de datos gratuita para enviar lo extraído directamente a Excel, Google Sheets, Airtable o Notion, sin código, sin credenciales y sin canalizaciones que mantener. Para una extracción puntual, de verdad es más rápido. Más abajo te cuento más.
Hacer que tu scraper esté listo para producción: manejo de errores, caché y programación
Si ejecutas un scraper una sola vez por curiosidad, el código anterior está bien. Si lo ejecutas cada día como parte de un flujo de trabajo, necesitas algunas piezas más.
Manejo de errores y lógica de reintento
Las redes fallan. Los servidores limitan. Una sola petición mala no debería tumbar todo el scraping. Aquí tienes una función de reintento con retroceso exponencial:
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 """Obtiene una URL con reintentos automáticos y retroceso exponencial."""
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 librería tenacity gestiona muy bien la lógica de reintento. Reintentará hasta 5 veces con retroceso exponencial con jitter, empezando en 1 segundo y llegando hasta 60 segundos. Esto maneja de forma elegante errores HTTP 429 (límite de tasa), 503 (servicio no disponible) y fallos de red transitorios.
Cachear respuestas para evitar volver a rastrear
Durante el desarrollo, ejecutarás el scraper muchas veces mientras ajustas la lógica de parsing. Sin caché, cada ejecución volverá a golpear los servidores de HN con los mismos datos. La librería requests-cache lo arregla en dos líneas:
1import requests_cache
2requests_cache.install_cache("hn_cache", expire_after=3600) # Cache durante 1 hora
Después de añadir esas líneas al inicio del script, todas las llamadas requests.get() se almacenarán automáticamente en una base de datos SQLite local. Si vuelves a ejecutar el script 10 veces en una hora, solo la primera ejecución hará la petición real a la red. Esta es una herramienta que , y con razón.
Separar el rastreo del parsing
Un patrón que los scrapers con experiencia recomiendan mucho: primero descargar los datos crudos y después analizarlos. Así, si tu lógica de parsing tiene un error, puedes corregirla y volver a procesar sin tener que descargar todo otra vez.
1import os, json
2def crawl_and_save(story_ids, output_dir="raw_data"):
3 """Obtiene datos de historias y guarda el JSON crudo en 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 # Saltar elementos ya obtenidos
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.
13Este enfoque en dos fases es especialmente útil cuando extraes cientos de elementos y quieres iterar rápido sobre la forma en que procesas los datos.
14### Automatizar tu scraper con un horario
15Para un resumen diario de HN, necesitas que el scraper se ejecute automáticamente. Dos opciones comunes:
16**Opción 1: cron (Linux/Mac)**
17```bash
18# Ejecutar todos los días a las 8:30 AM UTC
1930 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1
Opción 2: GitHub Actions (gratis, sin servidor)
1name: Scrape Hacker News
2on:
3 schedule:
4 - cron: '30 8 * * *' # Diariamente a las 8:30 AM UTC
5 workflow_dispatch: # Botón de ejecución manual
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
Unos cuantos detalles a tener en cuenta con la programación en GitHub Actions: todas las horas cron usan UTC, son comunes los retrasos de 15 a 60 minutos (usa horas con minuto no exacto como :30 en lugar de :00) y GitHub puede desactivar workflows programados en repositorios sin actividad durante 60 días. Incluye siempre workflow_dispatch para poder ejecutarlo manualmente en pruebas.
Para una opción más simple, la función Scheduled Scraper de Thunderbit te permite describir el horario en lenguaje natural — algo como "scrape every morning at 8am" — sin servidor ni configuración de cron.
Cuando Python es demasiado: la forma sin código de extraer Hacker News
Voy a ser sincero, aunque me encanta Python y mi equipo crea herramientas para desarrolladores. Si solo necesitas las 100 historias más destacadas de hoy en una hoja de cálculo — ahora mismo, una sola vez — escribir, depurar y ejecutar un script de Python es una carga innecesaria. Solo la configuración (entorno virtual, instalación de paquetes, averiguar selectores) tarda más que la propia recopilación de datos.
Aquí es donde encaja . El flujo es este:
- Abre
news.ycombinator.comen Chrome - Haz clic en el icono de la extensión Thunderbit y luego en "AI Suggest Fields"
- La IA analiza la página y propone columnas: Title, URL, Score, Author, Comment Count, Time Posted
- Ajusta los campos si quieres (renombrar, quitar o añadir personalizados; incluso puedes agregar una instrucción como "Categorize as AI/DevTools/Web/Other")
- Haz clic en "Scrape" y los datos aparecen en una tabla estructurada
- Exporta a Excel, Google Sheets, Airtable o Notion
Dos clics para obtener datos estructurados. Sin selectores, sin código, sin mantenimiento.
Una ventaja real: la IA de Thunderbit se adapta automáticamente a cambios de diseño. Los scrapers tradicionales basados en selectores CSS se rompen cuando un sitio cambia su marcado, y aunque el HTML de HN es bastante estable, sí ha cambiado (la clase class="athing submission" se actualizó y span.titleline sustituyó al antiguo a.storylink). Un scraper impulsado por IA relee la página cada vez, así que no le afectan los cambios de nombres de clase.

Thunderbit también gestiona la paginación (haciendo clic automáticamente en el botón "More" de HN) y el scraping de subpáginas (visitando la página de comentarios de cada historia para extraer la discusión). Para el caso de uso de , eso equivale al código recursivo de la API del Método 2, pero sin escribir una sola línea.
Las diferencias son claras: Python es la mejor opción cuando necesitas lógica personalizada, transformaciones complejas de datos, automatizaciones programadas o estás aprendiendo a programar. Thunderbit es la mejor opción cuando necesitas datos rápido, no quieres mantener código o no eres desarrollador. Elige la herramienta que encaje con tu situación.
Python vs. API vs. sin código: ¿qué método deberías elegir?
Aquí tienes el marco de decisión completo:
| Criterio | BeautifulSoup (HTML) | API Firebase | API Algolia | Thunderbit (sin código) |
|---|---|---|---|---|
| Nivel técnico necesario | Python intermedio | Python básico | Python básico | Ninguno |
| Tiempo de configuración | 10–15 min | 5–10 min | 5–10 min | 2 min |
| Mantenimiento | Medio (los selectores se rompen) | Bajo (JSON estable) | Bajo (JSON estable) | Ninguno |
| Profundidad de datos | Solo portada | Cualquier elemento, usuarios | Búsqueda + histórico | Portada + subpáginas |
| Comentarios | Difícil | Fácil (recursivo) | Fácil (árbol anidado) | Scraping de subpáginas |
| Datos históricos | No | No | Sí (archivo completo) | No |
| Opciones de exportación | Programarlas tú | Programarlas tú | Programarlas tú | Integradas (Excel, Sheets, etc.) |
| Programación | cron / GitHub Actions | cron / GitHub Actions | cron / GitHub Actions | Programador integrado |
| Mejor para | Aprender scraping | Canales fiables | Investigación y análisis | Extracciones rápidas |
Si estás aprendiendo Python o construyendo algo a medida, elige el Método 1 o 2. Si necesitas análisis histórico, añade la API de Algolia. Si solo quieres los datos sin escribir código, .
Conclusión y puntos clave
Esto es lo que ahora tienes en tu caja de herramientas:
- Dos métodos completos en Python para extraer Hacker News: BeautifulSoup para parsear HTML y la API de Firebase para obtener JSON limpio
- Técnicas de paginación para ir más allá de la página 1, incluida la API de Algolia para datos históricos que llegan hasta 2007
- Código de exportación para CSV, Excel y Google Sheets, porque los datos en una terminal no sirven mucho al resto de tu equipo
- Patrones listos para producción: reintentos, caché, separación entre rastreo y parsing, y automatización programada con cron o GitHub Actions
- Una alternativa sin código para cuando Python es más herramienta de la que realmente necesitas
Mi recomendación: empieza con la API de Firebase (Método 2) para la mayoría de los casos. Es más limpia, más fiable y te da acceso a comentarios sin el dolor de parsear HTML anidado. Añade la API de Algolia cuando necesites datos históricos. Y guarda en favoritos para esos momentos en los que solo necesitas una hoja de cálculo rápida y no quieres montar un proyecto de Python completo.
Si quieres profundizar más, prueba a extraer comentarios de HN para , crea un pipeline diario de resúmenes con GitHub Actions o explora la API de Algolia para ver cómo han cambiado las tendencias tecnológicas durante la última década.
Preguntas frecuentes
¿Es legal extraer Hacker News?
Los datos de HN son públicos, y Y Combinator ofrece una API oficial específicamente para acceso programático. El del sitio permite rastrear contenido de solo lectura (portada, páginas de elementos, páginas de usuarios), pero pide un retraso de 30 segundos. Si respetas ese retraso y no rastreas endpoints interactivos (votación, inicio de sesión), estás en terreno seguro. Para más información sobre ética en scraping, consulta nuestra guía sobre .
¿Hacker News tiene una API oficial?
Sí. La en hacker-news.firebaseio.com/v0/ es gratuita, no requiere autenticación y da acceso a historias, comentarios, perfiles de usuario y todos los tipos de feed (top, new, best, ask, show, jobs). Devuelve JSON limpio y no indica un límite de tasa específico, aunque siempre conviene ser prudente con la frecuencia de las solicitudes.
¿Cómo extraigo comentarios de Hacker News con Python?
Usando la API de Firebase, obtén un elemento de historia para acceder a su campo kids (una matriz con los IDs de los comentarios de nivel superior). Cada comentario es a su vez un elemento con su propio campo kids para las respuestas. Recorre el árbol de forma recursiva con una función que obtenga cada comentario y sus hijos. Mira la sección "Extraer comentarios (recorrido recursivo del árbol)" de arriba para ver el código completo. Como alternativa, el endpoint /items/<id> de la devuelve el árbol completo de comentarios anidados en una sola petición, mucho más rápido para historias con muchos comentarios.
¿Puedo extraer Hacker News sin escribir código?
Sí. El funciona como una extensión de Chrome: abre HN, haz clic en "AI Suggest Fields" y detectará automáticamente columnas como título, URL, puntuación y autor. Haz clic en "Scrape" y exporta directamente a Excel, Google Sheets, Airtable o Notion. También gestiona la paginación y puede visitar subpáginas para extraer datos de comentarios. Sin Python, sin selectores, sin mantenimiento.
¿Cómo obtengo datos históricos de Hacker News?
La es la mejor herramienta para esto. Usa el endpoint search_by_date con numericFilters=created_at_i>TIMESTAMP para filtrar por rango de fechas. Puedes buscar por palabra clave, filtrar por tipo de historia y paginar hasta 500 páginas de resultados. Para análisis históricos masivos, también hay conjuntos de datos públicos en (archivo completo), (28 millones de registros) y (4 millones de historias).
Saber más