I am trying to log to my Garmin Connect account using selenium/python in PyCharm. I know there is an API but, as this is for training purpose, I really would like to reach my goal through the Website.
My code:
import scrapy
import time
from scrapy_selenium import SeleniumRequest
from scrapy.selector import Selector
from selenium.webdriver.support.ui import WebDriverWait as wait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
class ScrapgarminSpider(scrapy.Spider):
name = 'scrapGarmin'
def start_requests(self):
yield SeleniumRequest(
url='https://connect.garmin.com/signin/',
wait_time=3,
callback=self.parse
)
def parse(self, response):
driver = response.meta['driver']
wait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, "//iframe[@id='gauth-widget-frame-gauth-widget']")))
frameLogin=driver.find_element_by_xpath("//iframe[@id='gauth-widget-frame-gauth-widget']")
driver.switch_to.frame(frameLogin)
champLogin=driver.find_element_by_xpath("//input[@class='login_email']")
champLogin.send_keys("my_email_here")
driver.implicitly_wait(3)
champPassword = driver.find_element_by_xpath("//input[@id='password']")
champPassword.send_keys("my_password_here")
time.sleep(5)
boutonSubmit = driver.find_element_by_xpath("//button[@type='submit']")
boutonSubmit.click()
time.sleep(5)
print(f"Current URL: {driver.current_url}")
If I do this, I have a:
selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element <iframe
Then I tried to click using javascript:
boutonSubmit = wait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, "//button[@type='submit']")))
driver.execute_script("arguments[0].click();", boutonSubmit)
In that case, there is no error message by my current URL remains the signin page. It does not enter the website.
Any idea please ? That would really help. Thanks
CodePudding user response:
Why just not simplifying your code instead, you can make a lot of improvements. You may have your preferences but here is a simplified script with my methods. No need to set classes, multiple functions and setting variables for each action. I also see that you are using scrapy but you can also just install and use the chrome driver. All you have to do is download chromium chromedriver at https://chromedriver.chromium.org/downloads and then replace the path in webdriver.Chrome(executable_path="Replace with chromedriver path")
.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
#setting up driver and loading page
driver = webdriver.Chrome(executable_path="Replace with chromedriver path")
driver.get("https://connect.garmin.com/signin/") #loading page
wait = WebDriverWait(driver, 20) #defining webdriver wait
#defining username and password, dont remove \n
username = 'xxx'
password = 'xxx\n' #\n will act as an enter key and automatically login after having entered the password without clicking on confirm
#locating the login script under a function to make things more "visible"
def login():
wait.until(EC.visibility_of_element_located((By.XPATH, "//iframe[@id='gauth-widget-frame-gauth-widget']")))
frameLogin = driver.find_element(By.XPATH, "//iframe[@id='gauth-widget-frame-gauth-widget']")
driver.switch_to.frame(frameLogin)
wait.until(EC.visibility_of_element_located((By.ID, 'username'))).send_keys(username) #userame/email
wait.until(EC.visibility_of_element_located((By.ID, 'password'))).send_keys(password) #password
#calling the login function and printing "you're in!!" when logged in
login()
print("you're in!!")
print('Url: ', driver.current_url)
You can locate elements by ID, or NAME which are much more simple and shorter than the XPATH.
I can also see that you define vairiables which is ok but then you 're-call' the variable adding a .send_keys
function, you can simply add it at the end:
test = driver.find_element(By.XPATH, "xxx")
test.send_keys("hello")
to
test = driver.find_element(By.XPATH, "xxx").send_keys("hello")
You can also remove the variable and just make a little #
note of the function.
driver.find_element(By.XPATH, "xxx").send_keys("hello") #test
I also recommend using the driver.wait
function which is brilliant to not get any errors or having to wait extra time with time.sleep()
for example, as you see I define the waiting function at the beginning.
wait.until(EC.visibility_of_element_located((By.XPATH,'xxx'))).send_keys(hello)` #test
Some more tips to learn selenium:
- do not stay stuck in the tutorial hell, make projects, preferably on your own or patchwork
- take notes or speccific scripts when you learn something new
- some projects ideas: type bot on online type learning sites, auto clicker etc...
- just have fun while learning
Simple and effective, have a nice day.
CodePudding user response:
Thanks for your answers. I know my code is not optimized. I'm a dummy :) Few remarks: I tried wait:
- element_to_be_clickable
- visibility_of_element_located
- presence_of_element_located
- frame_to_be_available_and_switch_to_it
None of the them work. In the login function which is proposed hereabove, I see an input of the login and password. But it does not solve my issue on clicking the submit button to log in.
I am still stuck :(