Финансовые решения принимаются на основе данных. Будь то отслеживание акции, которой вы владеете, создание рыночной панели или подача данных в исследовательскую модель, публичные показатели, движущие рынком (котировки, уровни индексов, отчётные финансы компаний), являются сырым материалом. Большая часть этих данных лежит на виду на финансовых сайтах, однако вручную собирать их по десяткам тикеров медленно, а живые страницы, отображающие их, построены для рендеринга в браузере, а не для передачи чистого ответа обычному скрипту.
В этом руководстве показано, как собирать финансовые данные с помощью Python надёжным способом. Рассматриваются наиболее важные публичные источники, а затем разбирается небольшой работающий скрапер, который загружает отрендеренную страницу финансовой котировки через Crawling API, парсит основные поля (цена, изменение, объём и несколько соседних значений) и экспортирует чистые JSON и CSV. Всё здесь ограничено публичными рыночными данными: фактическими числами, которые любой может увидеть без входа в систему, и никогда личными или закрытыми платным доступом данными. Раздел о законности ближе к концу не является формальностью, поэтому прочитайте его перед тем, как направить скрапер на реальные объёмы.
Какие публичные финансовые данные стоит собирать
Прежде чем писать код, полезно понять, какие данные одновременно полезны и допустимы для сбора. Публичные финансовые данные делятся на несколько широких категорий, каждая из которых подходит для определённого вида анализа.
- Рыночные котировки. Текущая цена, внутридневное изменение, процентное изменение, дневной диапазон и объём торгов для акции, ETF или фонда. Это наиболее часто отслеживаемые данные и основа разбираемого ниже примера.
- Индексы. Агрегированные уровни для бенчмарков (широкорыночные или отраслевые индексы), полезные для оценки позиции относительно рынка в целом.
- Публичные финансовые показатели компаний. Данные, которые публичные компании обязаны раскрывать: выручка, прибыль, рыночная капитализация и сводные коэффициенты, отображаемые на странице профиля компании.
- Публичные рыночные новости и заголовки. Редакционные заголовки и временные метки, питающие анализ настроений. Собирайте фактический заголовок и ссылку, но не полный текст статьи, защищённый авторским правом.
Такие источники, как Forbes, Reuters, Bloomberg, MarketWatch и Financial Times, хороши для чтения, но для программного фида вам нужна страница, которая представляет структурированные поля котировок, именно такую мы используем ниже. Для общего обзора наш обзор лучших поставщиков финансовых данных сравнивает основные источники.
Что вы создадите
Python-скрипт, который принимает URL публичной страницы финансовой котировки для тикера, загружает отрендеренную страницу через Crawling API и извлекает структурированную запись. Поля ниже представляют типичные публичные рыночные данные; точные CSS-селекторы зависят от целевой страницы, поэтому воспринимайте их как шаблон для адаптации.
- Symbol тикер, к которому относится котировка.
- Price текущая котируемая цена.
- Change абсолютное изменение цены за день.
- Change percent дневное движение в процентах.
- Volume количество проторгованных акций.
- Day range внутридневной минимум и максимум.
- Market cap рыночная капитализация компании, если страница её показывает.
Почему обычный запрос не работает на финансовых страницах
Запросите современный URL финансовой котировки обычным HTTP-клиентом, и вы, как правило, получите статус 200, но почти без чисел в теле. Две вещи работают против вас. Во-первых, большинство финансовых сайтов рендерят котировки на стороне клиента: цена, изменение и объём передаются через JavaScript после загрузки исходного HTML, поэтому первый ответ является тонкой оболочкой, а парсинг захватывает chrome страницы вместо нужных показателей. Во-вторых, финансовые сайты тщательно следят за автоматическим трафиком, поскольку данные котировок в реальном времени ценны. IP-адреса датацентров и нетипичные для браузера паттерны запросов ограничиваются по скорости, блокируются по IP или оспариваются до того, как добираются до отрендеренного контента.
Таким образом, работающему финансовому скраперу нужны две вещи в одном запросе: браузер, который действительно рендерит страницу, и IP-адрес, который сайт воспринимает как настоящего посетителя. Можно собрать это самостоятельно с помощью безголового браузера и ротирующих резидентных прокси, но поддержание такого стека работоспособным и занимает большую часть усилий. Crawling API объединяет оба компонента в одном вызове: вы отправляете URL с JavaScript-токеном, он рендерит страницу за доверенным IP и возвращает готовый HTML для парсинга. Подробнее о том, почему клиентские страницы требуют такого подхода, см. в скрапинге JavaScript-страниц с Python.
Crawlbase предлагает два типа токенов. Обычный токен загружает статический HTML; JavaScript (JS) токен сначала рендерит страницу в настоящем браузере. Финансовые страницы заполняют поля котировок на стороне клиента, поэтому здесь нужен JS-токен. Обычный токен возвращает ту же тонкую оболочку, что и обычный запрос, почти без полезного для парсинга содержимого.
Предварительные требования
Прежде чем писать код, нужно подготовить несколько вещей. Ни одна из них не занимает много времени.
Базовый Python. Вы должны уметь писать и запускать Python-скрипты и устанавливать пакеты с помощью pip. Если вы новичок в парсинге, руководство по BeautifulSoup станет хорошим дополнением к этому туториалу.
Python 3.8 или новее. Проверьте версию командой python --version. Если Python не установлен, скачайте его с python.org или через дистрибутив вроде Anaconda и убедитесь, что Python добавлен в PATH.
Аккаунт Crawlbase и JS-токен. Зарегистрируйтесь, откройте дашборд и скопируйте JavaScript (JS) токен со страницы документации аккаунта. Crawlbase предоставляет до 5 000 бесплатных запросов для начала, чего более чем достаточно для прохождения этого руководства, и вы платите только за успешные запросы. Обращайтесь с токеном как с паролем: он аутентифицирует ваши запросы, поэтому не добавляйте его в систему контроля версий.
Настройка проекта
Создайте виртуальное окружение, чтобы зависимости проекта оставались изолированными, затем установите библиотеки, необходимые скраперу.
python --version python -m venv finance_env source finance_env/bin/activate pip install crawlbase beautifulsoup4
В Windows активируйте окружение командой finance_env\Scripts\activate вместо строки source. Две зависимости выполняют основную работу: crawlbase является официальным клиентом Crawling API, а beautifulsoup4 парсит возвращаемый HTML, позволяя извлекать отдельные поля по CSS-селектору. Оба модуля json и csv входят в стандартную библиотеку, поэтому для шага экспорта ничего дополнительно устанавливать не нужно.
Шаг 1: загрузка отрендеренной страницы котировки
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с помощью JS-токена и запросите публичный URL котировки. Финансовые страницы асинхронно передают числа, поэтому передайте ajax_wait и page_wait, чтобы дождаться динамического контента перед захватом страницы. Проверка cb_status (legacy pc_status) от Crawlbase перед парсингом позволяет ошибкам быть заметными, а не молчаливыми.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 5000, } def crawl(page_url): response = api.get(page_url, OPTIONS) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None if __name__ == "__main__": quote_url = "https://www.example-finance.com/quote/AAPL" html = crawl(quote_url) print(html[:500] if html else "No HTML returned")
Замените quote_url на публичную страницу котировки, которую вы планируете отслеживать. Два параметра ожидания важны для клиентской финансовой страницы: ajax_wait ждёт завершения асинхронной загрузки контента, а page_wait выдерживает фиксированное количество миллисекунд после загрузки, чтобы числа с поздней передачей появились до захвата. Пять секунд является разумной точкой отсчёта; увеличивайте значение, если показатели возвращаются пустыми. Запустите скрипт, и вы должны увидеть настоящую разметку котировки, а не оболочку, которую возвращает обычный запрос, что подтверждает работоспособность рендеринга до написания единственного селектора.
Страница финансовой котировки требует отрендеренной страницы за доверенным IP в одном вызове, именно это и настраивают параметры ajax_wait и page_wait выше. Crawling API принимает JS-токен, запускает страницу в настоящем браузере, выполняет ротацию резидентных IP на стороне сервера и передаёт вам готовый HTML, позволяя не запускать самостоятельно безголовый флот и пул прокси. Сначала направьте его на публичную страницу котировки в бесплатном тарифе.
Шаг 2: парсинг полей котировки
Имея готовый HTML, загрузите его в BeautifulSoup и извлеките основные числа. Большинство финансовых страниц помечают ключевые поля стабильными атрибутами data-* или именами полей, которые более надёжны, чем сгенерированные имена классов. Каждый поиск защищён таким образом, чтобы отсутствующее поле возвращало None, а не прерывало выполнение.
from bs4 import BeautifulSoup def text_of(soup, selector): el = soup.select_one(selector) return el.get_text(strip=True) if el else None def parse_quote(html, symbol, url): soup = BeautifulSoup(html, "html.parser") return { "symbol": symbol, "price": text_of(soup, '[data-field="regularMarketPrice"]'), "change": text_of(soup, '[data-field="regularMarketChange"]'), "change_percent": text_of(soup, '[data-field="regularMarketChangePercent"]'), "volume": text_of(soup, '[data-field="regularMarketVolume"]'), "day_range": text_of(soup, '[data-field="regularMarketDayRange"]'), "market_cap": text_of(soup, '[data-field="marketCap"]'), "link": url, }
Вспомогательная функция text_of запрашивает один элемент и возвращает его очищенный текст или None при отсутствии элемента, поэтому страница котировки с пропущенным полем не прерывает цикл. Значения data-field здесь (regularMarketPrice, regularMarketChange, regularMarketVolume и другие) типичны для того, как финансовые страницы размечают свои числа в реальном времени. Откройте целевую страницу в инструментах разработчика, найдите атрибут или класс, оборачивающий каждое значение, и подставьте реальные селекторы; хранение поисков в одном словаре делает это однострочным изменением на каждое поле.
Финансовые сайты меняют разметку без предупреждения, и точные имена data-field различаются от источника к источнику. Воспринимайте селекторы здесь как начальный шаблон, а не как постоянный контракт. Когда поле возвращает None, повторно проверьте живую страницу и обновите селектор. Периодическое обслуживание селекторов, нормальное явление для любого производственного скрапера, а не признак неисправности.
Шаг 3: сборка полного скрипта
Теперь соедините части в один работающий скрипт: пройдитесь по списку тикеров, загрузите и разберите каждую котировку с небольшой обёрткой для повторных попыток и экспортируйте записи в JSON и CSV.
import csv import json import time from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 5000, } # Map each ticker to its public quote page URL. QUOTE_BASE = "https://www.example-finance.com/quote/" SYMBOLS = ["AAPL", "MSFT", "GOOGL"] def crawl(page_url): response = api.get(page_url, OPTIONS) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None def fetch_html(page_url, max_retries=2): for attempt in range(max_retries + 1): html = crawl(page_url) if html: return html if attempt < max_retries: time.sleep(1) return None def text_of(soup, selector): el = soup.select_one(selector) return el.get_text(strip=True) if el else None def parse_quote(html, symbol, url): soup = BeautifulSoup(html, "html.parser") return { "symbol": symbol, "price": text_of(soup, '[data-field="regularMarketPrice"]'), "change": text_of(soup, '[data-field="regularMarketChange"]'), "change_percent": text_of(soup, '[data-field="regularMarketChangePercent"]'), "volume": text_of(soup, '[data-field="regularMarketVolume"]'), "day_range": text_of(soup, '[data-field="regularMarketDayRange"]'), "market_cap": text_of(soup, '[data-field="marketCap"]'), "link": url, } def save_outputs(records): with open("quotes.json", "w") as f: json.dump(records, f, indent=2) if not records: return with open("quotes.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=records[0].keys()) writer.writeheader() writer.writerows(records) def main(): records = [] for symbol in SYMBOLS: url = QUOTE_BASE + symbol html = fetch_html(url) if html: records.append(parse_quote(html, symbol, url)) time.sleep(2) save_outputs(records) print(f"Saved {len(records)} quotes") if __name__ == "__main__": main()
Скрипт перебирает SYMBOLS, строит URL каждой котировки, загружает его с помощью обёртки для повторных попыток, парсит в запись и выдерживает паузу в цикле с двухсекундной задержкой. save_outputs записывает и JSON, и CSV, используя ключи первой записи в качестве заголовка, так что данные доступны в том формате, который нужен вашему инструменту. Добавляйте тикеры в SYMBOLS и корректируйте QUOTE_BASE под публичный источник, который вы используете.
Как выглядит результат
Запустите полный скрипт, и вы получите чистую структурированную запись на каждый тикер, готовую для анализа, базы данных или таблицы. Значения ниже иллюстративные, а не реальные данные.
[ { "symbol": "AAPL", "price": "225.40", "change": "+1.85", "change_percent": "+0.83%", "volume": "48,210,300", "day_range": "223.10 - 226.05", "market_cap": "3.42T", "link": "https://www.example-finance.com/quote/AAPL" }, { "symbol": "MSFT", "price": "418.20", "change": "-2.40", "change_percent": "-0.57%", "volume": "19,840,100", "day_range": "416.00 - 421.30", "market_cap": "3.11T", "link": "https://www.example-finance.com/quote/MSFT" } ]
Соответствующий CSV содержит те же столбцы, по одной строке на тикер, что позволяет сразу загрузить его в pandas или любую таблицу для сортировки по изменению, фильтрации по объёму или построения графика цены при повторных запусках. Запланируйте скрипт по расписанию через cron, и у вас будет простой временной ряд публичных котировок по отслеживаемым тикерам.
Масштабирование и сохранение доступности
Отслеживание сотен символов или опрос одного набора с плотным расписанием, именно там финансовый скрапинг становится сложнее, поскольку это именно тот трафик, за которым наблюдают эти сайты. Несколько привычек помогут поддерживать работоспособность при длительной работе.
- Управляйте темпом запросов. Опрос списка наблюдения в плотном цикле, это кратчайший путь к ограничению скорости или оспариванию. Двухсекундные паузы выше, это минимум, а не потолок; увеличивайте их для больших заданий и не запускайте все тикеры одновременно.
- Используйте ротацию. Пул резидентных IP распределяет запросы по множеству реальных пользовательских адресов, чтобы ни один из них не превысил лимит скорости. Crawling API делает это на стороне сервера; если вы строите собственный стек, именно этому стоит уделить наибольшее внимание.
-
Следите за кодами статусов. Если запуск начинает возвращать значения
cb_status, отличные от 200, это сигнал о том, что текущая скорость или уровень IP недостаточны. Воспринимайте это как сигнал снизить нагрузку, а не как шум, который можно игнорировать.
Для больших заданий асинхронный Crawler ставит запросы в очередь и доставляет результаты на вебхук, что подходит для опроса множества символов без удержания открытых соединений. Для общей стратегии см. крупномасштабный финансовый скрапинг и наше общее руководство по скрапингу без блокировок. Если конечная цель, конкурентный или ценовой анализ, наше руководство по веб-скрапингу для ценовой аналитики показывает, как команды превращают такой поток данных в решения.
Законно ли собирать финансовые данные?
Допустимость скрапинга финансовых данных зависит от условий использования источника, вашей юрисдикции и того, что вы делаете с данными. Рыночные котировки, уровни индексов и показатели, которые публичная компания обязана раскрывать, являются фактической публичной информацией, и сбор публичных фактов для анализа в целом защитим. Однако страница, на которой они размещены, принадлежит кому-то, и большинство финансовых сайтов ограничивают автоматический доступ и массовый сбор данных в своих условиях. Ни один из кодов здесь не меняет этого; он только обеспечивает техническую часть. Прочитайте Условия использования целевого сайта и его robots.txt, соблюдайте заявленные ограничения скорости и рассматривайте оба документа как границу того, что вы собираете.
Несколько правил, которых стоит придерживаться. Собирайте только публичные, не закрытые данные: поля котировок, уровни индексов и раскрытые финансовые показатели, которые любой может видеть без аккаунта. Не собирайте ничего за логином или платным доступом и никогда не обходите аутентификацию или ограничение запросов для получения премиальных данных, это переходит от публичного сбора к несанкционированному доступу. Поддерживайте объём достаточно низким, чтобы не создавать нагрузку на серверы источника. И помните, что даже фактические данные несут лицензионные условия при распространении: многие сайты получают котировки от бирж и поставщиков данных по соглашениям, ограничивающим повторную публикацию, поэтому сбор числа для собственного анализа не означает право на его перепродажу или публикацию. Редакционный контент, например полный текст статей, защищён авторским правом; для анализа настроений берите заголовок и ссылку, а не тело.
Данное руководство ограничено публичными рыночными данными, поскольку именно это делает работу защитимой. Для всего, что выходит за рамки личных исследований, особенно для производственных или коммерческих продуктов, используйте официальный API рыночных данных или лицензированный фид, а не более изощрённый скрапер. Большинство крупных финансовых платформ предлагают собственный API данных или сотрудничают с устоявшимися поставщиками рыночных данных, а биржи напрямую лицензируют данные в реальном времени и исторические данные. Эти пути предоставляют стабильный контракт, задокументированные поля и права на распространение, которых скрапинг публичной страницы никогда не даёт. Используйте скрапинг для прототипирования и заполнения пробелов; лицензируйте фид, когда данные лежат в основе чего-то реального.
Ключевые выводы
- Знайте, что допустимо собирать. Публичные рыночные данные (котировки, индексы, раскрытые финансовые показатели компаний, заголовки) фактические и допустимы; личные и закрытые данные, нет.
- Финансовые страницы рендерятся на стороне клиента. Обычный запрос возвращает тонкую оболочку без чисел, поэтому необходимо рендерить страницу перед парсингом.
-
Нужны одновременно рендеринг и доверенный IP. Crawling API с JS-токеном делает оба шага в одном вызове;
ajax_waitиpage_waitуправляют временем ожидания контента. - Парсите, перебирайте в цикле и экспортируйте. Сопоставьте каждое поле с защищённым селектором, обойдите тикеры с обёрткой для повторных попыток, добавьте паузы и запишите JSON и CSV.
- Лицензируйте для производственного использования. Соблюдайте ToS и robots.txt каждого источника, помните, что данные котировок несут условия распространения, и переходите на официальный API рыночных данных или лицензированный фид для любого коммерческого применения.
Часто задаваемые вопросы
Почему обычный запрос возвращает финансовую страницу без чисел?
Потому что большинство финансовых сайтов загружают поля котировок на стороне клиента с помощью JavaScript. Исходный HTML является оболочкой, которая заполняется только после выполнения скриптов страницы, поэтому сырой HTTP-запрос возвращает статус 200, но цена, изменение и объём отсутствуют. Чтобы получить числа, необходимо сначала отрендерить страницу, именно это делает JS-токен Crawling API.
Нужен ли обычный токен или JS-токен для финансовых страниц?
JS-токен. Обычный токен загружает статический HTML, который на клиентской финансовой странице представляет собой ту же тонкую оболочку, что и обычный запрос. JS-токен рендерит страницу в настоящем браузере перед возвратом HTML, поэтому поля котировок присутствуют при парсинге через BeautifulSoup.
Какие финансовые данные можно безопасно собирать?
Публичные, не закрытые рыночные данные: живые котировки, уровни индексов, финансовые показатели, которые публичная компания обязана раскрывать, и фактические заголовки со ссылками. Оставайтесь на данных, видимых любому посетителю без аккаунта, не собирайте ничего за логином или платным доступом и избегайте повторной публикации текста статей, защищённых авторским правом, или лицензированных фидов данных.
Мои селекторы возвращают None. Что изменилось?
Почти наверняка разметка источника. Финансовые сайты меняют сгенерированные имена классов и атрибуты data-field без предупреждения, причём эти имена различаются от источника к источнику, так что селекторы, работавшие в прошлом месяце, могут сломаться. Повторно проверьте живую страницу в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов нормально для любого производственного скрапера.
Стоит ли использовать скрапинг или официальный API рыночных данных для продакшена?
Для коммерческого или производственного использования, официальный API или лицензированный фид. Скрапинг публичной страницы отлично подходит для прототипирования и заполнения пробелов, но официальные API предоставляют задокументированные поля, стабильные контракты и права на распространение, которых публичная страница никогда не даёт. Большинство крупных финансовых платформ предлагают API данных или сотрудничают с устоявшимися поставщиками рыночных данных.
Как собирать данные по множеству тикеров без блокировки?
Делайте паузы между запросами, не опрашивайте все символы одновременно и используйте ротирующие резидентные IP, чтобы ни один адрес не превысил лимит скорости. Crawling API выполняет ротацию IP на стороне сервера, а асинхронный Crawler ставит большие задания в очередь и публикует результаты на вебхук. Следите за кодами cb_status и снижайте нагрузку, когда они перестают возвращать 200.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
