Блог им. tomas_b88

Забираем данные по ценным бумагам с finance.yahoo.com Python класс в подарок.

 

Забираем данные по ценным бумагам с finance.yahoo.com
Простой способ на Python.

Продолжаю рассматривать способы получения данных по бумагам в свой скрипт. Из предыдущего поста где я рассказывал как можно просто буквально распарсить поисковую выдачу в гугле и вытащить текущие показатели цены я узнал по комментариям уважаемых резидентов смартлаба, что этот способ не будет хорошим решением, в силу особенности использования html тэгов и атрибутов таких как id класса. В конечном итогеid поменяется и скрипт работать не будет. Лучше посмотреть в сторону чего то более долгоиграющего. 


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

ticker_list = {'gazp': 'GAZP.ME',
               'sber': 'SBER.ME',
               'tatn': 'TATN.ME',
               'moex': 'MOEX.ME',
               'rosn': 'ROSN.ME',
               'lkoh': 'LKOH.ME',
               'yndx': 'YNDX.ME',
               'nlmk': 'NLMK.ME',
               'alrs': 'ALRS.ME',
               'rual': 'RUAL.ME',
               'magn': 'MAGN.ME'}


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

Забираем данные по ценным бумагам с finance.yahoo.com Python класс в подарок.



Я решил создать класс Ticker отдельно и использовать его в разных скриптах, когда мне это потребуется. 
Через этот класс мы реализуем создание всех отдельно запрашиваемых тикеров. 

Скрипт будет состоять из двух частей. yahooparser.py который содержит в себе класс, и main.py, в котором я просто продемонстрирую что можно с этим классом делать.
 
Данный пример, о котором дальше пойдет речь, в исходниках можно забрать ТУТ 



Воспользуемся ресурсом finance.yahoo.com и будем получать интересующие нас данные в формате JSON. для удобства работы с JSON информацией сразу рекомендую установить расширение для браузера JSON-handle. Это облегчит вам жизнь в поиске нужной информации.

Разберем запрос вида : https://query2.finance.yahoo.com/v10/finance/quoteSummary/GAZP.ME?modules=price  

Если установлен плагин, в  браузере мы получим вот такой красивый отформатированный древовидный JSON

Забираем данные по ценным бумагам с finance.yahoo.com Python класс в подарок. 


Тут мы видим в теле запроса тикер GAZP.ME, который мы в дальнейшем можем заменить в новом запросе на любой другой и получить точно такую же информацию по данному инструменту. При этом структура запроса остается неизменной. Это как раз то, что нам нужно.
Просто меняем GAZP.ME на SBER.ME и получаем всю информацию по Сбербанку. 

Теперь подумаем, как это можно отсюда забрать.
Я буду использовать способ из предыдущего примера и воспользуюсь библиотекой requests для формирования URL запроса. 
Если она еще не установлена, в консоли пишем pip install requests и устанавливаем.

Также сразу можно установить библиотеку dpath, которая поможет нам работать с полученным JSON, а точнее мы будем использовать её для того чтобы более удобно передвигаться внутри словарей и доставать нужные строчки без особых усилий. Аналогичным образом устанавливаем через pip

Теперь переходим к коду.
Создадим отдельный файл, в моем примере это yahooparser.py подключаем нужные библиотеки вначале

import requests
from dpath.util import values as path_val

Создаем класс Ticker и внутри него сразу опишем переменные для URL запроса

class Ticker:
    # -------- URL запрос будет через открытие сессии
    session = requests.session()

    # -------- заголовки для URL запроса
    headers = 'Mozilla/5.0 ' \
              '(Windows NT 10.0; WOW64) ' \
              'AppleWebKit/537.36' \
              ' (KHTML, like Gecko) ' \
              'Chrome/91.0.4472.135 ' \
              'Safari/537.36'

В отличии от моего предыдущего  парсера теперь мы устанавливаем сессию через requests.session(), иначе ничего работать не будет. Также в запрос мы добавим заголовки. Сохраним их предварительно в переменную headers. Ссылку с запросом мы пока объявлять не будем, а добавим её в метод, который опишем чуть позже.

