Пишем многопоточную программу на Python

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

Мы разработаем многопоточную программу, которая считывает цены на акции с сайта Yahoo Finance.

Создаем многопоточную программу

Для создания программы, которая считывает цены на акции с сайта Yahoo Finance, воспользуемся двумя сторонними пакетами:

  • requests — для получения содержимого веб-страницы.
  • lxml — для выбора определенного элемента HTML-документа.

Сначала установим модули requests и lxml с помощью команды pip:

pip install request lxml

Теперь создадим новый класс Stock, который наследуется от класса Thread модуля threading. Поместим класс Stock в модуль stock.py:

import threading

class Stock(threading.Thread):
   pass

Теперь реализуем метод __init__(), который принимает символ и инициализирует переменную экземпляра url на основе этого символа:

import threading
import requests
from lxml import html


class Stock(threading.Thread):
    def __init__(self, symbol: str) -> None:
        super().__init__()

        self.symbol = symbol
        self.url = f'https://finance.yahoo.com/quote/{symbol}'
        self.price = None

Например, если передать GOOG в метод __init__(), URL будет выглядеть так:

https://finance.yahoo.com/quote/GOOG

Затем переопределим метод run() класса Thread. Метод run() получает содержимое из self.url и «выцепляет» цену акций:

import threading
import requests
from lxml import html


class Stock(threading.Thread):
    def __init__(self, symbol: str) -> None:
        super().__init__()

        self.symbol = symbol
        self.url = f'https://finance.yahoo.com/quote/{symbol}'
        self.price = None

    def run(self):
        response = requests.get(self.url)
        if response.status_code == 200:
            # парсим HTML
            tree = html.fromstring(response.text)
            # получаем цену в виде текста
            price_text = tree.xpath(
                '//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]/text()')
            if price_text:
                try:
                    self.price = float(price_text[0].replace(',', ''))
                except ValueError:
                    self.price = None

Как это работает

1. Выполняем запрос к URL с помощью метода requests.get():

response = requests.get(self.url)

2. Если запрос успешен, код состояния HTTP равен 200. В этом случае мы получаем HTML-содержимое из ответа и передаем его в функцию fromstring() модуля html из пакета lxml:

if response.status_code == 200:
   tree = html.fromstring(response.text)

3. Получаем цену из текста.

Любой элемент на веб-странице можно выбрать с помощью так называемого XPath.

Как получить XPath элемента страницы с помощь Google Chrome. ПКМ на странице → «Просмотреть код» → Выберите элемент → ПКМ по элементу → Copy → Copy XPath. 

XPath цены акций на момент написания этого руководства выглядит следующим образом:

//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]

Чтобы получить текст элемента, нужно добавить text() в конец XPath:

//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]/text()

Примечание. Если Yahoo изменит структуру страницы, вам необходимо будет соответствующим образом изменить XPath. В противном случае программа не будет работать так, как ожидается.

Итак, получаем цену из текста:

price_text = tree.xpath('//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]/text()')

4. Удаляем запятую и преобразуем текст в число.

if price_text:
    try:
        self.price = float(price_text[0].replace(',', ''))
    except ValueError:
        self.price = None

5. Добавляем метод __str__(), который возвращает строковое представление объекта Stock

import threading
import requests
from lxml import html


class Stock(threading.Thread):
    def __init__(self, symbol: str) -> None:
        super().__init__()

        self.symbol = symbol
        self.url = f'https://finance.yahoo.com/quote/{symbol}'
        self.price = None

    def run(self):
        response = requests.get(self.url)
        if response.status_code == 200:
            # parse the HTML
            tree = html.fromstring(response.text)
            # get the price in text
            price_text = tree.xpath(
                '//*[@id="quote-header-info"]/div[3]/div[1]/div[1]/fin-streamer[1]/text()')
            if price_text:
                try:
                    self.price = float(price_text[0].replace(',', ''))
                except ValueError:
                    self.price = None

    def __str__(self):
        return f'{self.symbol}\t{self.price}'

Используем многопоточную программу

Импортируем модуль stock.py в основной модуль main.py и используем в нем класс Stock:

from stock import Stock

symbols = ['MSFT', 'GOOGL', 'AAPL', 'META']
threads = []

for symbol in symbols:
    t = Stock(symbol)
    t.start()
    threads.append(t)

for t in threads:
    t.join()
    print(t)

Вывод

MSFT    253.67
GOOGL   2280.41
AAPL    145.86
META    163.27

Как это работает

1. Импортируем класс Stock из модуля stock.py:

from stock import Stock

2. Задаем список символов акций, которые будем считывать:

symbols = ['MSFT', 'GOOGL', 'AAPL', 'META']

3. Создаем поток для каждого символа, запускаем его и добавляем поток в список потоков:

threads = []
for symbol in symbols:
    t = Stock(symbol)
    t.start()
    threads.append(t)

4. Дожидаемся завершения всех потоков в списке потоков и выводим на экран цену акций:

for t in threads:
    t.join()
    print(t)
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©