#!/usr/bin/env python3 # stockquotes - Python module to pull stock quotes from Yahoo! Finance # # Copyright 2020-2023 Samuel Sloniker # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted. # # THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. from bs4 import BeautifulSoup as bs import requests import datetime class StockDoesNotExistError(Exception): pass class NetworkError(Exception): pass class Stock: def __init__(self, ticker): try: r = requests.get( f"https://finance.yahoo.com/quote/{ticker}/history", headers={ "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0" }, ) except: raise NetworkError() if r.status_code == 302: raise StockDoesNotExistError(ticker) try: soup = bs(r.text, features="lxml") self.symbol = soup.h1.string.split("(")[1].split(")")[0] self.name = soup.h1.string.split("(")[0].strip() rows = soup.table.tbody.find_all("tr") self.historical = [] for i in rows: row = i.find_all("td") try: parsed = { "date": datetime.datetime.strptime( row[0].span.string, "%b %d, %Y" ), "open": float(row[1].span.string.replace(",", "")), "high": float(row[2].span.string.replace(",", "")), "low": float(row[3].span.string.replace(",", "")), "close": float(row[4].span.string.replace(",", "")), "adjusted_close": float( row[5].span.string.replace(",", "") ), "volume": int(row[6].span.string.replace(",", "")) if row[6].string != "-" else None, } except: continue self.historical.append(parsed) price_selector = f'fin-streamer[data-field="regularMarketPrice"][data-symbol="{self.symbol}"]' price_element = soup.select_one(price_selector) self.current_price = float(price_element.text) change_selector = f'fin-streamer[data-field="regularMarketChange"][data-symbol="{self.symbol}"]' change_element = soup.select_one(change_selector) self.increase_dollars = float(change_element.text) change_percent_selector = f'fin-streamer[data-field="regularMarketChangePercent"][data-symbol="{self.symbol}"]' change_percent_element = soup.select_one(change_percent_selector) change_percent_text = "".join( [ char for char in change_percent_element.text if char in "-.0123456789" ] ) self.increase_percent = float(change_percent_text) except AttributeError as error: raise StockDoesNotExistError(ticker) from error