Как создать и развернуть веб-скребок для акций: Практическое руководство
TL;DR
Этот проект собирает ежедневные OHLCV через документированный API рыночных данных, валидирует записи и загружает их в SQLite.
Включенная фикция работает без учетных данных и содержит синтетические цены, а не рыночные наблюдения.
Даты представляют собой рыночные сессии; время получения, валюта, биржа и основание корректировки являются отдельными полями.
Развертывание использует однократный контейнер Docker и внешнее расписание, с постоянной базой данных и секретами вне образа.
Доступ к живому API, права на рыночные данные и развертывание контейнера требуют проверки в вашем окружении. Локальная фикция не подтверждает живые права или актуальность в реальном времени.
Что вы построите: Скрапер данных фондового рынка
Вы создадите ежедневный сборщик рыночных данных: символ → поддерживаемый API → валидация → SQLite → запланированное обновление. Та же структура инженерии может поддерживать авторизованный HTML источник, но документированный API избегает ненадежных селекторов страниц котировок.
Учебник использует документированный контракт TIME_SERIES_DAILY от Alpha Vantage. Это сырые ежедневные бары, а не живые исполняемые цены или автоматически откорректированный ряд полной доходности. Данные могут быть задержаны или историческими в зависимости от прав на источник; этот проект не является торговой лентой.
Предварительные требования и настройка проекта
Используйте Python 3.12, предоставленный скрипт и фикцию, а также Docker, если хотите путь развертывания. Python проект использует только стандартную библиотеку.
Попробуйте Nstproxy Crawl - Начните Бесплатно Уже Сегодня
project/
Файл требований может содержать комментарий о том, что нет сторонних зависимостей времени выполнения. Для живого запуска требуется одобренный API-ключ Alpha Vantage и лицензия на данные, охватывающая ваше использование.
Выбор надежного источника данных акций
Выберите источник по праву доступа, охвату биржи, определению сессии, корректировкам, бюджету доллара и разрешенному использованию. Источник, подходящий для внутреннего ежедневного исследования, может не позволять перепродажу или отображение в реальном времени.
API фондового рынка против веб-скрейпинга
Лицензированный API обычно предлагает документированную структуру данных и ошибки. Открытая страница котировок может загружать данные динамически, показывать задержанные значения и накладывать отдельные ограничения доступа.
Документация Alpha Vantage определяет поля ежедневной серии и параметры API. Его условия различают права использования; не предполагайте, что доступная конечная точка предоставляет коммерческую перепродажу.
Понимание полей данных акций
Поле
Значение в этом проекте
Проблема валидации
Символ и биржа
Идентичность инструмента, специфичная для источника
Один и тот же короткий тикер может идентифицировать разные инструменты
Дата сессии
Торговая дата, связанная с ежедневным баром
Не дата UTC получения
Открытие/максимум/минимум/закрытие
Сырые цены сессии
Положительные, конечные, внутренне согласованные
Объем
Отчетные единицы для инструмента/сессии
Неотрицательные; определение единицы зависит от источника
Валюта
Явно предоставленные метаданные инструмента
Не делайте выводы по символу в одиночку
Основание
raw_daily
Храните отдельно от данных с корректировкой сплита/дивидендов
Наблюдаемое в
Временная метка UTC сборщика
Отслеживает свежесть получения
Десятичная арифметика избегает ненужных изменений двоичных чисел с плавающей точкой в хранимых ценах. Значения валюты и биржи являются конфигурацией в демонстрации; валидируйте их из вашего основного инструмента производства.
Шаг 1: Создайте проект Python Scraper
Скопируйте предоставленные файлы кода в директорию, создайте виртуальную среду и сначала запустите фикцию.
Фикция содержит два вымышленных бара под конвертом, оформленным как API. Ее значения никогда не должны смешиваться с исследовательским или производственным набором рыночных данных.
Шаг 2: Получите данные акций из источника
Сборщик отправляет явный символ, функцию, размер вывода, тип данных и API-ключ. Он применяет тайм-аут, обрабатывает допустимые временные HTTP ошибки и избегает регистрации URLs с учетными данными.
После подтверждения вашего права доступа, выполните:
python stock_scraper.py --live--symbol IBM \--currency USD --exchange YOUR_VERIFIED_EXCHANGE --db stocks.sqlite
Ни одного живого вызова с учетными данными для этой статьи не было выполнено. Эта полная реализация включена в пакет:
"""Ежедневный сборщик OHLCV. По умолчанию требуется явная локальная фикция."""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:# Никогда не выводите exc или его URL: запрос содержит API ключ.if exc.code notin(429,502,503,504)or attempt ==2:raise RuntimeError(f"Ошибка HTTP источника: {exc.code}")fromNone delay = exc.headers.get("Retry-After","")if delay:# Запланировать более позднюю попытку; никогда не пытайтесь раньше, чем запрашивалось.raise RuntimeError("Превышен лимит запросов; перенастройтесь, используя Retry-After")fromNoneexcept(URLError, TimeoutError):if attempt ==2:raise RuntimeError("Источник недоступен после трех попыток")fromNone time.sleep(2** attempt)defnormalize(payload, symbol, currency, exchange):ifnot re.fullmatch(r"[A-Z]{3}", currency)ornot exchange.strip():raise ValueError("Укажите действительный код валюты и идентификатор биржи")if payload.get("Meta Data",{}).get("2. Symbol","").upper()!= symbol.upper():raise ValueError("Отсутствует или несовпадающий символ провайдера") series = payload.get("Time Series (Daily)")ifnotisinstance(series,dict)ornot series:raise ValueError("Нет дневных баров; проверьте лимиты аккаунта или ошибку источника") 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,"open"),(2,"high"),(3,"low"),(4,"close"))]ifany(not p.is_finite()or p <=0for p in prices):raise ValueError(f"Недопустимые OHLC: {session}") opening, high, low, close = prices
if low >min(opening, close)or high <max(opening, close)or low > high:raise ValueError(f"Несоответствующая свеча: {session}") volume =int(bar["5. volume"])if volume <0:raise ValueError("Отрицательный объем") 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("Выберите точно одно из --fixture или --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)
Компактный размер ответа намерен. Он ограничивает один запуск и поддерживает повторные обновления; это не устраняет необходимость в согласовании исторических правок вне этого окна.
Шаг 3: Парсинг и проверка цен на акции
Парсер отклоняет несовпадающие символы, отсутствующие серии, недействительные даты, не конечные или неположительные значения OHLC, несоответствующие диапазоны свечи и отрицательный объем. Он различает действительную предыдущую сессию и неудачный запрос.
Не указывайте дату извлечения как дату рыночной сессии. Если источник не опубликовал новую свечу из-за закрытия биржи, сохраните предыдущую принятую сессию и укажите её возраст в контексте.
Для сплитов и дивидендов выберите отдельный контракт с откорректированными данными, если ваш анализ требует этого. Смешивание сырых и откорректированных закрытий может создать искусственные «изменения цены».
Шаг 4: Сохранение данных акций в CSV или SQLite
SQLite хранит одну строку на символ, биржу, валюту, основу и дату сессии. Повторяющееся получение обновляет существующую свечу, а не создает дубликаты.
Коллектор сохраняет observed_at отдельно. Для аудита исправлений расширьте дизайн таблицей revisions для источника только на добавление; компактный пример хранит последние принятые значения.
Экспортируйте отчет после сбора, если это необходимо:
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)
Документация SQLite UPSERT объясняет поведение обновления конфликтов, использованное в скрипте.
Шаг 5: Добавление журналирования, попыток и обработки ошибок
Попытки ограничены и предназначены только для временных сбоев. Ответ Retry-After заставляет коллектор остановиться и запрашивать переназначение вместо повторной попытки раньше, чем это разрешено. Сообщения уровня JSON об учетной записи или квотах не проходят валидацию, даже если HTTP-статус равен 200.
Журналируйте принятые свечи, последнюю сессию, время, прошедшее с момента начала работы, и отредактированную категорию ошибок. Никогда не записывайте полный URL запроса, API токен или содержимое секретной среды. Сохраняйте последний принятый набор данных, если последняя попытка завершилась неудачей.
Шаг 6: Запланировать автоматические обновления данных акций
Для ежедневных данных запланируйте обновление после того, как ожидается публикация завершенной сессии, с учетом часового пояса и календаря биржи. Крон для будних дней не знает о праздниках на бирже или изменениях в переходе на летнее/зимнее время.
Эта иллюстративная расписание хоста запускает выполнение в 23:15 в настроенном часовом поясе хоста:
15 23 * * 1-5 /opt/stock/run-collector.sh
Настройте время в соответствии с вашим правом и биржей. Избегайте наложения заданий с помощью блокировки расписания и уведомляйте, если ожидаемая последняя сессия все еще отсутствует после временного окна публикации.
Шаг 7: Развертывание скрапера в производственной среде
Используйте запланированное разового развертывания. Успешный выход процесса означает, что настроенное получение завершено; внешний планировщик отвечает за следующее выполнение.
Dockerize скрапер акций
Предоставленный образ работает под пользователем, не имеющим прав root, и записывает данные в смонтированную директорию.
Docker не был доступен для сборки контейнера в этой среде написания; это инструкция для развертывания, а не утверждение о успешном удаленном развертывании.
Настройка переменных среды и секретов
Поместите только необходимый API ключ в файл окружения с ограниченными правами доступа или в хранилище секретов. Держите его за пределами контекста сборки образа и репозитория. Предоставленный .dockerignore исключает файлы окружения и базы данных.
Используйте отдельные тестовые учетные данные и базу данных во время проверок развертывания. Поменяйте ключ, если он раскрыт в журналах или закоммичен в файл.
Настройка планирования и мониторинга
Для операций мониторинга цен следите за статусом выхода процесса, последним успешным выполнением, последней ожидаемой сессией, количеством принятых строк и доступностью базы данных. Держите оповещения о сбоях провайдеров отдельно от закрытия рынка.
Не привязывайте постоянный цикл перезапуска к успешно завершенной разовой задаче. Позвольте планировщику повторно пытаться исправить подходящие сбои с явным бюджетом и блокировкой пересечения.
Тестирование развернутого скрапера акций
Локальная проверка фиксации проверяет две принятые свечи, отклонение недействительных свечей и отсутствие лишних строк после идентичного повторного запуска. Она не проверяет авторизацию источника, живые цены, Docker или постоянство VPS.
На вашем развертывании проверьте, что смонтированная база данных сохраняется после замены контейнера; секрет доступен только во время выполнения; повторная работа является идемпотентной; ошибки квоты дают ненулевой выход; и восстановление резервной копии работает.
Масштабирование от одной акции до сотен тикеров
Вычислите свой бюджет на запрос перед добавлением параллелизма. Применяйте контроль запросов на производстве и, когда это возможно, предпочитайте поддерживаемые пакетные интерфейсы, кэшируйте неизмененную историю, распределяйте задания и поддерживайте проверенный мастер инструментов.
Разделяйте состояние по инструменту и сессии. Сохраняйте частичный прогресс, чтобы один неудачный символ не повторял каждый успешный запрос. Большой цикл при маленьком праве будет приводить к сбоям квоты, а не к использованию пропускной способности.
Общие проблемы и решения при скрейпинге акций
Пустой JSON может быть сообщением о лимите на аккаунт, а не пустым рынком. Застарелый бар может быть праздничным днем, приостановленным инструментом, задержкой публикации или неудачным запросом; проверьте правильные доказательства.
Неожиданные скачки могут быть связаны с делением, изменениями валюты, повторным использованием символа или смешением необработанных/скорректированных данных. Проверьте событие и идентичность инструмента перед публикацией отчета о аномалиях.
Затраты на скрейпинг данных об акциях и юридические соображения
Составьте бюджет на доступ к данным, права на перераспределение или отображение, вычисления, хранение, резервное копирование, мониторинг и обслуживание. Учетные данные API сами по себе не устанавливают все права на использование.
Если вы используете Nstdata Crawl для авторизованной финансовой веб-страницы, рассматривайте это как инфраструктуру приобретения. Это не является разрешенной на бирже лентой рыночных данных или заменой проверки финансовых данных.
Начните с устройства, подтвердите метаданные инструмента, проверьте права на источник, подтвердите ответ активного аккаунта, протестируйте постоянство хранения и настройте мониторинг с учетом календаря. Переходите к нескольким инструментам только после того, как один запланированный запуск корректно обработает как успех, так и неудачу.
Подтвердите одобренные источники страниц
Отдельно инспектируйте разрешенное содержимое веб-страниц от лицензированных лент рыночных данных.
В: Могу ли я скрейпить цены акций с помощью Python?
Да, через авторизованный API или разрешенный источник. Этот учебник использует документированный контракт с API для ежедневных данных и локальное устройство.
В: Могу ли я использовать Yahoo Finance вместо этого?
Используйте его текущий разрешенный доступ и соглашения по лицензированию данных, а не предполагая, что публичная страница позволяет неограниченную автоматизацию или перераспределение.
В: Является ли пример реальным временем?
Нет. Он собирает ежедневные бары; свежесть зависит от прав поставщика и публикации.
В: Почему демо требует валюту и биржу?
Выбранный ответ не является полным мастер-списком инструментов. Сборщик избегает предположений по этим полям на основе тикера.
В: Как часто должны выполняться обновления?
Согласно праву источника и предполагаемому интервалу данных. Более частые запросы на ежедневные бары не превращают набор данных в живую ленту.
В: Как доказать, что развертывание работает?
Проверки принятия с учетом, постоянное хранение, идемпотентные повторные запуски, правильная свежесть рыночной сессии и наблюдаемое восстановление после неудач в вашей собственном окружении.
Lena Zhou
Oct. 19th 2026
Сканируйте целые сайты одним API-запросом
Преобразуйте любой сайт в Markdown, HTML, JSON, ссылки, PDF и другие форматы без управления инфраструктурой сканирования.