Yahoo Finance является одной из наиболее посещаемых платформ для отслеживания акций, индексов и криптовалют, а её страницы котировок содержат именно те структурированные рыночные данные, которые нужны для отслеживания цен, скрининга и исследований: последняя цена, изменение за день, цена закрытия предыдущей сессии, рыночная капитализация, объём и дневной диапазон. Для тех, кто следит за списком тикеров, эти публичные данные котировок являются исходным материалом, а считывать их вручную по десяткам символов медленно и ненадёжно.
В этом руководстве показано, как парсить Yahoo Finance с помощью Python надёжным способом. Вы создадите небольшой, выполняемый скрапер, который получает отрендеренные страницы котировок через Crawling API, разбирает нужные поля с помощью BeautifulSoup, перебирает список тикеров и экспортирует чистые JSON и CSV. Всё руководство ограничено публичными рыночными данными, а раздел о законности в конце не является формальностью, поэтому прочитайте его, прежде чем направлять скрапер на реальный объём.
Что вы создадите
Скрипт на Python, который принимает список тикеров, получает каждую отрендеренную страницу котировок Yahoo Finance через Crawling API и извлекает структурированную запись для каждого символа. В качестве работающего примера используются AAPL, TSLA и BTC-USD. Мы извлекаем следующие поля:
- Цена последняя торговая цена символа.
- Изменение изменение цены относительно предыдущего закрытия в абсолютном и процентном выражении.
- Цена закрытия предыдущей сессии цена закрытия предыдущей торговой сессии.
- Рыночная капитализация общая рыночная капитализация из статистики котировки.
- Объём количество акций или единиц, торгуемых в текущей сессии.
- Дневной диапазон ценовой коридор (минимум-максимум) текущего торгового дня.
Почему обычный запрос не работает на Yahoo Finance
Если запросить URL котировки Yahoo Finance с помощью обычного HTTP-клиента, вы получите ответ со статусом 200, но лишь часть чисел в теле. Против вас работают два фактора. Во-первых, страница котировки рендерит живую цену, изменение и таблицу статистики в браузере через JavaScript, поэтому исходный HTML является тонкой оболочкой, которая заполняется лишь после выполнения скриптов страницы. Разобрав этот первый ответ, вы получите заполнители или пустые узлы вместо реальных данных. Во-вторых, Yahoo быстро помечает автоматический трафик: IP датацентров и паттерны запросов, не похожие на реальный браузер, получают ограничение скорости, перенаправление на страницу согласия или запрос подтверждения ещё до того, как дойдут до отрендеренного контента.
Таким образом, работающий скрапер Yahoo Finance требует двух вещей в одном запросе: браузера, который реально рендерит страницу, и IP, который платформа воспринимает как реального посетителя. Вы можете собрать это самостоятельно из безголового браузера и пула ротирующих резидентских прокси, но поддержание этого стека составляет большую часть работы. Crawling API объединяет оба компонента в одном вызове: вы отправляете ему URL с JavaScript-токеном, он рендерит страницу за доверенным IP и возвращает готовый HTML для разбора. Если вы хотите узнать подробнее о рендеринге таких целей, руководство по парсингу JavaScript-страниц на Python является хорошим дополнением.
Crawlbase предлагает два типа токенов. Обычный токен получает статический HTML; JavaScript (JS) токен сначала рендерит страницу в реальном браузере. Yahoo Finance заполняет поля цены и статистики на стороне клиента, поэтому здесь нужен JS-токен. Обычный токен возвращает ту же тонкую оболочку, что и обычный запрос, из которой практически нечего разбирать.
Предварительные требования
Несколько вещей, которые нужно иметь под рукой. На это не уйдёт много времени.
Базовые знания Python. Вы должны уметь писать и запускать скрипт на Python и устанавливать пакеты с помощью pip. Если вы новичок в разборе данных, руководство по BeautifulSoup является хорошим дополнением к этому материалу.
Python версии 3.8 или выше. Проверьте версию командой python --version. Если не установлен, загрузите с python.org или через дистрибутив вроде Anaconda и убедитесь, что Python добавлен в PATH.
Аккаунт Crawlbase и JS-токен. Зарегистрируйтесь, откройте панель управления и скопируйте JavaScript (JS) токен со страницы документации аккаунта. Crawlbase предоставляет 1000 бесплатных запросов для начала, чего вполне достаточно для прохождения этого руководства. Относитесь к токену как к паролю: он аутентифицирует ваши запросы, поэтому не храните его в системе контроля версий.
Настройка проекта
Создайте виртуальное окружение, чтобы зависимости проекта оставались изолированными, затем установите нужные скраперу библиотеки.
python --version python -m venv yahoo_env source yahoo_env/bin/activate pip install crawlbase beautifulsoup4
В Windows активируйте окружение командой yahoo_env\Scripts\activate вместо строки с source. Две зависимости выполняют основную работу: crawlbase является официальным клиентом для Crawling API, а beautifulsoup4 разбирает возвращаемый HTML, позволяя извлекать отдельные поля по CSS-селектору. И json, и csv входят в стандартную библиотеку, поэтому для шага экспорта ничего дополнительно устанавливать не нужно.
Шаг 1: Получение отрендеренной страницы котировки
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с вашим JS-токеном и запросите URL котировки Yahoo Finance. Yahoo загружает цену и статистику асинхронно, поэтому передайте ajax_wait и page_wait, чтобы дождаться динамического контента перед захватом страницы. Проверка Crawlbase-статуса pc_status перед разбором делает ошибки явными.
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"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['pc_status']}") return None if __name__ == "__main__": quote_url = "https://finance.yahoo.com/quote/AAPL" html = crawl(quote_url) print(html[:500] if html else "No HTML returned")
Два параметра ожидания важны для цели с клиентским рендерингом. ajax_wait указывает API дожидаться завершения загрузки асинхронного контента, а page_wait выдерживает фиксированное количество миллисекунд после загрузки, чтобы живая цена устоялась до захвата страницы. Пять секунд является разумной отправной точкой; увеличьте это значение, если данные возвращаются пустыми. Запустите скрипт командой python yahoo_scraper.py, и вы должны увидеть реальную разметку котировки, а не оболочку, которую возвращает обычный запрос. Это подтверждает работоспособность рендеринга до написания единого селектора.
Для котировки Yahoo Finance нужна отрендеренная страница за доверенным IP в одном вызове, что именно и настраивают параметры ajax_wait и page_wait выше. Crawling API принимает JS-токен, выполняет страницу в реальном браузере, осуществляет ротацию через резидентские IP на стороне сервера и возвращает готовый HTML, поэтому вам не нужно запускать собственный парк безголовых браузеров и пул прокси. Попробуйте на публичной котировке на бесплатном тарифе.
Шаг 2: Разбор основной цены и изменения
Yahoo Finance предоставляет свои данные в реальном времени через стабильные атрибуты data-testid в заголовке котировки, что делает их надёжными целями для разбора. Загрузите отрендеренный HTML в BeautifulSoup и считайте название, цену и изменение из этих атрибутов. Каждый поиск защищён так, что отсутствующее поле возвращает 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 scrape_header(html): soup = BeautifulSoup(html, "html.parser") return { "title": text_of(soup, "div.hdr h1"), "price": text_of(soup, '.livePrice[data-testid="qsp-price"]'), "change": text_of(soup, '.priceChange[data-testid="qsp-price-change"]'), "change_percent": text_of(soup, '[data-testid="qsp-price-change-percent"]'), }
Вспомогательная функция text_of запрашивает один элемент и возвращает его очищенный текст или None, когда элемент отсутствует, поэтому символ, у которого нет какого-либо поля, не прерывает цикл. Селекторы взяты прямо из заголовка котировки Yahoo: title считывает название компании и тикер из заголовка div.hdr h1, price считывает узел живой цены с тегом qsp-price, а два селектора изменения считывают абсолютное движение (qsp-price-change) и процентное движение (qsp-price-change-percent), расположенные рядом с ним.
Шаг 3: Разбор таблицы статистики
Под заголовком Yahoo Finance отображает небольшой блок статистики с ценой закрытия предыдущей сессии, рыночной капитализацией, объёмом, дневным диапазоном и другими данными. Каждый показатель находится в элементе списка с атрибутом data-field, поэтому значение читается по имени поля, а не по хрупкой позиции. Это делает разбор стабильным даже при изменении порядка элементов в сетке.
STAT_FIELDS = { "previous_close": "regularMarketPreviousClose", "market_cap": "marketCap", "volume": "regularMarketVolume", "day_range": "regularMarketDayRange", } def scrape_stats(soup): stats = {} for key, field in STAT_FIELDS.items(): stats[key] = text_of( soup, f'li[data-field="{field}"] span.value, li[data-field="{field}"] fin-streamer' ) return stats
Карта STAT_FIELDS сопоставляет каждое имя выходного поля с внутренним именем поля Yahoo. Yahoo оборачивает живые значения в элемент fin-streamer, а статические значения в span.value, поэтому селектор пробует оба варианта и берёт тот, что присутствует. Чтобы найти точное имя поля для конкретного показателя, откройте страницу котировки в браузере, щёлкните правой кнопкой мыши на значении и прочитайте атрибут data-field его элемента списка. Дневной диапазон возвращается как одна строка вида 168.49 - 171.05, которую можно разбить по тире позже, если нужны отдельные значения минимума и максимума.
Yahoo Finance периодически переделывает разметку своих котировок, и генерируемые имена классов меняются вместе с ней. Атрибуты data-testid и data-field, используемые здесь, более стабильны, чем имена классов, но относитесь к каждому селектору как к стартовому шаблону, а не к контракту. Когда поле возвращает None, заново проинспектируйте живую страницу в браузерных инструментах разработчика и обновите селектор. Периодическое обслуживание селекторов является нормой для любого производственного скрапера, а не признаком сбоя.
Шаг 4: Сборка полного скрипта
Теперь объединим части в один выполняемый скрипт: переберём список тикеров, получим каждую отрендеренную страницу котировки с небольшой обёрткой для повторных попыток, разберём заголовок и статистику в одну запись и экспортируем записи в 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, } STAT_FIELDS = { "previous_close": "regularMarketPreviousClose", "market_cap": "marketCap", "volume": "regularMarketVolume", "day_range": "regularMarketDayRange", } def crawl(page_url): response = api.get(page_url, OPTIONS) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['pc_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 scrape_quote(html, symbol): soup = BeautifulSoup(html, "html.parser") record = { "symbol": symbol, "title": text_of(soup, "div.hdr h1"), "price": text_of(soup, '.livePrice[data-testid="qsp-price"]'), "change": text_of(soup, '.priceChange[data-testid="qsp-price-change"]'), "change_percent": text_of(soup, '[data-testid="qsp-price-change-percent"]'), } for key, field in STAT_FIELDS.items(): record[key] = text_of( soup, f'li[data-field="{field}"] span.value, li[data-field="{field}"] fin-streamer' ) return record def save_outputs(records): with open("yahoo_quotes.json", "w") as f: json.dump(records, f, indent=2) if not records: return with open("yahoo_quotes.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=records[0].keys()) writer.writeheader() writer.writerows(records) def main(): symbols = ["AAPL", "TSLA", "BTC-USD"] records = [] for symbol in symbols: url = f"https://finance.yahoo.com/quote/{symbol}" html = fetch_html(url) if html: records.append(scrape_quote(html, symbol)) time.sleep(2) save_outputs(records) print(f"Saved {len(records)} quotes") if __name__ == "__main__": main()
Скрипт перебирает список тикеров, получает каждую страницу котировки с обёрткой для повторных попыток, объединяет поля заголовка и статистики в одну запись и задаёт темп цикла двухсекундной паузой. save_outputs записывает одновременно JSON-файл и CSV, используя ключи первой записи в качестве заголовка, так что данные доступны в любом формате для нужного вам инструмента. Добавляйте или убирайте символы из списка symbols в соответствии с вашим вотч-листом.
Как выглядит результат
Запустите полный скрипт командой python yahoo_scraper.py, и вы получите чистую структурированную запись для каждого символа, готовую для анализа, базы данных или таблицы. Значения ниже приведены для примера; реальные данные меняются с каждой сессией.
[ { "symbol": "AAPL", "title": "Apple Inc. (AAPL)", "price": "168.99", "change": "-3.70", "change_percent": "(-2.14%)", "previous_close": "172.69", "market_cap": "2.61T", "volume": "54,318,920", "day_range": "168.49 - 171.05" }, { "symbol": "TSLA", "title": "Tesla, Inc. (TSLA)", "price": "156.90", "change": "-4.58", "change_percent": "(-2.84%)", "previous_close": "161.48", "market_cap": "499.81B", "volume": "112,045,300", "day_range": "155.41 - 160.39" } ]
Аналогичный CSV содержит те же столбцы, по одной строке на символ, что позволяет сразу открыть его в pandas или любой таблице для построения графика вотч-листа или сравнения дневных движений по тикерам.
Масштабирование на множество тикеров и защита от блокировок
Даже при обработанном рендеринге Yahoo отслеживает трафик, похожий на скрапер. Несколько привычек помогут сохранить здоровье длительного прогона, и они применимы к любой жёсткой коммерческой цели.
- Задавайте темп запросов. Интенсивный обход страниц котировок в плотном цикле является самым быстрым способом получить ограничение скорости или запрос на подтверждение. Задержки в две секунды выше являются минимумом, а не потолком; увеличивайте их для больших вотч-листов и чередуйте тикеры, а не просматривайте один путь на полной скорости.
- Опирайтесь на ротацию. Пул резидентских IP распределяет запросы по множеству реальных пользовательских адресов, чтобы ни один не превысил ограничение скорости. Crawling API делает это за вас; если вы строите собственный стек, именно здесь нужно всё сделать правильно.
-
Читайте коды состояния. Прогон, начавший возвращать не-200 значения
pc_status, говорит вам, что текущая частота или уровень IP больше недостаточны. Относитесь к этому как к сигналу снизить темп, а не как к шуму, который можно игнорировать.
Для большого вотч-листа асинхронный Crawler ставит запросы в очередь и доставляет результаты на вебхук, что удобно для получения сотен страниц котировок без удержания открытых соединений. Для общего руководства смотрите как парсить сайты без блокировок и заметки о крупномасштабном парсинге финансовых данных. Тот же паттерн разбора переносится на другие источники рыночных данных, например парсинг цен на криптовалюту с CoinMarketCap.
Законно ли парсить Yahoo Finance?
Разрешён ли парсинг Yahoo Finance, зависит от условий использования Yahoo, вашей юрисдикции и того, что вы делаете с данными. Условия Yahoo ограничивают автоматический доступ и массовый сбор данных, поэтому парсинг может нарушать эти условия вне зависимости от аккуратности вашего инструментария. Цифры на странице котировки являются фактическими рыночными данными, а не персональными данными, что снижает риски конфиденциальности, но не выводит вас за пределы условий сайта. Ни один из кодов здесь не меняет ничего из этого; он только делает рабочей техническую часть. Прочитайте Условия использования Yahoo Finance и его robots.txt и относитесь к обоим как к границе того, что вы собираете.
Более жёстким ограничением финансовых данных является лицензирование, а не конфиденциальность. Цены, рыночная капитализация и объём, которые отображает Yahoo, являются не собственными данными Yahoo: они поступают от бирж и сторонних поставщиков рыночных данных, и эти фиды имеют собственные ограничения на распространение. Получение числа с публичной страницы не даёт вам лицензии на его публикацию, перепродажу или построение на его основе коммерческого продукта. Оставайтесь на публичных страницах котировок и статистики, поддерживайте объём запросов достаточно скромным, чтобы не нагружать серверы Yahoo, и не парсите ничего за логином, платным или премиальным контентом, например Yahoo Finance Plus.
Это руководство намеренно ограничено публичными рыночными данными, поскольку именно это является линией, которая делает работу защищённой. Для чего-либо за пределами лёгкого исследования, разового анализа или личного вотч-листа правильным путём является лицензированный фид: Yahoo и его партнёры по данным, а также специализированные поставщики рыночных данных и биржевые API предлагают официальный доступ в соответствии с условиями для производственного использования. Если вы рассматриваете варианты, наш обзор лучших поставщиков финансовых данных в мире является хорошей отправной точкой. Лицензированный фид, а не более хитрый скрапер, является правильным путём для коммерческого или высокообъёмного использования.
Ключевые выводы
- Yahoo Finance использует клиентский рендеринг. Обычный запрос возвращает тонкую оболочку с заполнителями, поэтому страницу необходимо отрендерить перед разбором цены и статистики.
-
Нужны одновременно рендеринг и доверенный IP. Crawling API с JS-токеном выполняет оба в одном вызове;
ajax_waitиpage_waitуправляют временем ожидания живых данных. -
Разбирайте по стабильным атрибутам. Читайте заголовок по значениям
data-testidвродеqsp-price, а статистику по именамdata-fieldвродеmarketCap, которые лучше переживают изменения разметки, чем имена классов. - Перебирайте и экспортируйте. Итерируйте список тикеров, задавайте темп с короткими паузами и записывайте записи в JSON и CSV, чтобы данные сразу попадали в pandas или таблицу.
- Соблюдайте лицензирование. Цифры поступают от бирж и поставщиков данных с собственными условиями распространения; оставайтесь на публичных страницах и используйте лицензированный фид для любого коммерческого или высокообъёмного использования.
Часто задаваемые вопросы
Почему обычный запрос возвращает пустые цены с Yahoo Finance?
Потому что Yahoo загружает свою живую цену, изменение и таблицу статистики на стороне клиента с помощью JavaScript. Исходный HTML является оболочкой, которая заполняется лишь после выполнения скриптов страницы в браузере, поэтому сырой HTTP-запрос возвращает статус 200 с узлами-заполнителями или пустыми значениями. Чтобы получить реальные данные, необходимо сначала отрендерить страницу, что именно и делает JS-токен Crawling API.
Нужен ли мне обычный токен или JS-токен для Yahoo Finance?
JS-токен. Обычный токен получает статический HTML, который на странице котировки Yahoo представляет собой ту же тонкую оболочку, что и обычный запрос. JS-токен рендерит страницу в реальном браузере перед возвратом HTML, поэтому цена, изменение и статистика присутствуют при разборе BeautifulSoup.
Какие поля можно извлечь со страницы котировки Yahoo Finance?
Публичные рыночные данные на странице: последняя цена, абсолютное и процентное изменение относительно предыдущего закрытия, сама цена предыдущего закрытия, рыночная капитализация, объём и дневной диапазон. Это фактические поля котировки и статистики, а не персональные данные. Оставайтесь на публичных страницах котировок и избегайте всего, что находится за логином или премиальным тарифом.
Мои селекторы возвращают None. Что изменилось?
Скорее всего, разметка Yahoo. Сайт периодически переделывает макет котировок, и генерируемые имена классов меняются вместе с ним. Атрибуты data-testid (qsp-price, qsp-price-change) и имена data-field (marketCap, regularMarketVolume), используемые здесь, более стабильны, чем классы, но и они могут меняться. Заново проинспектируйте живую страницу в браузерных инструментах разработчика и обновите селекторы; периодическое обслуживание является нормой для любого производственного скрапера.
Как парсить много тикеров без блокировки?
Перебирайте список символов, сохраняйте короткую паузу между запросами и позвольте Crawling API ротировать резидентские IP, чтобы ни один адрес не превысил ограничение скорости. Для большого вотч-листа переходите на асинхронный Crawler, который ставит запросы в очередь и постит результаты на вебхук вместо удержания открытых соединений. Следите за заголовком pc_status и снижайте темп при появлении не-200 значений.
Можно ли коммерчески использовать спарсенные данные Yahoo Finance?
Рассматривайте это как юридический, а не технический вопрос. Цены и статистика на Yahoo поступают от бирж и лицензированных поставщиков рыночных данных с собственными условиями распространения, а Условия использования Yahoo ограничивают автоматический сбор и повторное использование данных. Для коммерческого или высокообъёмного использования правильным путём является лицензированный фид рыночных данных или официальный API, а не скрапер. Изучите условия и проконсультируйтесь с юристом, прежде чем строить продукт на основе этих данных.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

