From 659042c45aa6f5dadc5f3fbd000bd4889c2f5e2d Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 8 Sep 2023 00:07:40 +0100 Subject: [PATCH] Initial commit --- ftse_all_share | 8 +++ google_stocks/__init__.py | 116 ++++++++++++++++++++++++++++++++++++++ google_stocks/py.typed | 0 s_and_p_total_market | 8 +++ 4 files changed, 132 insertions(+) create mode 100755 ftse_all_share create mode 100644 google_stocks/__init__.py create mode 100644 google_stocks/py.typed create mode 100755 s_and_p_total_market diff --git a/ftse_all_share b/ftse_all_share new file mode 100755 index 0000000..2de076a --- /dev/null +++ b/ftse_all_share @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +"""FTSE All Share.""" + +from google_stocks import Index + +index = Index("FTSE All Share") + +print(index.one_line) diff --git a/google_stocks/__init__.py b/google_stocks/__init__.py new file mode 100644 index 0000000..7f07fd8 --- /dev/null +++ b/google_stocks/__init__.py @@ -0,0 +1,116 @@ +"""Look up stock index on Google.""" + +import decimal +import os +import re +import urllib.parse +from datetime import datetime + +import lxml.html +from playwright.sync_api import Playwright, expect, sync_playwright + +auth_file = os.path.expanduser("~/lib/auth/google.json") +data_loc = os.path.expanduser("~/lib/google_stocks") + + +attr_map = { + "52-wk high": "price_52_wk_high", + "52-wk low": "price_52_wk_low", + "Company Name": "company_name", + "High": "price_high", + "Low": "price_low", + "Open": "price_open", + "Prev close": "price_prev_close", + "day change": "day_change", + "title": "title", + "subtitle": "subtitle", +} + + +def data_filename(page_type: str, ext: str = "html") -> str: + """Filename to use for saving data.""" + now_str = datetime.utcnow().strftime("%Y-%m-%d_%H%M%S") + + return os.path.join(data_loc, now_str + f"_{page_type}.{ext}") + + +class Index: + """Stock market index.""" + + price_52_wk_high: decimal.Decimal + price_52_wk_low: decimal.Decimal + company_name: str + price_high: decimal.Decimal + price_low: decimal.Decimal + price_open_price: decimal.Decimal + price_prev_close: decimal.Decimal + day_change: decimal.Decimal + percent_change: decimal.Decimal + price: decimal.Decimal + subtitle: str + title: str + + def __init__(self, name: str): + """Init.""" + self.name = name + + with sync_playwright() as playwright: + self.run(playwright) + + @property + def search_url(self) -> str: + """Search URL.""" + return "https://www.google.com/search?q=" + urllib.parse.quote_plus(self.name) + + def run(self, playwright: Playwright) -> None: + """Run playwright.""" + browser = playwright.chromium.launch(headless=True) + context = browser.new_context(storage_state=auth_file) + page = context.new_page() + + page.goto(self.search_url, wait_until="domcontentloaded") + expect(page.get_by_text("Market Summary")).to_be_visible() + + html = page.content() + filename = data_filename("serp") + with open(filename, "w") as out: + out.write(html) + + self.parse_html(html) + + page.close() + + context.storage_state(path=auth_file) + context.close() + browser.close() + + def parse_html(self, html: str) -> None: + """Parse HTML.""" + root = lxml.html.fromstring(html) + + re_percent_change = re.compile(r" *\(([0-9.]+)%\) *") + + for attrid_tag in root.findall(".//*[@data-attrid]"): + attrid = attrid_tag.get("data-attrid") + if attrid not in attr_map: + continue + setattr(self, attr_map[attrid], attrid_tag.text_content()) + + tag = root.find('.//*[@data-attrid="Price"]') + assert tag is not None + + assert tag[0] is not None and tag[1] is not None + self.price = decimal.Decimal(tag[0].text_content().replace(",", "").strip()) + percent_change_str = tag[1][0].text_content().strip().replace("−", "-") + self.day_change = decimal.Decimal(percent_change_str) + m = re_percent_change.match(tag[1][1].text_content()) + assert m + percent_change = decimal.Decimal(m.group(1)) + if percent_change_str[0] == "-": + percent_change = -percent_change + self.percent_change = percent_change + + @property + def one_line(self) -> str: + """Index name, price and price change.""" + return f"{self.title}: {self.price} ({self.percent_change}%)" diff --git a/google_stocks/py.typed b/google_stocks/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/s_and_p_total_market b/s_and_p_total_market new file mode 100755 index 0000000..bfcfdc0 --- /dev/null +++ b/s_and_p_total_market @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +"""FTSE All Share.""" + +from google_stocks import Index + +index = Index("S&P Total Market") + +print(index.one_line)