Посмотрим в полученный выше JSON в браузере и определим какие нам нужно вытащить ключи. 

Забираем данные по ценным бумагам с finance.yahoo.com Python класс в подарок.


Я возьму оттуда 
regularMarketPrice - текущая цена, 
regularMarketChange - значениеизменения цены, 
regularMarketChangePercent — значение изменения цены, выраженное в процентах
regularMarketVolume — текущие объемы

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


Следующим шагом объявим словарь (все также внутри класса), который будет содержать в себе те поля, которые мы будем вытаскивать из полученного JSON.
Этот список вы можете редактировать на свое усмотрение и вытаскивать именно то, что нужно по вашим задачам. 

# -------- Запрашиваемые поля
    value = {'price': 'regularMarketPrice',
             'percent': 'regularMarketChangePercent',
             'change': 'regularMarketChange',
             'volume': 'regularMarketVolume'}

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

# -------- конструктор
    def __init__(self, name):
        # -------- определим первоначальные значения
        self.name = name
        self.price = 0.00
        self.change = 0.00
        self.percent = 0.0
        self.volume = 0.0

Также я описал переменную self.name значение которой экземпляр получит при его объявлении. 

Например gazp = Ticker('GAZP.ME') создаст экземпляр класса gazp с полем name, значение этого поля будет GAZP.ME  и  его мы будем передавать в URL запрос, добавляя его в ссылку запроса.


Следующим шагом опишем метод  __get_update(self) который будет обслуживать URL запросы, получать JSON, обрабатывать его, и возвращать все искомые значения по итогу своей работы. Здесь разместим ссылку для URL запроса. 

Переменнаяlink содержит ссылку запроса: 
query2.finance.yahoo.com/v10/finance/quoteSummary/{self.name}?modules=price"

def __get_update(self):
        """
            Отправляет URL запрос,  получает JSON
            возвращает список со значениями float
        """
        # -------- ссылка для URL запроса
        link = f"https://query2.finance.yahoo.com/v10/" \
               f"finance/quoteSummary/{self.name}?modules=price"

Всё готово для того, чтобы отправить URL запрос. используем session.get() передаем ей ссылку и заголовки.
Ответ запишем в response

# -------- отправляем запрос и получаем результат в response 
        response = self.session.get(link, headers={'User-Agent': self.headers})
response теперь содержит в себе полученный массив данных. Для того чтобы с этим можно было работать запишем его в виде словаря

array = response.json()
Данные получены, осталось только вытащить те поля, которые нам нужны. Объявим список return_value  для наполнения его искомыми значениями. 

В цикле for мы прогоним словарьself.value объявленный вначале класса, содержащий в себе те поля, которые мы будем искать в JSON. 

Для каждого ключа из этого словаря мы будем добавлять в список return_value значение, полученное в ходе работы функции values из dpath.util импорт которой мы определили в самом начале  какpath_val. Функция принимает наш массив данных, который сейчас лежит вarray и возвращает нам каждый проходself.value[key], то есть значение из словаря  по индексу ключа. Это значение содержит искомое поле

например, цикл доходит до{'percent': 'regularMarketChangePercent'} и возвращает значение regularMarketChangePercent по индексу percent, оно подставляется в дерево запроса списка. И далее нам нужно получить первый элемент c индексом [0].
Полученную строку конвертируем какfloat. 
Возвращаем список return_value в конце работы метода __get_update(self)

Выглядит это менее страшно, чем я объяснил.

# ---- создадим список, в который будут помещаться возвращаемые значения
        return_value = []

        # -------- перебором ключей словаря получим все нужные строки,
        #          и сконвертируем их в числа типа float
        for key in self.value:
            return_value.append(float(path_val(array, f"/**/{self.value[key]}/raw")[0]))

        # -------- возвращаем список из всех элементов по ключам из value
        return return_value

Создадим еще один метод update(self), который мы будем использовать для вызова __get_update(self)
Его задача просто быть вызванным через экземпляр класса и присвоить полям обновленные значения.

например,  gazp.update() обновит все значения в этом объекте. И можно просто обратиться например к цене print(gazp.price) которая покажет нам обновленное значение. Аналогичным образом можно получить значения других полей.

Значения обновляются через присвоение вызова __get_update(self) для полей класса. Так как в ходе работы возвращается список, то присваиваем переменным новые значения просто указывая их через запятую. 

def update(self):
        """
        Устанавливает обновленные значения
        полученные в результате работы _get_update
        """
        # присвоим возвращенный кортеж из функции _ger_update
        self.price, self.percent, self.change, self.volume = self.__get_update()

Вот и всё! наш класс готов и его можно использовать в разных ситуациях. Сохраним его отдельным файлом. В моем случае yahooparser.py. 


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

Создаем новый документ main.py

Для этого примера я использовал словарь с тикерами по которым собираюсь получить информацию.

Для наглядности представления вывода информации можно установить очень маленькую и очень простую но удобную библиотеку prettytable
Устанавливаем ее черезpip install prettytable

Импортируем всё что нам нужно
в первую очередь нам нужен наш созданный класс Ticker.
И prettytable

from yahooparser import Ticker
from prettytable import PrettyTable

Создадим словарь с тикерами которые будем скармливать в класс для создания экземпляров.

# ----------------------------------------------------
# в этот словарь можно добавить любые бумаги
# после запуска скрипта создадутся объекты с
# именами по индексам
# ----------------------------------------------------
ticker_list = {'gazp': 'GAZP.ME',
               'sber': 'SBER.ME',
               'tatn': 'TATN.ME',
               'moex': 'MOEX.ME',
               'rosn': 'ROSN.ME',
               'lkoh': 'LKOH.ME',
               'yndx': 'YNDX.ME',
               'nlmk': 'NLMK.ME',
               'alrs': 'ALRS.ME',
               'rual': 'RUAL.ME',
               'magn': 'MAGN.ME'}
# ----------------------------------------------------

Создадим функцию, которая будет получать этот словарь в аргументе и через for создаст нам все объекты. 

Первый for создает новый экземпляр класса с именем индекса словаряticker_list и передает ему в качестве аргумента значение словаря по индексу соответственно
например,  при проходе for создаетсяgazp = Ticker('GAZP.ME') и так далее по словарю

Второй for обращается к методу update для каждого вновь созданного объекта
например, при проходе for вызывается gazp.update() а что случается при этом вызове мы уже знаем 😁
Таким образом получаем актуальную информацию по всем тикерам. 

В конце работы функция возвращает список с объектами

def get_tickers(ticker_list):
    """
    Создает экземпляры класса Ticker из полученного списка
    :param ticker_list:  получает список тикеров
    :return: возвращает список с экземплярами класса Ticker
    """
    tickers = []
    for element in ticker_list:
        name = ticker_list[element]
        tickers.append(Ticker(name))

    for name in range(len(tickers)):
        tickers[name].update()
    return tickers

теперь вызовем эту функцию и поместим результат её работы в переменную tickers в которую и вернется список с созданными объектами.

# -------- создаем объекты тикеров
tickers = get_tickers(ticker_list)
Далее эти объекты и данные из них, вы можете использовать как вашей душе угодно. Полет фантазии не ограничен. 


Я приведу простой пример и просто распечатаю табличку с полученной информацией. 

Создадим функцию show_table(tickers) и в цикле for переберем полученный нами список из переменной tickers, которую нужно передать в аргументе при вызове.
При каждом проходеfor вытаскиваем значения полей из объектов и присваиваем в переменные.
(можно конечно этого и не делать и использовать обращения к полям, но это так, просто для примера).

Все результаты помещаем в список show_list элементами которого будут словари с элементом по индексуname и значением, состоящим из списка элементовprice,change,percent, volume

Значения переменных можно округлить до сотых для удобства представления данных. Также значение percent умножается на 100 чтобы отобразить значение в процентах

def show_table():
    """
    Демонстрация полученных данных в ходе работы скрипта
    :return: ничего не возвращает
    """
    # ----- считаем знрачения из класса в переменные
    show_list = []
    for ticker in tickers:
        name = ticker.name
        price = round(ticker.price, 2)
        change = round(ticker.change, 2)
        percent = round(ticker.percent * 100, 2)
        volume = round(ticker.volume, 2)
        # ----- создадим словарь со списком внутри
        show_list.append({name: [price, change, percent, volume]})
