Двойная выгода

Динамич.+10 ГБ за каждые 50 ГБ

Статич.За 30 дн.: +7 дн. и 500 МБ в подарок

Купить >

Сообщение о безопасностиФишинговый сайт мошенничества (rola-ip.com ) подделка нашего старого официального сайта. Единственный официальный сайт — rola-ip.co(обратите внимание на.co(суффикс). Не производите выплаты на других доменах, чтобы избежать финансовых потерь!

Парсинг Yahoo Finance на Python: котировки и история цен

Adrian Cole

12 окт. 2026 г. · Руководства · 13 мин. чтения

Для анализа портфеля или проверки торговой стратегии мало скачать столбец с ценами. Нужно понять, к какой сессии относятся котировки, учтены ли дивиденды и сплиты, в какой валюте указан инструмент и за какие даты действительно получены данные. Иначе аккуратный график может опираться на несопоставимые значения.

Парсинг Yahoo Finance на Python удобно организовать двумя способами: библиотека yfinance подходит для получения структурированной истории котировок, а Selenium — для чтения таблицы, которую показывает браузер. Ниже разберём оба варианта на акциях Apple (AAPL), проверим результат и сохраним его в CSV для pandas и Excel.

Для русскоязычного аналитика особенно полезны три практические детали: сохранение биржевой даты без случайного сдвига в московский часовой пояс, явное указание валюты и экспорт с подходящими региональными разделителями. Разберём их вместе с типичными ошибками загрузки.

Публичная страница не означает неограниченное право на автоматический сбор и распространение данных. До запуска проверьте применимые условия Yahoo, правила доступа и ограничения использования. Для коммерческой системы отдельно уточните лицензию на рыночные данные; доступность страницы или библиотеки сама по себе её не предоставляет.

Какие данные можно получить из Yahoo Finance

На странице https://finance.yahoo.com/quote/AAPL/ рядом могут отображаться цена основной торговой сессии и котировки после её завершения. Поэтому самое заметное число на экране не стоит автоматически считать текущей биржевой ценой: проверьте подпись, время обновления и возможное уведомление о задержке.

История находится во вкладке Historical Data. Для дневных цен используется следующий набор полей:

Поле Что означает Что проверить при загрузке
Date Дата торгов или метка периода Часовой пояс биржи и выбранную частоту
Open Цена открытия Соответствие нужной сессии
High / Low Максимальная и минимальная цены Выполнение условия Low ≤ Open, Close ≤ High
Close Цена закрытия Правила корректировки у источника
Adj Close Скорректированная цена закрытия Учёт корпоративных действий
Volume Объём торгов Целочисленный тип; пропуск не равен нулю
Dividends / Stock Splits Дивиденды и дробления акций Отдельное хранение событий и цен

Исторические данные Yahoo Finance: диапазон дат, частота и таблица котировок

Скриншоты иллюстрируют интерфейс на момент съёмки. Показанные на них цены нельзя использовать как актуальные котировки.

Close и Adj Close: какой столбец нужен для анализа

В Yahoo Finance поле Close обычно уже учитывает дробления акций, а Adj Close дополнительно отражает дивиденды и другие применимые корректировки. Точное описание нужно сверять с пояснениями источника. Называть Close полностью «необработанной» ценой некорректно.

Для сравнения закрытий используйте Close с учётом его методологии. Для анализа доходности на длинном горизонте часто удобнее Adj Close: дивидендный разрыв не будет выглядеть как обычное падение стоимости без выплаты инвестору. При этом скорректированный ряд не заменяет полный расчёт доходности портфеля с комиссиями, налогами, валютной переоценкой и выбранной моделью реинвестирования.

Не прибавляйте дивиденды повторно к доходности, в которой они уже учтены через Adj Close. Для бэктеста также сохраняйте дату выгрузки: исторические корректировки могут пересчитываться после новых корпоративных событий.

Валюта и часовой пояс: важные детали для русскоязычных пользователей

Язык интерфейса и регион подключения не определяют валюту инструмента. Не записывайте RUB только потому, что результат открывают в русской версии Excel. Для пересчёта доходности в рубли нужен отдельный валютный ряд с согласованными датами и методикой.

Американские биржи работают по своему календарю, а переход на летнее время меняет разницу с Москвой. Для дневной истории сохраняйте дату сессии в часовом поясе биржи. Для внутридневных данных оставляйте временную метку с часовым поясом; московское время можно добавить отдельным столбцом. Время получения файла удобно фиксировать в UTC.

Отсутствие строки в выходной или биржевой праздник нормально. В свою очередь, наличие тикера в Yahoo Finance не гарантирует, что инструмент доступен для торговли у конкретного брокера.

Что выбрать: yfinance или Selenium

Задача Подход Ограничение
Получить историю цен для анализа в pandas yfinance Неофициальная библиотека; зависит от доступности сервисов Yahoo
Проверить данные именно в таблице браузера Selenium Разметка и загрузка страницы могут меняться
Повторно разобрать уже сохранённую страницу Локальный HTML-снимок Содержит только данные, попавшие в сохранённый снимок

Для исследовательского скрипта обычно удобнее начать с yfinance. Selenium имеет смысл, когда важен именно видимый результат: например, нужно сопоставить строки таблицы с выбранными параметрами интерфейса.

Обычный requests.get() не гарантирует получение готовой таблицы: часть содержимого загружается динамически. Ответ без нужных строк или HTTP 429 — повод проверить способ доступа, а не запускать бесконечные повторы.

Подготовка Python

Создайте отдельное виртуальное окружение. В Windows PowerShell:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install pandas yfinance selenium requests

В Linux и macOS:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install pandas yfinance selenium requests

Если PowerShell запрещает активацию, можно запускать Python окружения напрямую: .\.venv\Scripts\python.exe. Менять общесистемную политику выполнения ради одного скрипта необязательно.

После успешной проверки сохраните версии зависимостей командой python -m pip freeze > requirements.txt. Для воспроизводимости также запишите версию Python, операционную систему и, при работе с Selenium, версию браузера.

Загрузка исторических котировок через yfinance

yfinance — независимый проект, а не официальный Yahoo Finance API или SDK. В документации yfinance указаны отсутствие связи с Yahoo и ограничения использования данных, включая назначение API для личного использования. Возможность скачать ряд не означает, что его разрешено перепродавать или публиковать в коммерческом продукте.

Создайте файл yahoo_history.py со следующим кодом. Он получает дневные данные AAPL за три месяца, оставляет Close и Adj Close раздельно и подготавливает две таблицы: data с ценами и event_data с корпоративными событиями.

from datetime import datetime, timezone
import pandas as pd
import yfinance as yf

SYMBOL = "AAPL"
PRICE_COLUMNS = ["Open", "High", "Low", "Close", "Adj Close", "Volume"]

ticker = yf.Ticker(SYMBOL)
try:
    history = ticker.history(
        period="3mo",
        interval="1d",
        auto_adjust=False,
        actions=True,
        timeout=20,
        raise_errors=True,
    )
    source_meta = ticker.get_history_metadata()
except Exception as exc:
    message = str(exc).lower()
    if "429" in message or "rate limit" in message or "too many requests" in message:
        raise RuntimeError(
            "Достигнут лимит запросов. Остановите загрузку и выдержите паузу."
        ) from exc
    raise

if history.empty:
    raise RuntimeError("Источник вернул пустую историю котировок")
missing = set(PRICE_COLUMNS).difference(history.columns)
if missing:
    raise RuntimeError(f"Отсутствуют столбцы: {sorted(missing)}")

currency = source_meta.get("currency")
exchange_timezone = source_meta.get("exchangeTimezoneName")
if not currency or not exchange_timezone:
    raise RuntimeError("Не удалось подтвердить валюту или часовой пояс биржи")
if history.index.tz is None:
    raise RuntimeError("Получен индекс без часового пояса")

# Сохраняем дату сессии в часовом поясе биржи.
dates = history.index.tz_convert(exchange_timezone).strftime("%Y-%m-%d")
data = history[PRICE_COLUMNS].copy()
data.insert(0, "Date", dates)
data = data.reset_index(drop=True)

events = []
for event_name in ("Dividends", "Stock Splits", "Capital Gains"):
    if event_name in history.columns:
        for date, value in zip(dates, history[event_name]):
            if pd.notna(value) and value != 0:
                events.append({
                    "Date": date,
                    "Symbol": SYMBOL,
                    "Event": event_name,
                    "Value": float(value),
                })
event_data = pd.DataFrame(events, columns=["Date", "Symbol", "Event", "Value"])

metadata = {
    "symbol": SYMBOL,
    "source": "yfinance",
    "currency": currency,
    "exchange_timezone": exchange_timezone,
    "requested_period": "3mo",
    "frequency": "1d",
    "adjustment": "auto_adjust=False",
    "retrieved_at": datetime.now(timezone.utc).isoformat(),
}
print(data.tail())

Параметр auto_adjust=False задан явно. При включённой автоматической корректировке меняются значения ценовых полей и состав столбцов; полагаться на настройку по умолчанию между версиями библиотеки не стоит.

Для фиксированного интервала вместо period можно передать start="2025-01-01", end="2026-01-01". В yfinance граница end не включается. Проверяйте первую и последнюю фактически полученные даты, особенно если границы попадают на выходные или праздники.

Этот пример предназначен для дневного ряда. У внутридневных интервалов другие ограничения глубины истории; простая замена 1d на 1m не гарантирует минутные данные за тот же период.

Парсинг таблицы Yahoo Finance через Selenium

Selenium открывает браузер и читает отрисованную таблицу Historical Data. Это полезно для сверки с интерфейсом, но не доказывает, что браузер загрузил весь выбранный период.

Настройте диапазон и частоту

На странице https://finance.yahoo.com/quote/AAPL/history/ нажмите на диапазон дат, выберите готовый период или задайте границы вручную, затем подтвердите выбор кнопкой Done. Для первого запуска достаточно одного–трёх месяцев.

Выбор начальной и конечной даты в разделе Historical Data Yahoo Finance

В списке частоты доступны Daily, Weekly и Monthly. Недельные и месячные OHLC-данные описывают соответствующий период, поэтому их нельзя считать произвольно выбранными дневными строками.

Выбор дневной, недельной или месячной частоты исторических котировок Yahoo Finance

Пример ниже открывает новое окно Chrome и приостанавливается для настройки этой страницы. Параметры из другой вкладки автоматически не переносятся. После выбора диапазона дождитесь обновления таблицы; если используется подгрузка при прокрутке, загрузите нужные строки до продолжения.

Прочитайте цены и корпоративные события

Сохраните код в yahoo_selenium.py. Chrome должен быть установлен; совместимый драйвер современные версии Selenium обычно находят через Selenium Manager. В среде с ограничением загрузок драйвер потребуется подготовить заранее.

from datetime import datetime, timezone
from decimal import Decimal
import math
import re
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

SYMBOL = "AAPL"
URL = f"https://finance.yahoo.com/quote/{SYMBOL}/history/"
COLUMNS = ["Date", "Open", "High", "Low", "Close", "Adj Close", "Volume"]
MONTHS = dict(zip(
    "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(), range(1, 13)
))

def parse_date(text):
    # Не зависит от русской или английской системной локали Python.
    month, day, year = text.replace(",", "").split()
    return datetime(int(year), MONTHS[month], int(day)).date().isoformat()

def parse_number(text, integer=False):
    value = text.strip().replace(",", "")
    if value in {"", "-", "--", "N/A"}:
        return None
    if integer:
        number = Decimal(value)
        if not number.is_finite() or number != number.to_integral_value():
            raise ValueError(f"Некорректный объём: {text}")
        return int(number)
    number = float(value)
    if not math.isfinite(number):
        raise ValueError(f"Некорректное число: {text}")
    return number

options = webdriver.ChromeOptions()
options.add_argument("--lang=en-US")
driver = webdriver.Chrome(options=options)
try:
    driver.set_page_load_timeout(30)
    driver.get(URL)
    input("Выберите период и частоту в открытом Chrome, дождитесь таблицы и нажмите Enter: ")
    selected_range = input("Диапазон дат, показанный на странице: ").strip()
    frequency = input("Частота (Daily / Weekly / Monthly): ").strip()
    currency = input("Код валюты со страницы, например USD: ").strip().upper()
    if not selected_range or frequency not in {"Daily", "Weekly", "Monthly"} or not currency:
        raise ValueError("Заполните параметры по фактическим значениям страницы")

    table = WebDriverWait(driver, 30).until(
        EC.visibility_of_element_located((
            By.XPATH,
            "//table[.//th[contains(normalize-space(.),'Date')] "
            "and .//th[contains(normalize-space(.),'Adj Close')]]"
        ))
    )
    headers = [
        re.sub(r"[\s*]+", "", cell.text)
        for cell in table.find_elements(By.CSS_SELECTOR, "thead th")
    ]
    if headers != [name.replace(" ", "") for name in COLUMNS]:
        raise ValueError(f"Изменились заголовки таблицы: {headers}")

    prices, events = [], []
    for row in table.find_elements(By.CSS_SELECTOR, "tbody tr"):
        cells = [cell.text.strip() for cell in row.find_elements(By.CSS_SELECTOR, "td")]
        if not cells:
            continue
        date = parse_date(cells[0])
        if len(cells) == 2 and any(
            label in cells[1].lower() for label in ("dividend", "split", "capital gain")
        ):
            events.append({"Date": date, "Symbol": SYMBOL, "Event": cells[1]})
        elif len(cells) == 7:
            values = [parse_number(value) for value in cells[1:6]]
            values.append(parse_number(cells[6], integer=True))
            prices.append(dict(zip(COLUMNS, [date] + values)))
        else:
            raise ValueError(f"Неизвестный формат строки: {cells}")

    data = pd.DataFrame(prices, columns=COLUMNS)
    event_data = pd.DataFrame(events, columns=["Date", "Symbol", "Event"])
    metadata = {
        "symbol": SYMBOL,
        "source": URL,
        "currency": currency,
        "selected_range": selected_range,
        "frequency": frequency,
        "coverage": "rendered_rows_only",
        "retrieved_at": datetime.now(timezone.utc).isoformat(),
    }
finally:
    driver.quit()

print(data.tail())

Для этого парсера нужны английские названия месяцев и заголовков в самой странице. Параметр --lang=en-US выражает предпочтение языка браузера, но не гарантирует выбор языка сайтом; проверьте его вручную. При изменении разметки код остановится, а не сохранит значения под неверными заголовками.

Разбор дат не использует %b из strptime: английское сокращение месяца может не распознаться при русской системной локали. Числовой парсер тоже рассчитан именно на формат этой таблицы: запятая разделяет тысячи, точка — дробную часть.

Строки с дивидендами и сплитами сохраняются отдельно. В Selenium это текст события, в yfinance — тип события и числовое значение. Для объединения этих наборов сначала потребуется привести события к общей схеме.

Проверка и выгрузка данных Yahoo Finance в CSV

Добавьте следующий блок в конец выбранного скрипта — yahoo_history.py или yahoo_selenium.py. Оба создают переменные data, event_data и metadata, которые нужны для проверки и экспорта.

import json
import math
from pathlib import Path
import pandas as pd

if data.empty:
    raise RuntimeError("Таблица цен пуста")
if data["Date"].isna().any() or data["Date"].duplicated().any():
    raise ValueError("Обнаружены пропуски или дубликаты дат")
pd.to_datetime(data["Date"], format="%Y-%m-%d", errors="raise")

price_columns = ["Open", "High", "Low", "Close", "Adj Close"]
for column in price_columns + ["Volume"]:
    data[column] = pd.to_numeric(data[column], errors="raise")
    if not data[column].dropna().map(math.isfinite).all():
        raise ValueError(f"Неконечные значения в столбце {column}")

ohlc = data[["Open", "High", "Low", "Close"]].dropna()
valid = (
    (ohlc["Low"] <= ohlc[["Open", "Close"]].min(axis=1))
    & (ohlc[["Open", "Close"]].max(axis=1) <= ohlc["High"])
)
if not valid.all():
    raise ValueError("Нарушены соотношения OHLC")

volume = data["Volume"].dropna()
if (volume < 0).any() or (volume % 1 != 0).any():
    raise ValueError("Объём должен быть неотрицательным целым числом")
data["Volume"] = data["Volume"].astype("Int64")
data = data.sort_values("Date").reset_index(drop=True)
metadata.update({
    "first_date": data["Date"].iloc[0],
    "last_date": data["Date"].iloc[-1],
    "row_count": len(data),
    "missing_values": {
        key: int(value) for key, value in data.isna().sum().items()
    },
})

output = Path("output")
output.mkdir(exist_ok=True)
prefix = f"{metadata['symbol']}-{datetime.now(timezone.utc):%Y%m%dT%H%M%S%fZ}"
csv_path = output / f"{prefix}-history.csv"
data.to_csv(csv_path, index=False, encoding="utf-8-sig")
event_data.to_csv(
    output / f"{prefix}-events.csv", index=False, encoding="utf-8-sig"
)
# Дополнительная версия для Excel с русскими региональными настройками.
data.to_csv(
    output / f"{prefix}-history-ru.csv",
    index=False, encoding="utf-8-sig", sep=";", decimal=","
)
with (output / f"{prefix}-metadata.json").open("w", encoding="utf-8") as handle:
    json.dump(metadata, handle, ensure_ascii=False, indent=2)

check = pd.read_csv(csv_path, dtype={"Date": "string", "Volume": "Int64"})
if list(check.columns) != list(data.columns) or len(check) != len(data):
    raise RuntimeError("После экспорта изменились столбцы или число строк")
if check["Date"].tolist() != data["Date"].tolist():
    raise RuntimeError("После экспорта изменились даты")
print(f"Сохранено строк: {len(data)}; файл: {csv_path}")
print("Пропуски:", metadata["missing_values"])

Запустите выбранный файл командой python yahoo_history.py или python yahoo_selenium.py. В каталоге output появятся цены, события, метаданные и отдельная версия цен для русских региональных настроек Excel. Метка времени в имени позволяет сохранять несколько выгрузок для последующего сравнения.

В русской версии Excel надёжнее использовать Данные → Из текста/CSV. Для обычного файла выберите UTF-8, запятую как разделитель и регион интерпретации чисел с десятичной точкой. Для файла history-ru.csv используйте точку с запятой и десятичную запятую. Реальное поведение зависит от региональных настроек Windows и Excel, а не только от языка меню.

Кодировка utf-8-sig помогает корректно распознавать кириллицу, но сама по себе не выбирает разделитель. После импорта проверьте, что цены стали числами, Date не потерял смысл, а Close и Adj Close остались разными столбцами.

Как убедиться, что выгрузка полная

Проверка OHLC выявляет часть ошибок разбора, но не подтверждает полноту истории. Перед анализом:

  1. Сопоставьте фактические первую и последнюю даты с запрошенным диапазоном.
  2. Проверьте несколько строк по странице или другому разрешённому источнику при одинаковых настройках корректировки.
  3. Сверьте пропуски с календарём нужной биржи, а не с обычным списком будних дней.
  4. Убедитесь, что последняя строка не относится к ещё не завершившейся сессии.
  5. Изучите число пропусков в метаданных. Не заменяйте отсутствующую цену нулём.

Для регулярной загрузки храните локальный кэш и обновляйте данные с небольшим перекрытием дат. Учитывайте, что после дивидендов или сплитов может понадобиться обновить и более старые скорректированные значения. Если рассчитываете бэктест, сохраняйте использованный снимок данных.

Ошибка 429 и другие проблемы при парсинге Yahoo Finance

Yahoo Finance или yfinance возвращает 429

HTTP 429 означает ограничение запросов. Причиной может быть частота обращений, суммарная нагрузка с общего внешнего IP или политика сервиса. Это не признак неправильной установки Python.

Остановите задачу и сохраните время ошибки. Если доступен заголовок Retry-After, выдержите указанную паузу: его значение может быть числом секунд или HTTP-датой. Когда yfinance не предоставляет исходные заголовки ответа, нельзя выдавать выбранную самостоятельно паузу за рекомендацию сервера.

Перед повторным запуском сократите параллелизм, проверьте другие задачи с того же IP и используйте кэш. Универсальной гарантированно допустимой частоты для всех пользователей нет. Бесконечные повторы и смена IP после каждой ошибки не решают вопрос разрешённого доступа.

Страница открывается, но Selenium не находит таблицу

Проверьте окно браузера: загрузку может прерывать диалог согласия, страница входа или сообщение об ограничении доступа. Затем проверьте язык, заголовки и структуру таблицы. Начинайте с видимого браузера; включайте headless-режим после проверки сценария.

Получено меньше строк, чем ожидалось

Проверьте частоту и выбранный диапазон. При Weekly и Monthly строк будет меньше, чем при Daily. В браузере часть таблицы может подгружаться только при прокрутке. Приведённый Selenium-скрипт считывает уже отрисованные строки и не выполняет автоматическую прокрутку.

Кнопка Download недоступна или помечена замком

Доступность скачивания зависит от текущих условий продукта, региона и учётной записи. Не стройте процесс на предположении, что эта кнопка всегда бесплатно выдаёт CSV. Выберите доступный разрешённый способ получения данных или лицензированный источник.

Котировка вверху страницы отличается от последней строки истории

Сравните дату, время, валюту, основную и дополнительную торговые сессии, задержку обновления и корректировки. Котировка после закрытия не обязана совпадать с Close основной сессии.

Когда для сбора данных нужен прокси Rola IP

Прокси может быть частью сетевой настройки: например, команде нужен согласованный внешний IP для повторяемых проверок или определённый регион подключения. Он не предоставляет права на данные и не гарантирует, что Yahoo Finance примет запрос.

Для задач с постоянным внешним адресом можно рассмотреть статические резидентские прокси Rola IP. Если разрешённый сценарий предполагает выбор географии подключения, условия и доступные варианты описаны на странице резидентских прокси.

Страница статических резидентских прокси Rola IP

Перед интеграцией сверьте инструкцию по подключению прокси в Python и параметры подключения. Учётные данные храните в переменных окружения, а не в коде и скриншотах.

Следующий независимый пример проверяет HTTP-прокси через сервис определения IP. Предварительно задайте ROLA_PROXY_HOST, ROLA_PROXY_PORT, ROLA_PROXY_USER, ROLA_PROXY_PASSWORD и ожидаемый двухбуквенный код страны в ROLA_EXPECTED_COUNTRY.

Сохраните Python-код ниже в файл verify_proxy.py. Хост, порт и данные для авторизации возьмите из панели Rola IP. В ROLA_PROXY_HOST укажите только имя хоста, без http:// и порта. Задайте переменные в той же консоли, из которой будете запускать скрипт, заменив все значения YOUR_... своими данными.

В Windows PowerShell:

$env:ROLA_PROXY_HOST = "YOUR_PROXY_HOST"
$env:ROLA_PROXY_PORT = "YOUR_PROXY_PORT"
$env:ROLA_PROXY_USER = "YOUR_PROXY_USER"
$env:ROLA_PROXY_PASSWORD = "YOUR_PROXY_PASSWORD"
$env:ROLA_EXPECTED_COUNTRY = "US" # Укажите страну вашего подключения

В Linux и macOS:

export ROLA_PROXY_HOST='YOUR_PROXY_HOST'
export ROLA_PROXY_PORT='YOUR_PROXY_PORT'
export ROLA_PROXY_USER='YOUR_PROXY_USER'
export ROLA_PROXY_PASSWORD='YOUR_PROXY_PASSWORD'
export ROLA_EXPECTED_COUNTRY='US' # Укажите страну вашего подключения

Не сохраняйте реальные учётные данные в общем журнале команд или скриншотах. На рабочем сервере задавайте секреты через предусмотренный для этого механизм окружения.

import os
from urllib.parse import quote
import requests

host = os.environ["ROLA_PROXY_HOST"]
port = os.environ["ROLA_PROXY_PORT"]
user = quote(os.environ["ROLA_PROXY_USER"], safe="")
password = quote(os.environ["ROLA_PROXY_PASSWORD"], safe="")
expected_country = os.environ["ROLA_EXPECTED_COUNTRY"].upper()
proxy_url = f"http://{user}:{password}@{host}:{port}"

with requests.Session() as session:
    session.trust_env = False
    session.proxies.update({"http": proxy_url, "https": proxy_url})
    response = session.get("https://ipinfo.io/json", timeout=20)
    errors = {
        401: "Сервис проверки IP требует авторизацию (401).",
        403: "Сервис проверки IP отказал в доступе (403). Проверьте правила доступа.",
        407: "Прокси требует авторизацию (407). Проверьте логин и пароль прокси.",
    }
    if response.status_code in errors:
        raise RuntimeError(errors[response.status_code])
    if response.status_code == 429:
        retry_after = response.headers.get("Retry-After", "не указан")
        raise RuntimeError(f"Проверка ограничена: 429; Retry-After: {retry_after}")
    response.raise_for_status()
    country = response.json().get("country")
    if country != expected_country:
        raise RuntimeError(f"Неожиданная страна подключения: {country}")
    print("Страна подключения:", country)

Запустите python verify_proxy.py из активированного окружения. Успешная проверка завершается без исключения и выводит Страна подключения: с ожидаемым кодом страны. При несовпадении страны скрипт остановится с ошибкой; HTTP 200 сам по себе не считается достаточным результатом.

Эта проверка относится только к созданной сессии Requests. Она не настраивает прокси автоматически для Selenium или yfinance: каждому клиенту требуется отдельная конфигурация с учётом его версии и способа аутентификации. Для SOCKS5 также нужна поддержка соответствующего протокола в клиенте.

Успешный ответ сервиса определения IP подтверждает работу этого соединения, но не доступность Yahoo Finance и не точность геолокации у всех сервисов. Если проверка завершилась ошибкой, сначала определите её источник:

Ошибка Что означает и что проверить
HTTP 401 Сервис, вернувший ответ, требует авторизацию. Проверьте его требования к токену или учётным данным; это не то же самое, что авторизация на прокси.
HTTP 403 Сервис отказал в доступе. Проверьте его правила и текст ответа; один код 403 не позволяет установить причину или считать IP некачественным.
HTTP 407 Требуется авторизация на прокси. Проверьте логин, пароль, порт и выбранный способ доступа. При создании HTTPS-туннеля CONNECT ошибка может проявиться как requests.exceptions.ProxyError, без объекта response.
HTTP 429 Достигнуто ограничение запросов. Остановите задачу, проверьте Retry-After и снизьте частоту перед следующим запуском.
Ошибка DNS или отказ в соединении Проверьте имя хоста, порт, доступность сети и правила межсетевого экрана. Уточните причину по вложенному исключению ConnectionError или ProxyError, не публикуя журнал с учётными данными.
Тайм-аут Проверьте, на каком этапе прервалось соединение: подключение или ожидание ответа. Сверьте доступность сервиса и настройку timeout; не запускайте бесконечные повторы.

После исправления конфигурации выполните одну повторную проверку; при 429 сначала выдержите требуемую паузу. Сопоставьте полученную страну с ROLA_EXPECTED_COUNTRY и зафиксируйте фактический результат.

Итоги

Надёжный парсинг Yahoo Finance начинается с определения полей и правил расчёта. Для анализа истории используйте yfinance с явными параметрами корректировки, для проверки браузерной таблицы — Selenium. Сохраняйте цены, события и метаданные отдельно, проверяйте торговые даты и полноту периода, а формат CSV выбирайте под инструмент, в котором будете работать. Это позволит повторить выгрузку и объяснить результат анализа, а не только получить файл с числами.

Часто задаваемые вопросы

Готовы начать сбор данных в большом масштабе?

Попробовать бесплатно