Cómo crear y desplegar un scraper web de acciones: una guía práctica
TL;DR
Este proyecto recopila OHLCV diarios a través de una API de datos de mercado documentada, valida registros y los inserta en SQLite.
El fixture incluido funciona sin credenciales y contiene precios sintéticos, no observaciones de mercado.
Las fechas representan sesiones de mercado; el tiempo de recuperación, la moneda, el intercambio y la base de ajuste son campos separados.
La implementación utiliza un contenedor Docker de una sola vez y un programa externo, con una base de datos persistente y secretos fuera de la imagen.
El acceso a la API en vivo, los derechos de datos de mercado y la implementación del contenedor necesitan validación en su entorno. El fixture local no prueba derechos en vivo ni frescura en tiempo real.
Lo que construirás: Un recolector de datos del mercado de acciones
Construirás un recolector de datos del mercado diario: símbolo → API soportada → validación → SQLite → actualización programada. La misma estructura de ingeniería puede soportar una fuente HTML autorizada, pero una API documentada evita selectores frágiles de páginas de cotización.
El tutorial utiliza el contrato TIME_SERIES_DAILY documentado de Alpha Vantage. Estas son barras diarias crudas en lugar de precios ejecutables en vivo o una serie de rendimiento total ajustada automáticamente. Los datos pueden estar retrasados o ser históricos de acuerdo con los derechos de acceso de la fuente; este proyecto no es un feed de trading.
Requisitos y configuración del proyecto
Utiliza Python 3.12, el script y fixture proporcionados, y Docker si deseas la ruta de implementación. El proyecto de Python utiliza solo la biblioteca estándar.
El archivo de requisitos puede contener un comentario que indique que no hay dependencias de tiempo de ejecución de terceros. Una ejecución en vivo requiere una clave API de Alpha Vantage aprobada y una licencia de datos que cubra tu uso.
Elegir una fuente de datos de acciones confiable
Elige la fuente según derechos de acceso, cobertura de intercambio, definición de sesión, ajustes, presupuesto de tarifas y uso permitido. Una fuente que está bien para la investigación diaria interna puede no permitir la redistribución o la exhibición en tiempo real.
APIs del mercado de acciones vs. raspado de sitios web
Una API licenciada normalmente ofrece una forma de datos documentada y errores. Una página de cotización pública puede cargar datos dinámicamente, mostrar valores retrasados e imponer restricciones de acceso separadas.
La documentación de Alpha Vantage identifica campos de series diarias y parámetros de API. Sus términos distinguen derechos de uso; no asumas que un endpoint accesible otorga redistribución comercial.
Comprendiendo los campos de datos de acciones
Campo
Significado en este proyecto
Preocupación de validación
Símbolo e intercambio
Identidad de instrumento específica de la fuente
El mismo ticker corto puede identificar diferentes instrumentos
Fecha de sesión
Fecha de trading asociada con la barra diaria
No la fecha de recuperación UTC
Apertura/alto/bajo/cierre
Precios de sesión crudos
Positivos, finitos, internamente consistentes
Volumen
Unidades reportadas para el instrumento/sesión
No negativo; la definición de unidad depende de la fuente
Moneda
Metadatos de instrumento proporcionados explícitamente
No infieras solo de un símbolo
Base
raw_daily
Mantener separado de datos ajustados por división/dividendo
Observado en
Marca de tiempo UTC del recolector
Rastrea la frescura de la ingestión
La aritmética decimal evita cambios innecesarios de punto flotante binario en los precios almacenados. Los valores de moneda e intercambio son configuración en la demostración; valídalos a partir de tu maestro de instrumentos de producción.
Paso 1: Crea el proyecto de recolector de Python
Copia los archivos de código proporcionados en un directorio, crea un entorno virtual y ejecuta el fixture primero.
El fixture tiene dos barras inventadas dentro de un sobre con forma de API. Sus valores nunca deben mezclarse con un conjunto de datos del mercado de investigación o producción.
Paso 2: Obtén datos de acciones de la fuente
El recolector envía un símbolo explícito, función, tamaño de salida, tipo de datos y clave API. Aplica un tiempo de espera, maneja errores transitorios HTTP elegibles y evita registrar URL que contengan credenciales.
Después de confirmar tu derecho de acceso, ejecuta:
python stock_scraper.py --live--symbol IBM \--currency USD --exchange YOUR_VERIFIED_EXCHANGE --db stocks.sqlite
No se realizó ninguna llamada en vivo con credenciales para este artículo. Esta implementación completa está incluida en el paquete:
"""Recolector diario de OHLCV. La ejecución predeterminada requiere un fixture local explícito."""import argparse
import json
import logging
import os
import re
import sqlite3
import time
from datetime import date, datetime, timezone
from decimal import Decimal
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
deffetch(symbol):key = os.environ["ALPHAVANTAGE_API_KEY"] query = urlencode(dict(function="TIME_SERIES_DAILY", symbol=symbol, outputsize="compact", datatype="json", apikey=key))for attempt inrange(3):try: request = Request("https://www.alphavantage.co/query?"+ query, headers={"User-Agent":"DailyDataCollector/1.0"})with urlopen(request, timeout=30)as response:return json.load(response)except HTTPError as exc:# Never print exc or its URL: the query contains the API key.if exc.code notin(429,502,503,504)or attempt ==2:raise RuntimeError(f"Error HTTP de origen: {exc.code}")fromNone delay = exc.headers.get("Retry-After","")if delay:# Schedule a later run; never retry earlier than requested.raise RuntimeError("Limitado por tasa; reprogramar usando Retry-After")fromNoneexcept(URLError, TimeoutError):if attempt ==2:raise RuntimeError("Origen no disponible después de tres intentos")fromNone time.sleep(2** attempt)defnormalize(payload, symbol, currency, exchange):ifnot re.fullmatch(r"[A-Z]{3}", currency)ornot exchange.strip():raise ValueError("Proporcione un código de moneda validado e identificador de intercambio")if payload.get("Meta Data",{}).get("2. Symbol","").upper()!= symbol.upper():raise ValueError("Símbolo de proveedor faltante o incompatible") series = payload.get("Time Series (Daily)")ifnotisinstance(series,dict)ornot series:raise ValueError("No hay barras diarias; inspeccione los límites de la cuenta o el error de origen") observed = datetime.now(timezone.utc).isoformat() rows =[]for session, bar insorted(series.items()): date.fromisoformat(session) prices =[Decimal(bar[f"{i}. {name}"])for i, name in((1,"apertura"),(2,"alto"),(3,"bajo"),(4,"cierre"))]ifany(not p.is_finite()or p <=0for p in prices):raise ValueError(f"OHLC inválido: {session}") opening, high, low, close = prices
if low >min(opening, close)or high <max(opening, close)or low > high:raise ValueError(f" vela inconsistente: {session}") volume =int(bar["5. volume"])if volume <0:raise ValueError("Volumen negativo") rows.append((symbol, exchange, currency,"raw_daily", session,*(str(p)for p in prices), volume,"alpha_vantage", observed))return rows
defsave(rows, database):with sqlite3.connect(database)as db: db.execute("""CREATE TABLE IF NOT EXISTS bars(
symbol TEXT, exchange TEXT, currency TEXT, basis TEXT, session_date TEXT,
open TEXT, high TEXT, low TEXT, close TEXT, volume INTEGER,
source TEXT, observed_at TEXT,
PRIMARY KEY(symbol,exchange,currency,basis,session_date))""") db.executemany("""INSERT INTO bars VALUES(?,?,?,?,?,?,?,?,?,?,?,?)
ON CONFLICT(symbol,exchange,currency,basis,session_date) DO UPDATE SET
open=excluded.open,high=excluded.high,low=excluded.low,
close=excluded.close,volume=excluded.volume,observed_at=excluded.observed_at
""", rows)defmain(): parser = argparse.ArgumentParser() parser.add_argument("--symbol", default="IBM") parser.add_argument("--currency", required=True) parser.add_argument("--exchange", required=True) parser.add_argument("--fixture") parser.add_argument("--live", action="store_true") parser.add_argument("--db", default="stocks.sqlite") args = parser.parse_args()ifbool(args.fixture)== args.live: parser.error("Elija exactamente uno de --fixture o --live") payload = json.loads(Path(args.fixture).read_text())if args.fixture else fetch(args.symbol) rows = normalize(payload, args.symbol, args.currency, args.exchange) save(rows, args.db) logging.info("accepted_bars=%s latest_session=%s",len(rows), rows[-1][4])if __name__ =="__main__": logging.basicConfig(level=logging.INFO,format="%(levelname)s %(message)s")try: main()except(ValueError, KeyError, RuntimeError, OSError)as exc: logging.error("collector_failed: %s",type(exc).__name__)raise SystemExit(1)
El tamaño de respuesta compacto es deliberado. Limita una ejecución y admite actualizaciones repetidas; no elimina la necesidad de reconciliar correcciones históricas fuera de esa ventana.
Paso 3: Analizar y Validar Precios de Acciones
El analizador rechaza símbolos incompatibles, series ausentes, fechas inválidas, valores OHLC no finitos o no positivos, rangos de velas inconsistentes y volumen negativo. Distingue una sesión previa válida de una recuperación fallida.
No escriba la fecha de recuperación como la fecha de la sesión del mercado. Si la fuente no ha publicado una nueva barra porque el intercambio está cerrado, conserve la sesión anterior aceptada e informe su antigüedad en el contexto.
Para divisiones y dividendos, seleccione un contrato de datos ajustados separado si su análisis lo necesita. Mezclar cierres crudos y ajustados puede crear "cambios de precio" artificiales.
Paso 4: Guardar Datos de Acciones en CSV o SQLite
SQLite almacena una fila por símbolo, intercambio, moneda, base y fecha de sesión. La ingestión repetida actualiza la barra existente en lugar de crear duplicados.
El recolector preserva observed_at por separado. Para la auditoría de correcciones, amplíe el diseño con una tabla de revisión de fuente de solo adición; el ejemplo compacto mantiene los últimos valores aceptados.
Exporte un informe después de la colección si es necesario:
import csv
import sqlite3
with sqlite3.connect("stocks.sqlite")as db: cursor = db.execute("SELECT * FROM bars ORDER BY symbol, session_date")withopen("stocks.csv","w", newline="")as handle: writer = csv.writer(handle) writer.writerow([column[0]for column in cursor.description]) writer.writerows(cursor)
Paso 5: Agregar Registro, Reintentos y Manejo de Errores
Los reintentos están limitados y son solo para fallos transitorios. Una respuesta de Retry-After hace que el recolector se detenga y solicite una reprogramación en lugar de reintentar antes de lo permitido. Los mensajes de cuenta o cuota a nivel de JSON fallan la validación incluso si el estado HTTP es 200.
Registre las barras aceptadas, la última sesión, el tiempo transcurrido y una categoría de error redactada. Nunca registre la URL completa de la solicitud, el token de API o el contenido del entorno secreto. Preserve el último conjunto de datos aceptado si la última ejecución falla.
Paso 6: Programar Actualizaciones Automáticas de Datos de Acciones
Para datos diarios, programe después de que se espera que el proveedor publique la sesión completada, con un calendario de intercambio consciente del huso horario. Un cron de días laborables solo no conoce los días festivos del intercambio o los cambios de horario de verano.
Este horario host ilustrativo inicia una ejecución a las 23:15 en el huso horario configurado del host:
15 23 * * 1-5 /opt/stock/run-collector.sh
Ajuste el tiempo a su derecho y al intercambio. Evite trabajos superpuestos con un bloqueo del programador, y alerte si la última sesión esperada aún está ausente después de la ventana de publicación.
Paso 7: Desplegar el Raspador en Producción
Utilice un despliegue programado de un solo uso. Una salida de proceso exitosa significa que la colección configurada se completó; el programador externo es dueño de la próxima ejecución.
Dockerice el Raspador de Acciones
La imagen suministrada se ejecuta como un usuario no root y escribe datos en un directorio montado.
Docker no estaba disponible para una construcción de contenedor en este entorno de autoría; estas son instrucciones de despliegue, no una afirmación de un despliegue remoto exitoso.
Configure Variables de Entorno y Secretos
Coloque solo la clave de API requerida en un archivo de entorno con permisos restringidos o en un almacén de secretos. Manténgala fuera del contexto de construcción de la imagen y del repositorio. El .dockerignore suministrado excluye archivos de entorno y bases de datos.
Utilice una credencial y base de datos de prueba separadas durante las verificaciones de despliegue. Rote una clave si se expone en registros o en un archivo comprometido.
Configure Programación y Monitoreo
Para operaciones de monitoreo de precios, monitoree el estado de salida del proceso, la última ejecución exitosa, la última sesión esperada, el conteo de filas aceptadas y la accesibilidad de la base de datos. Mantenga las alertas de falla del proveedor distintas del cierre del mercado.
No adjunte un bucle de reinicio permanente a un trabajo exitoso de una sola vez. Permita que el programador reintente fallos elegibles con un presupuesto explícito y un bloqueo de superposición.
Pruebas del Raspador de Acciones Desplegado
La verificación del dispositivo local comprueba dos barras aceptadas, el rechazo de barras inválidas y ninguna fila extra después de una reejecución idéntica. No verifica la autorización de la fuente, precios en vivo, Docker o persistencia de VPS.
En su despliegue, verifique que la base de datos montada sobreviva al reemplazo del contenedor; un secreto está disponible solo en tiempo de ejecución; un trabajo repetido es idempotente; los errores de cuota producen una salida diferente de cero; y la restauración de la copia de seguridad funciona.
Escalando de Una Acción a Cientos de Tickers
Calcula tu presupuesto de solicitud antes de agregar concurrencia. Aplica controles de solicitud de producción y prefieres las interfaces de lote soportadas cuando estén disponibles, almacena el historial inalterado, escalona trabajos y mantiene un maestro de instrumentos verificado.
Particiona el estado por instrumento y sesión. Persiste el progreso parcial para que un símbolo fallido no repita cada solicitud exitosa. Un gran bucle contra un pequeño derecho producirá fallos de cuota en lugar de un rendimiento utilizable.
Problemas y Soluciones Comunes de Raspado de Acciones
Un JSON vacío puede ser un mensaje de límite de cuenta, no un mercado vacío. Una barra obsoleta puede ser un día festivo, un instrumento suspendido, un retraso en la publicación o una solicitud fallida; inspecciona la evidencia correcta.
Saltos inesperados pueden provenir de divisiones, cambios de moneda, reutilización de símbolos o mezcla de bruto/ajustado. Valida el evento y la identidad del instrumento antes de publicar un informe de anomalías.
Costos y Consideraciones Legales del Raspado de Datos de Acciones
Presupuesta el acceso a datos, derechos de redistribución o exhibición, computación, almacenamiento, respaldo, monitoreo y mantenimiento. Las credenciales de la API por sí solas no establecen todos los derechos de uso.
Si usas Nstdata Crawl para una página web financiera autorizada, trátala como infraestructura de adquisición. No es un feed de datos de mercado autorizado por la bolsa ni un sustituto para la validación de datos financieros.
Comienza con el fixture, confirma los metadatos del instrumento, revisa los derechos de origen, valida la respuesta de la cuenta en vivo, prueba la persistencia del almacenamiento y configura el monitoreo consciente del calendario. Pasa a múltiples instrumentos solo después de que una ejecución programada maneje correctamente tanto el éxito como el fracaso.
Valida las fuentes de página aprobadas
Inspecciona el contenido de la página web permitida por separado de los feeds de datos de mercado licenciados.
Sí, a través de una API autorizada o una fuente permitida. Este tutorial utiliza un contrato de API de datos diarios documentado y un fixture local.
P: ¿Puedo usar Yahoo Finanzas en su lugar?
Usa su acceso permitido actual y los acuerdos de licenciamiento de datos en lugar de asumir que una página pública permite automatización o redistribución sin restricciones.
P: ¿Es el ejemplo en tiempo real?
No. Recoge barras diarias; la frescura depende del derecho del proveedor y la publicación.
P: ¿Por qué la demostración requiere moneda y bolsa?
La respuesta seleccionada no es un maestro de instrumentos completo. El recolector evita adivinar esos campos a partir de un ticker.
P: ¿Con qué frecuencia deben ejecutarse las actualizaciones?
Hazlo coincidir con el derecho de origen y el intervalo de datos previsto. Solicitudes de barras diarias más frecuentes no convierten el conjunto de datos en un feed en vivo.
P: ¿Qué demuestra que el despliegue funciona?
Controles de aceptación con credenciales, almacenamiento persistente, ejecuciones idénticas, frescura correcta de la sesión de mercado y recuperación de fallos observable en tu propio entorno.
Lena Zhou
Oct. 19th 2026
Descubre Nstproxy Crawl -
Empieza Tu Prueba Gratuita Hoy
Rastrea sitios web completos con una sola solicitud API
Convierte cualquier sitio web en Markdown, HTML, JSON, enlaces, PDF y más, sin gestionar infraestructura de rastreo.
99,8% de éxito con renderizado JavaScript
Obtén datos limpios, listos para LLM, en múltiples formatos