Данные отформатированы как нам нужно и теперь их можно распечатать
Для этого воспользуемся нашей prettytable  создаем новую таблицу myTable и объявляем для нее заголовки столбцов 

# ----- простая табличка - очень крутая вещь
    myTable = PrettyTable()

    # ----- создаем заголовки таблицы
    myTable.field_names = ["Name", "Price", "Change", "Perc. change", "Volume"]

Теперь просто через for перебираем наш список show_list, который мы сформировали парой строк выше и добавляем строки через myTable.add_row, в аргументах указывая список и его элементы. 
Напечатаем нашу таблицу в конце 
# ----- добавляем строки в таблицу
    for string in show_list:
        for key in string:
            myTable.add_row([key, string[key][0], string[key][1], string[key][2], string[key][3]])
    # печатаем таблицу с полученными значениями.
    print(myTable)

Теперь чтобы всё заработало вызываем эту функцию
# -------- распечатаем для примера
show_table(tickers)

Кто дочитал — молодец! 😁Ниже полный код класса и полный код main: 

yahooparser.py
import requests
from dpath.util import values as path_val


class Ticker:
    # -------- URL запрос будет через открытие сессии
    session = requests.session()

    # -------- заголовки для URL запроса
    headers = 'Mozilla/5.0 ' \
              '(Windows NT 10.0; WOW64) ' \
              'AppleWebKit/537.36' \
              ' (KHTML, like Gecko) ' \
              'Chrome/91.0.4472.135 ' \
              'Safari/537.36'

    # -------- Запрашиваемые поля
    value = {'price': 'regularMarketPrice',
             'percent': 'regularMarketChangePercent',
             'change': 'regularMarketChange',
             'volume': 'regularMarketVolume'}

    # -------- конструктор
    def __init__(self, name):
        # -------- определим первоначальные значения
        self.name = name
        self.price = 0.00
        self.change = 0.00
        self.percent = 0.0
        self.volume = 0.0

    def update(self):
        """
        Устанавливает обновленные значения
        полученные в результате работы _get_update
        """
        # присвоим возвращенный кортеж из функции _ger_update
        self.price, self.percent, self.change, self.volume = self.__get_update()

    def __get_update(self):
        """
            Отправляет URL запрос,  получает JSON
            возвращает список со значениями float
        """
        # -------- ссылка для URL запроса
        link = f"https://query2.finance.yahoo.com/v10/" \
               f"finance/quoteSummary/{self.name}?modules=price"

        # -------- отправляем запрос и получаем результат в response
        response = self.session.get(link, headers={'User-Agent': self.headers})

        # -------- получаем json массив
        array = response.json()

        # ---- создадим список, в который будут помещаться возвращаемые значения
        return_value = []

        # -------- перебором ключей словаря получим все нужные строки,
        #          и сконвертируем их в числа типа float
        for key in self.value:
            return_value.append(float(path_val(array, f"/**/{self.value[key]}/raw")[0]))

        # -------- возвращаем список из всех элементов по ключам из value
        return return_value



main.py
from yahooparser import Ticker
from prettytable import PrettyTable


def get_tickers(ticker_list):
    """
    Создает экземпляры класса Ticker из полученного списка
    :param ticker_list:  получает список тикеров
    :return: возвращает список с экземплярами класса Ticker
    """
    tickers = []
    for element in ticker_list:
        name = ticker_list[element]
        tickers.append(Ticker(name))

    for name in range(len(tickers)):
        tickers[name].update()
    return tickers


