-
Notifications
You must be signed in to change notification settings - Fork 3
/
main.py
131 lines (121 loc) · 5.3 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import os
import json
from dotenv import load_dotenv
import tkinter as tk
from datetime import datetime
import argparse
import random
import time
import getpass
from selenium import webdriver
from selenium_stealth import stealth
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC\
# Function to get the screen width and height
def get_screen_dimensions():
root = tk.Tk()
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.destroy()
return screen_width, screen_height
# Convert a date/time string from 'January 30' or 'January 30, 2024' format to a date
def convert_datetime(input_string):
current_year = datetime.now().year
if ',' in input_string:
date_format = '%B %d, %Y'
else:
date_format = '%B %d'
input_string += f", {current_year}" # Add the current year
return datetime.strptime(input_string, date_format)
# Main function
if __name__ == "__main__":
# Validate arguments
output_path=""
after_str=None
parser = argparse.ArgumentParser()
parser.add_argument('--file', help='Where to save the output (can be an existing file for incremental scraping)')
parser.add_argument('--after', help='A \'Y-m-d H:M\' string to filter out orders older than a certain date/time')
args = parser.parse_args()
if args.file:
output_path = args.file
if args.after:
after_str = args.after
# Setup Webdriver and load env. vars.
load_dotenv()
screen_width, screen_height = get_screen_dimensions()
window_width = screen_width // 2
window_height = screen_height
options = webdriver.ChromeOptions()
options.add_argument(f"window-size={window_width},{window_height}")
options.add_argument(f"window-position={screen_width},0")
dataDir = f"/home/{getpass.getuser()}/.config/chromium"
if not os.path.isdir(dataDir):
dataDir = f"/home/{getpass.getuser()}/.config/google-chrome"
if os.path.isdir(dataDir):
options.add_argument(f"--user-data-dir={dataDir}")
options.add_argument(f"--profile-directory=Default")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options=options)
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
driver.get("https://my.wealthsimple.com")
email = os.getenv("WS_EMAIL")
password = os.getenv("WS_PASSWORD")
if (email): # If not defined, you can login manually
WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div > div > div > input")))
fields = driver.find_elements(By.CSS_SELECTOR, "div > div > div > input")
fields[0].send_keys(email)
fields[1].click()
if (password): # If not defined, you can login manually
WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div > div > div > input")))
fields = driver.find_elements(By.CSS_SELECTOR, "div > div > div > input")
fields[1].send_keys(password)
fields[0].click()
if (email and password): # If not defined, you can login manually
driver.find_elements(By.CSS_SELECTOR, "div > div > div > button").pop().click()
WebDriverWait(driver, 3600).until(EC.url_changes(driver.current_url)) # Long timeout needed for manual login or 2FA
driver.get("https://my.wealthsimple.com/app/activity")
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//button/div/div/div[2]/p[1]")))
time.sleep(2) # If you need to scroll down to 'Load More', increase this timeout to have enough time to scroll manually (scrolling is not automated)
tickers = driver.find_elements(By.XPATH, "//button/div/div/div[2]/p[1]")
transactions = []
for x in range(len(tickers)):
ticker = driver.find_elements(By.XPATH, "//button/div/div/div[2]/p[1]")[x]
transactionType = ticker.find_element(By.XPATH, "../div/p[1]")
try:
amount = ticker.find_element(By.XPATH, "../../../div[2]/p[1]")
except:
continue
amount.click()
time.sleep(1)
details_div = amount.find_element(By.XPATH, "../../../../../div[2]")
try:
date = convert_datetime(details_div.find_element(By.XPATH, "//p[text() = 'Date']/../div/div/p").text).isoformat()
except:
date = convert_datetime(details_div.find_element(By.XPATH, "//p[text() = 'Filled']/../div/div/p").text).isoformat()
if after_str is not None:
after_date = datetime.strptime(after_str, '%Y-%m-%d %H:%M')
curr_date = datetime.fromisoformat(date)
if after_date > curr_date:
break
transactions.append({
"description": ticker.text,
"type": transactionType.text,
"amount": amount.text,
"date": date
})
amount.click()
# Output
report = json.dumps(transactions, indent=4)
print(report)
if output_path:
with open(output_path, 'w') as f:
f.write(report)