1

I'm writing an script and I need to find cursor position automatically for an element. The script will run on different computers, so I want it be done automatically.

Suppose I want to click on search box in the head of stackoverflow.com to find its position using selenium and pyautogui.

How to do that so that mouse is in that position and script is exited?

Edit 1

Suppose I want it to click on this element:

input which name="q" and class="s-input s-input__search js-search-field "

Edit 2:

My current code with the help of Prophet and undetected Selenium, but the output I get is not correct:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import pyautogui
from time import sleep
from datetime import datetime
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait

path = r'path'
user_data = r'user'
options = webdriver.ChromeOptions()
options.add_argument(f'--user-data-dir={user_data}')
options.add_argument('--profile-directory=Default')
driver = webdriver.Chrome(executable_path=path, options=options)
actions = ActionChains(driver)
xpath = '//*[@id="search"]/div/input'

url = 'https://stackoverflow.com/'
driver.get(url)
position = WebDriverWait(driver, 20).until(ec.visibility_of_element_located((By.XPATH, xpath))).location_once_scrolled_into_view
# both tried the above position var and the below
# position = WebDriverWait(driver, 20).until(ec.visibility_of_element_located((By.XPATH, xpath))).location
# element = driver.find_element(By.XPATH, xpath)
# actions.move_to_element(element).perform()
# position = pyautogui.position()
file = open('position.txt', 'a')
file.write(f'{position}, {datetime.now()}\n')
driver.close()

# to test the exact or relative position to compare with above code
# while True:
#     sleep(1)
#     print(pyautogui.position())

Edit 3:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import pyautogui
from time import sleep
from datetime import datetime
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

path = r'D:\\Downloads\\chromedriver_win32\\chromedriver.exe'
user_data = r'C:\\Users\\Saeed\\AppData\\Local\\Google\\Chrome\\User Data\\Default'
options = webdriver.ChromeOptions()
options.add_argument(f'--user-data-dir={user_data}')
options.add_argument('--profile-directory=Default')
driver = webdriver.Chrome(executable_path=path, options=options)
actions = ActionChains(driver)
xpath = '//*[@id="search"]/div/input'  # `search` box at header
url = 'https://stackoverflow.com//'
driver.get(url)
driver.maximize_window()
sleep(10)
position = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, xpath))).location
print(position)
file = open('position.txt', 'a')
file.write(f'{position}, {datetime.now()}\n')
# pyautogui.click(793, 9)
driver.close()

# while True:
#     sleep(1)
#     print(pyautogui.position())

The video how my code now works: https://up.mazandhost.com/uploads/1028407950.bandicam-2022-03-27-21-33-27-729.mp4

Solution in my case:

Answer https://stackoverflow.com/a/56693949/5790653 helps me.

Saeed
  • 3,255
  • 4
  • 17
  • 36

2 Answers2

2

With Selenium you can perform mouse actions with the use of action_chains library.
Once you locate the element with selenium you can move the mouse to that element as following:

from selenium.webdriver.common.action_chains import ActionChains

actions = ActionChains(driver)