def show_table(tickers):
    """
    Демонстрация полученных данных в ходе работы скрипта
    :return: ничего не возвращает
    """
    # ----- считаем знрачения из класса в переменные
    show_list = []
    for ticker in tickers:
        name = ticker.name
        price = round(ticker.price, 2)
        change = round(ticker.change, 2)
        percent = round(ticker.percent * 100, 2)
        volume = round(ticker.volume, 2)
        # ----- создадим словарь со списком внутри
        show_list.append({name: [price, change, percent, volume]})

    # ----- эта простая табличка - очень крутая вещь
    myTable = PrettyTable()

    # ----- создаем заголовки таблицы
    myTable.field_names = ["Name", "Price", "Change", "Perc. change", "Volume"]

    # ----- добавляем строки в таблицу
    for string in show_list:
        for key in string:
            myTable.add_row([key, string[key][0], string[key][1], string[key][2], string[key][3]])
    # печатаем таблицу с полученными значениями.
    print(myTable)


# ----------------------------------------------------
# в этот словарь можно добавить любые бумаги
# после запуска скрипта создадутся объекты с
# аналогичными именами, для которых будут запрошены
# значения, после чего их можно использовать или распечатать
# ----------------------------------------------------
ticker_list = {'gazp': 'GAZP.ME',
               'sber': 'SBER.ME',
               'tatn': 'TATN.ME',
               'moex': 'MOEX.ME',
               'rosn': 'ROSN.ME',
               'lkoh': 'LKOH.ME',
               'yndx': 'YNDX.ME',
               'nlmk': 'NLMK.ME',
               'alrs': 'ALRS.ME',
               'rual': 'RUAL.ME',
               'magn': 'MAGN.ME'}
# ----------------------------------------------------


# -------- создаем экземпляры класса
tickers = get_tickers(ticker_list)

# -------- распечатаем для примера
show_table(tickers)
Данная публикация является личным мнением автора. Мнение владельца сайта может не совпадать с мнением автора.
5.2К | ★13
8 комментариев
А что скринеров штатных сайтовских мало, или там не передашь данные в свой скрипт, опять таки зачем это. Раз взглянул визуально.
the Rolling Stones, вопрос не в скринерах а в обработке значений, визуализация если не понятно, тут не преследуется. Преследуется обработка.
avatar
Что-то как-то сложно. Не пробовали использовать библиотеку yfinance?
avatar
Riskplayer, и что же тут сложного? если подумать головой и внимательно смотерть. (вот что сложно) то весь механизм закрыт в пяти строчках

response = self.session.get(link, headers={'User-Agent': self.headers})

array = response.json()

return_value = []

for key in self.value:
return_value.append(float(path_val(array, f"/**/{self.value[key]}/raw")[0]))

return return_value
 
Тут же я завернул это в нормальный доработанный подключаемый класс. и код по факту вырос до 40 строк. без комментов. 

avatar
Riskplayer, щас попробовал рекомендуемую вами библиотеку. Крутая штука попробую на её основе сделать несколько скриптов в следующие разы :) Норм тема
avatar
Riskplayer, вот тут попробовал :) в принципе норм.
avatar
Не то же самое? github.com/ranaroussi/yfinance
avatar
Денис Г., без понятия. попробую использовать в следующих экспериментах сначала нужно было пощупать своими руками так сказать что да как. Глубина понимания не помешает. 
avatar

Читайте на SMART-LAB:
Фото
С Днём России!
Россия — наша огромная страна, в которой живут представители десятков национальностей, культур и традиций. Несмотря на различия, всех...
Обновление кредитных рейтингов в ВДО и розничных облигациях (ООО «Интерлизинг» подтвержден ruA, ООО «Виллина» повышен B-|ru|)
🟢ООО «Интерлизинг» " Эксперт РА» подтвердил рейтинг кредитоспособности на уровне ruA со стабильным прогнозом. Ранее у Компании действовал...
Фото
Новые фьючерсы на Мосбирже: TSMC, SAP SE, Sony, ASML
На Мосбирже начались торги расчетными фьючерсными контрактами на американские депозитарные расписки (АДР) восьми крупнейших международных...
Фото
Интер РАО. МСФО Q1 2026г. Капекс растёт, рентабельность снижается…
Компания Интер РАО опубликовала финансовые результаты за Q1 2026г. по МСФО: 👉Выручка — 523,3 млрд руб. (+18,6% г/г) 👉Операционные...

теги блога tomas_b88

....все тэги



UPDONW
Новый дизайн