I have been trying to create an automated way of adding stocks to google finance, using selenium. I am able to login and get to the adding of the new investment, but that's where I get stuck.
Steps to reproduce:
Create a new portfolio in google finance. Populate the portfolio with at least 1 item. Next, when you run the script as below, it gets to clicking investment but is unable to send any stock names to the combo box.
Code:
class Google:
def __init__(self) -> None:
self.url = "https://accounts.google.com"
self.driver = uc.Chrome()
self.driver.delete_all_cookies()
self.time = 60
def login_and_goto_google_finance(self, email, password):
self.driver.get(self.url)
WebDriverWait(self.driver, 20) \
.until(EC.visibility_of_element_located((By.NAME, 'identifier'))) \
.send_keys(f'{email}' + Keys.ENTER)
WebDriverWait(self.driver, 20) \
.until(EC.visibility_of_element_located((By.NAME, 'Passwd'))) \
.send_keys(f'{password}' + Keys.ENTER)
def navigate_to_site(self, url, wait_enabled=True):
if wait_enabled:
sleep(self.time/20)
self.driver.get(url)
self.driver.find_element(By.XPATH, '//span[text()="Investment"]').click()
self.enter_symbol("BP", 1, 20240201, 100)
sleep(60*self.time)
def enter_symbol(self, symbol_name, qty, date, price):
try:
x = self.driver.find_element(By.XPATH, "//*[contains(@class, 'Ax4B8 ZAGvjd')]")
print(x.aria_role)
sleep(1)
x.click()
sleep(1)
x.send_keys(f'{symbol_name}'+Keys.ENTER)
except:
pass
finally:
sleep(60*self.time)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--username", type=str, help="Email Id for logging in to Google", required=True)
parser.add_argument("-p", "--password", type=str, help="Password for logging in to Google", required=True)
args = parser.parse_args()
google = Google()
google.login_and_goto_google_finance(args.username, args.password)
google.navigate_to_site(<link_to_your_portfolio_here>)
What am I doing wrong here, and what should be the correct way of handling this?
The locator "//*[contains(@class, 'Ax4B8 ZAGvjd')]"
fits two elements. find_element
returns the first element in the DOM, but you want to send the data to the second one, so you need to use a more specific locator using ancestor elements
self.driver.find_element(By.XPATH, '//div[@class="M52nVb ytPNkd"]//input[@class="Ax4B8 ZAGvjd"]')