element = driver.find_element(By.XPATH, 'the_xpath_locator')
actions.move_to_element(element).perform()
Prophet
  • 32,350
  • 22
  • 54
  • 79
  • Thanks, but not sure why it does not work perfectly for me (surely works for you fine). It returns the current mouse cursor position (not the position on search box). I did edit the question. Do you please check edit 2? – Saeed Mar 27 '22 at 15:48
  • 1
    Sure. I can't do it right now, but I will take a look in 20-30 minutes – Prophet Mar 27 '22 at 15:50
  • 1
    Can you please clarify: what exactly are you trying to achieve? The location of mouse cursor or Google search input web element? In case you are talking about the former - I can't see you scrolling the web page, so why should the location of the web element change and also if so why / how it is connected with mouse you mentioned in the original question? – Prophet Mar 27 '22 at 16:16
  • I want to find the location of `Google search input web element` automatically, so when I run this code in another laptop, it clicks on search box (mine, yours, and someone else's laptop should work the same). – Saeed Mar 27 '22 at 16:20
  • 1
    OK, seems that is what undetected Selenium initially gave you. His code prints the location of that element. But why should the location of that element change if you don't scroll the entire web page? – Prophet Mar 27 '22 at 16:28
  • Do you please check edit 3 and my video? – Saeed Mar 27 '22 at 17:13
  • I really do appreciate your help and favor. I did edit the question as my question is solved. – Saeed Mar 27 '22 at 22:17
  • 1
    Thank you for letting me know, that's really interesting. I'm sorry I could not help you more, was very busy this evening. – Prophet Mar 27 '22 at 22:21
  • At this moment the mentioned answer helped me. I'll try your answer tomorrow to see what part I was doing wrong with your answer and undetected selenium. – Saeed Mar 27 '22 at 22:30
  • 1
    Sure. In case you need more my assistance please let me know – Prophet Mar 28 '22 at 06:48
1

To extract the location and/or position an element you need to induce WebDriverWait for the visibility_of_element_located() and you can use either of the following locator strategies:

  • Using location attribute:

    driver.get('https://www.google.com/')
    print(WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.NAME, "q"))).location)
    
  • Console Output:

    {'x': 439, 'y': 184}
    
  • Using location_once_scrolled_into_view attribute:

    driver.get('https://www.google.com/')
    print(WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.NAME, "q"))).location_once_scrolled_into_view)
    
  • Console Output:

    {'x': 439, 'y': 184}
    
  • Note : You have to add the following imports :

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    

This usecase

As per the HTML details:

input which name="q" and class="s-input s-input__search js-search-field "

To click on the clickable element you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following locator strategies:

  • Using CSS_SELECTOR:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input.s-input.s-input__search.js-search-field[name='q']"))).click()
    
  • Using XPATH:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[@class='s-input s-input__search js-search-field ' and @name='q']"))).click()
    

Alternative

As an alternative you can also use the move_to_element() method from ActionChains API as follows:

ActionChains(driver).move_to_element(WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[@class='s-input s-input__search js-search-field ' and @name='q']")))).perform()
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • Thanks, but my output is not correct. It shows `{'x': 474, 'y': 9}`, while it should be about `(x=635, y=94)` in maximized, or `(x=1338, y=104)` in side minimized window. I edited the question. Do you please check that? – Saeed Mar 27 '22 at 15:50
  • 1
    Ideally your screen should be always maximized `options.add_argument("start-maximized")` while executing your tests. The example was of the search field on Google Home Page. Checkout the answer update and let me know the status. – undetected Selenium Mar 27 '22 at 15:51
  • With `CSS_SELECTOR`, I get `selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: An invalid or illegal selector was specified`, but wit `XPATH`, it prints my current mouse position (it's not on search box). – Saeed Mar 27 '22 at 15:56
  • 1
    Fixed the _CSS_SELECTOR_. Well we are not printing the `current mouse position` as that would involve different set of [Mouse actions APIs](https://stackoverflow.com/a/71609969/7429447). We are printing the element identified by the respective [_locator strategies_](https://stackoverflow.com/questions/48054321/of-the-many-findelements-by-functions-in-selenium-when-would-you-use-one-over) – undetected Selenium Mar 27 '22 at 15:59
  • So, is there any way to control mouse to go to that element? – Saeed Mar 27 '22 at 16:00
  • 1
    Updated my previous comment with link to usage of _**Mouse actions APIs**_. – undetected Selenium Mar 27 '22 at 16:02
  • I use `move_to_element` but mouse position is not changed. Should it change the position physically like when I move my mouse? – Saeed Mar 27 '22 at 16:14
  • 1
    @Saeed I would suggest hold on the idea of using the _**Mouse actions APIs**_, use the `expected_conditions` if that works. Else I will update the answer with another approach. – undetected Selenium Mar 27 '22 at 16:19
  • 1
    Moreover location through _Selenium_ and _pyautogui_ may differ significantly. – undetected Selenium Mar 27 '22 at 16:23
  • 1
    Checkout the answer update. – undetected Selenium Mar 27 '22 at 16:32
  • Do you please check edit 3 and my video? – Saeed Mar 27 '22 at 17:13
  • I really do appreciate your help and favor. I did edit the question as my question is solved. – Saeed Mar 27 '22 at 22:17