Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
90353f1251 | |||
148f59664e | |||
6225cf7fd0 | |||
3a6b6d26d5 | |||
b8fd0276df | |||
5f23f1c04c | |||
14be2b99c5 | |||
1bd1ced732 | |||
9f1bd098bc | |||
43a0a9408e | |||
204a91b1f6 | |||
879ac760d0 | |||
8b202c4fb2 | |||
0d12a89e58 | |||
518692c69f | |||
|
6225cecf45 |
|
@ -1,3 +1,6 @@
|
||||||
|
# Branches
|
||||||
|
All PRs should be made against the `devel` branch.
|
||||||
|
|
||||||
# `black`
|
# `black`
|
||||||
Be sure to run
|
Be sure to run
|
||||||
|
|
||||||
|
|
22
README.md
22
README.md
|
@ -1,14 +1,22 @@
|
||||||
`stockquotes` is a simple Python module for collecting stock quotes and
|
`stockquotes` is a Python module for collecting stock, ETF, and mutual fund
|
||||||
historical data from Yahoo! Finance. It's perfect for developers who can't
|
quotes and historical data from Yahoo! Finance.
|
||||||
afford the (often high) prices charged by many stock data APIs.
|
|
||||||
|
# Branches
|
||||||
|
|
||||||
|
* `devel`: current development version
|
||||||
|
* `stable`: latest stable release
|
||||||
|
|
||||||
|
All PRs should be made against devel.
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
* Python 3.5+
|
* Python 3.6+
|
||||||
* Beautiful Soup 4
|
* Beautiful Soup 4
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
pip3 install stockquotes
|
pip3 install git+https://git.kj7rrv.com/kj7rrv/stockquotes@stable
|
||||||
|
|
||||||
|
Do not install from PyPI; the PyPI package is no longer updated.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
First, import the `stockquotes` module.
|
First, import the `stockquotes` module.
|
||||||
|
@ -48,13 +56,13 @@ for recent IPOs. Also, a known but unexplained bug causes it to only give two
|
||||||
days of data for some stocks.
|
days of data for some stocks.
|
||||||
|
|
||||||
# Exceptions
|
# Exceptions
|
||||||
`stockquotes.StockDoesNotExistError` is raised when the stock does not exist.
|
`stockquotes.StockDoesNotExistError` is raised when the stock does not (appear
|
||||||
|
to) exist.
|
||||||
|
|
||||||
`stockquotes.NetworkError` is raised when a connection to Yahoo! Finance
|
`stockquotes.NetworkError` is raised when a connection to Yahoo! Finance
|
||||||
cannot be established.
|
cannot be established.
|
||||||
|
|
||||||
# License
|
# License
|
||||||
Copyright (c) 2019 ScoopGracie. All rights reversed.
|
|
||||||
This is free and unencumbered software released into the public domain.
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
beautifulsoup4
|
5
setup.py
5
setup.py
|
@ -4,7 +4,7 @@ with open("README.md", "r") as fh:
|
||||||
long_description = fh.read()
|
long_description = fh.read()
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="stockquotes",
|
name="stockquotes",
|
||||||
version="2.0.1",
|
version="2.0.4",
|
||||||
description="A simple module for retreiving stock data",
|
description="A simple module for retreiving stock data",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
|
@ -13,5 +13,6 @@ setuptools.setup(
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
],
|
],
|
||||||
python_requires=">=3.5",
|
python_requires=">=3.6",
|
||||||
|
install_requires="beautifulsoup4",
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,28 +1,18 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# stockquotes - Python module to pull stock quotes from Yahoo! Finance
|
# stockquotes - Python module to pull stock quotes from Yahoo! Finance
|
||||||
# Copyright 2020 ScoopGracie. All rights reversed.
|
|
||||||
# This is free and unencumbered software released into the public domain.
|
|
||||||
#
|
#
|
||||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
# Copyright 2020-2023 Samuel Sloniker
|
||||||
# distribute this software, either in source code form or as a compiled
|
|
||||||
# binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
# means.
|
|
||||||
#
|
#
|
||||||
# In jurisdictions that recognize copyright laws, the author or authors
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
# of this software dedicate any and all copyright interest in the
|
# purpose with or without fee is hereby granted.
|
||||||
# software to the public domain. We make this dedication for the benefit
|
|
||||||
# of the public at large and to the detriment of our heirs and
|
|
||||||
# successors. We intend this dedication to be an overt act of
|
|
||||||
# relinquishment in perpetuity of all present and future rights to this
|
|
||||||
# software under copyright law.
|
|
||||||
#
|
#
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# OTHER DEALINGS IN THE SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
from bs4 import BeautifulSoup as bs
|
from bs4 import BeautifulSoup as bs
|
||||||
import requests
|
import requests
|
||||||
|
@ -41,14 +31,14 @@ class Stock:
|
||||||
def __init__(self, ticker):
|
def __init__(self, ticker):
|
||||||
try:
|
try:
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
"https://finance.yahoo.com/quote/{}/history".format(ticker),
|
f"https://finance.yahoo.com/quote/{ticker}/history",
|
||||||
headers={
|
headers={
|
||||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3941.4 Safari/537.36"
|
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
raise NetworkError()
|
raise NetworkError()
|
||||||
if r.status_code is 302:
|
if r.status_code == 302:
|
||||||
raise StockDoesNotExistError(ticker)
|
raise StockDoesNotExistError(ticker)
|
||||||
try:
|
try:
|
||||||
soup = bs(r.text, features="lxml")
|
soup = bs(r.text, features="lxml")
|
||||||
|
@ -70,39 +60,32 @@ class Stock:
|
||||||
"adjusted_close": float(
|
"adjusted_close": float(
|
||||||
row[5].span.string.replace(",", "")
|
row[5].span.string.replace(",", "")
|
||||||
),
|
),
|
||||||
"volume": int(row[6].span.string.replace(",", "")),
|
"volume": int(row[6].span.string.replace(",", ""))
|
||||||
|
if row[6].string != "-"
|
||||||
|
else None,
|
||||||
}
|
}
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.historical.append(parsed)
|
self.historical.append(parsed)
|
||||||
top_data = soup.find(id="quote-header-info")
|
|
||||||
try:
|
|
||||||
self.current_price = float(
|
|
||||||
top_data.findAll("span")[11].string.replace(",", "")
|
|
||||||
)
|
|
||||||
raw_change = top_data.findAll("span")[12].string
|
|
||||||
except IndexError:
|
|
||||||
self.current_price = float(
|
|
||||||
top_data.findAll("span")[3].string.replace(",", "")
|
|
||||||
)
|
|
||||||
raw_change = top_data.findAll("span")[4].string
|
|
||||||
except ValueError:
|
|
||||||
self.current_price = float(
|
|
||||||
top_data.findAll("span")[9].string.replace(",", "")
|
|
||||||
)
|
|
||||||
raw_change = top_data.findAll("span")[10].string
|
|
||||||
|
|
||||||
|
|
||||||
self.increase_dollars = float(
|
price_selector = f'fin-streamer[data-field="regularMarketPrice"][data-symbol="{self.symbol}"]'
|
||||||
raw_change.split(" ")[0].replace(",", "")
|
price_element = soup.select_one(price_selector)
|
||||||
)
|
self.current_price = float(price_element.text)
|
||||||
self.increase_percent = float(
|
|
||||||
raw_change.split(" ")[1]
|
change_selector = f'fin-streamer[data-field="regularMarketChange"][data-symbol="{self.symbol}"]'
|
||||||
.replace(",", "")
|
change_element = soup.select_one(change_selector)
|
||||||
.replace("(", "")
|
self.increase_dollars = float(change_element.text)
|
||||||
.replace(")", "")
|
|
||||||
.replace("%", "")
|
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:
|
except AttributeError as error:
|
||||||
raise StockDoesNotExistError(ticker) from error
|
raise StockDoesNotExistError(ticker) from error
|
||||||
|
|
Loading…
Reference in New Issue
Block a user