2

Im trying to log into a website using selenium by doing:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.eco-visio.net/v5/")
username = driver.find_element_by_id("login-username")
username.send_keys("midregcog.admin")

but im getting the following error

ElementNotInteractableException           Traceback (most recent call last)
<ipython-input-9-975030640535> in <module>()
      8 driver.get("https://www.eco-visio.net/v5/")
      9 username = driver.find_element_by_id("login-username")
---> 10 username.send_keys("midregcog.admin")
     11 

C:\Anaconda2\lib\site-packages\selenium\webdriver\remote\webelement.pyc in send_keys(self, *value)
    477         self._execute(Command.SEND_KEYS_TO_ELEMENT,
    478                       {'text': "".join(keys_to_typing(value)),
--> 479                        'value': keys_to_typing(value)})
    480 
    481     # RenderedWebElement Items

C:\Anaconda2\lib\site-packages\selenium\webdriver\remote\webelement.pyc in _execute(self, command, params)
    631             params = {}
    632         params['id'] = self._id
--> 633         return self._parent.execute(command, params)
    634 
    635     def find_element(self, by=By.ID, value=None):

C:\Anaconda2\lib\site-packages\selenium\webdriver\remote\webdriver.pyc in execute(self, driver_command, params)
    319         response = self.command_executor.execute(driver_command, params)
    320         if response:
--> 321             self.error_handler.check_response(response)
    322             response['value'] = self._unwrap_value(
    323                 response.get('value', None))

C:\Anaconda2\lib\site-packages\selenium\webdriver\remote\errorhandler.pyc in check_response(self, response)
    240                 alert_text = value['alert'].get('text')
    241             raise exception_class(message, screen, stacktrace, alert_text)
--> 242         raise exception_class(message, screen, stacktrace)
    243 
    244     def _value_or_default(self, obj, key, default):

ElementNotInteractableException: Message: element not interactable
  (Session info: chrome=74.0.3729.131)
  (Driver info: chromedriver=74.0.3729.6 (255758eccf3d244491b8a1317aa76e1ce10d57e9-refs/branch-heads/3729@{#29}),platform=Windows NT 10.0.17134 x86_64)

Here is the id tag of the website im trying to get. enter image description here

Daniel
  • 5,095
  • 5
  • 35
  • 48

2 Answers2

2

As explained here, you need to artificially click the element with javascript since it is being made by javascript itself (selenium cannot "see" the element). Another example of a similar situation is my answer on this question. Adjusting your code like this works (I implemented waits too, but that probably isn't necessary):

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
from selenium.webdriver.common import action_chains
import time

driver = webdriver.Chrome()
driver.get("https://www.eco-visio.net/v5/")
username_element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//form/div/input[@id='login-username']")))# driver.find_element_by_id("login-username")

driver.execute_script("arguments[0].click();", username_element)
time.sleep(1) # give the click a moment to register
action = action_chains.ActionChains(driver)
action.send_keys("midregcog.admin")
action.perform()

This works to fill in the field on my machine! Hope this helps.

update

@MarcelWilson points out that you just need to wait for the page to load (not entirely sure why since it is , and with a couple tests I'm not sure why it doesn't work with just an explicit wait. Apparently the first answer was overly complicated and you can accomplish the desired effect with:

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
import time

driver = webdriver.Chrome()
driver.get("https://www.eco-visio.net/v5/")
username_element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//form/div/input[@id='login-username']")))# driver.find_element_by_id("login-username")
time.sleep(2) # give a longer wait than the explicit wait in the previous line
username_element.click()
username_element.send_keys("username")
password_element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//form/div/input[@id='login-password'][@type='password']")))# driver.find_element_by_id("login-username")
password_element.click()
password_element.send_keys("password")

I think this may be because in the javascript that generates the page (and I'm no javascript expert) there are lines such as if: loaded() which leads me to believe that the explicit WebDriverWait is getting the element, but the javascript is still in the process of loading it in. So the WebDriverWait makes sure the element exists, and the time.sleep(2) makes sure it is also loaded before trying to interact with it, at which point it looks like you can interact with it as a normal page without problems. I even got to learn some new stuff for this post XD!

Reedinationer
  • 5,661
  • 1
  • 12
  • 33
  • You do not need to use javascript to click the element. You only need to wait for the element to be visible. – Marcel Wilson May 22 '19 at 05:12
  • im doing `driver.execute_script("arguments[0].click(); arguments[1].click();", username_element, password_element)` and then `action.send_keys("midregcog.admin", "passherer")` but both inputs are being written in the same box. How can i specify the password to the password input? – Daniel May 22 '19 at 16:07
  • 1
    The problem with using time.sleep is apparent when network hiccups or the server lags. The sleep duration won't be sufficient. Adding more time would only delay the test when things are running smoothly. Using WebDriverWait solves this problem by waiting only as long as needed. You should be ok removing the sleep. However, I would use `EC.visibility_of_element_located`. If there is an ajax call going on, there is a way to wait until the ajax finishes. – Marcel Wilson May 23 '19 at 02:20
  • @MarcelWilson when I tested just using `WebDriverWait` it was resulting in errors on my machine though. Maybe 1 second wait would be fine when used in conjunction though. As I commented in my answer I think selenium is able to recognize the element is there, but not able to interact with it without the wait (in my tests at least) – Reedinationer May 23 '19 at 02:30
  • visibility vs presence will make a difference. presence can trigger before visibility. – Marcel Wilson May 23 '19 at 02:36
  • @MarcelWilson Yeah I tried that too and couldn't seem to get it to work within a few minutes. I'd try to figure it out, but OP has already accepted an answer and I've posted multiple that worked for me! If you wanted to explain how to use visibility that'd be awesome though. – Reedinationer May 23 '19 at 03:43
  • Simply change out `EC.presence_of_element_located` for `EC.visibility_of_element_located`. The difference is presence will return the element as soon as the DOM has created it whereas visibility waits to return the element once it is visible. It's a nuanced difference but one that I'm guessing you're running into. – Marcel Wilson May 23 '19 at 10:37
1

The problem is selenium is trying to interact with the element before it's finished loading in the DOM. One way around this is to use the WebDriverWait in conjunction with the expected_conditions module to "look" for the elements and then interacting with them.

Try something like this.

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

driver = webdriver.Chrome()
driver.get("https://www.eco-visio.net/v5/")
wait = WebDriverWait(driver, 10)
ec = EC.visibility_of_element_located((By.ID, "login-username"))
try:
    username = wait.until(ec, f"Could not find {ec.locator}")
    username.send_keys("midregcog.admin")
except Exception as exc:
    raise
finally:
    driver.quit()
Marcel Wilson
  • 3,842
  • 1
  • 26
  • 55
  • This answer has a syntax error at `username = wait.until(ec, f"Could not find {ec.locator}")` yet very informative and to know about the interaction of selenium and the website – Daniel May 22 '19 at 14:45
  • 1
    I'm guessing you're not on 3.6 or higher. I used an f-string here which is a new thing. Use this line instead; `username = wait.until(ec, "Could not find {}".format(loc=ec.locator)` – Marcel Wilson May 23 '19 at 02:15