Created
October 5, 2023 00:14
-
-
Save justengel/290e54645bc77431785b79f85b1a3193 to your computer and use it in GitHub Desktop.
Check prices and report on the cheapest price found
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Check and store the prices of an item. | |
Create a find_prices.json file and run daily to see if prices changed. | |
..code-block:: json | |
{ | |
"Coffee Maker": { | |
"Vendor": { | |
"url": "https://vendor.url", | |
}, | |
"Amazon": { | |
"url": "https://www.amazon.com/...", | |
} | |
} | |
On windows use taskschd.msc as a cron job. | |
""" | |
import sys | |
try: | |
import local | |
except (ImportError, Exception): | |
print("Could not import local env!", file=sys.stderr) | |
import json | |
import requests | |
from bs4 import BeautifulSoup | |
from selenium import webdriver | |
from selenium.webdriver.chrome.options import Options | |
from script_utils import send_mail | |
FILENAME = "find_prices.json" | |
def find_price(url): | |
r = requests.get(url) | |
if r.status_code != 200: | |
chrome_options = Options() | |
chrome_options.add_argument("--headless") | |
chrome_options.add_experimental_option("excludeSwitches", ["enable-logging"]) | |
browser = webdriver.Chrome(options=chrome_options) | |
browser.get(url) | |
page = browser.page_source | |
browser.close() | |
else: | |
page = r.text | |
# Stock | |
in_stock = "out of stock" not in page.lower() | |
if "amazon" in url: | |
soup = BeautifulSoup(page, "html.parser") | |
try: | |
a_price = soup.find("span", class_="a-offscreen").text | |
except (AttributeError, Exception): | |
a_price = ( | |
soup.find("span", class_="a-price-whole").text | |
+ soup.find("span", class_="a-price-fraction").text | |
) | |
price = float(a_price.replace("$", "").replace(",", "")) | |
else: | |
page_lower = page.lower() | |
price = None | |
try: | |
button_idx = page_lower.find("add to cart") | |
price_idx = page_lower[:button_idx].rfind("$") | |
s_price = page[price_idx:button_idx].split(" ", 1)[0].split("<", 1)[0] | |
price = float(s_price.replace("$", "").replace(",", "")) | |
except (TypeError, ValueError): | |
for i in range(100): | |
try: | |
price_idx = page_lower.rfind("$") | |
page_lower = page_lower[price_idx + 1 :] | |
s_price = page_lower.split(" ", 1)[0].split("<", 1)[0] | |
price = float(s_price.replace("$", "").replace(",", "")) | |
break | |
except (TypeError, ValueError): | |
pass | |
return price, in_stock | |
def main(): | |
with open(FILENAME, "r") as f: | |
prices = json.loads(f.read()) | |
cheaper = {} | |
for product, stores in prices.items(): | |
print(f"{product}: ") | |
for store, store_prices in stores.items(): | |
price, stock = find_price(store_prices["url"]) | |
if price is None: | |
continue | |
try: | |
old_price = store_prices["cheapest_price"] | |
except KeyError: | |
# Initialize cheapest price | |
old_price = store_prices["cheapest_price"] = price | |
cheap = old_price > price | |
print( | |
f"\t{store}\tprice: ${price:.02f}\tstock: {stock}\tcheaper: {cheap}\tdiff: ${old_price - price:.02f}", | |
flush=True, | |
) | |
store_prices[f"last_price"] = price | |
if cheap: | |
if not cheaper.get(product) or cheaper[product][1] > price: | |
cheaper[product] = (store, price, store_prices["url"]) | |
store_prices[f"cheapest_price"] = price | |
with open(FILENAME, "w") as f: | |
json.dump(prices, f, indent=4) | |
if cheaper: | |
body = [] | |
for product, (store, price, url) in cheaper.items(): | |
body.append(f"{product} found a cheaper price at {store} for ${price:.02f}\n\t{url}\n") | |
send_mail( | |
"Cheaper price found!", | |
"\n".join(body), | |
"[email protected]", | |
["[email protected]"], | |
) | |
print("\nCheaper price found!\n\n" + '\n'.join(body)) | |
else: | |
print("\tNo cheaper prices found!", file=sys.stderr) | |
if __name__ == "__main__": | |
with local.env(): | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment