3

I have the following selenium script the waits for the page to load and looks for an element. After the element is retrieved I want to close the driver s that my ram gets freed up, but instead the whole ends in a InvalidSessionIdException meaning the driver is closed at an inappropriate time. How do I properly close my driver after getting the information that I want?

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait



def selenium_get_time(ort):
    options = Options()
    options.headless = True
    driver = webdriver.Chrome(chrome_options=options, executable_path='/Users/andreas/.wdm/chromedriver/83.0.4103.39/mac64/chromedriver')

    driver.get("https://fp.trafikverket.se/boka/#/search/dIccADaISRCIi/5/0/0/0")
    element = WebDriverWait(driver, 40).until(EC.element_to_be_clickable((By.CLASS_NAME, "form-control")))
    driver.find_element_by_xpath("//select[@id='examination-type-select']/option[@value='3']").click()
    driver.find_element_by_xpath("//select[@id='language-select']/option[@value='13']").click()
    driver.find_element_by_id('id-control-searchText').clear()
    inputElement = driver.find_element_by_id("id-control-searchText")
    inputElement.send_keys(ort)
    inputElement.send_keys(Keys.ENTER)
    # time.sleep(10)
    try:
        element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='col-sm-3 text-center']/button[@data-bind='click:$parent.select']")))
        first_time = driver.find_element_by_xpath("//div[@class='col-xs-6']/strong")
        return first_time.text
    except (NoSuchElementException, TimeoutException) as e:
        if NoSuchElementException:
            print('Nothing found for: ', ort, ' NoElemFound')
        else:
            print('Nothing found for: ', ort, ' TimedOut')
    finally;
        driver.close()
        driver.quit()

#This is the program that I run
def main(ort):
    first_availible = selenium_get_time(ort)
    if first_availible:
        date = convert_time(first_availible)
        if check_schedule(date, '2020-07-01', '2020-07-05'):
            print('FOUND: ', ort +' '+ first_availible)
            send_email(first_availible, ort)
        else:
            now = datetime.datetime.now()
            dt_string = now.strftime("%H:%M:%S")
            print('Found Nothing for: ', ort, ' ', dt_string)


MisterButter
  • 749
  • 1
  • 10
  • 27

1 Answers1

3

When you are calling:

first_availible = selenium_get_time(ort)

In def selenium_get_time(ort) you are invoking:

finally:
    driver.close()
    driver.quit()

Hence when the control comes back to main(ort) you are seeing InvalidSessionIdException.


Solution

Close/Quit the WebDriver and the Web Browser in a seperate method which will get executed after main(ort) making the the ChromeDriver instance as a global instance as follows:

from selenium import webdriver

driver = None

def selenium_get_time(ort):
    global driver
    options = Options()
    options.headless = True
    driver = webdriver.Chrome(chrome_options=options, executable_path='/Users/andreas/.wdm/chromedriver/83.0.4103.39/mac64/chromedriver')
    .
    .
#This is the program that I run
def main(ort):
    first_availible = selenium_get_time(ort)
    .
    .
    tear_down()

def tear_down():
    driver.quit()

Additionally, check the compatibility between the version of Google Chrome, ChromeDriver, Selenium binaries you are using.

You can find a relevant discussion in selenium.common.exceptions.InvalidSessionIdException using GeckoDriver Selenium Firefox in headless mode through Python

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • thank you for the input! Will the driver close even if the driver is defined in the local scope of `selenium_get_time`? – MisterButter Jul 01 '20 at 20:23
  • @MisterButter Keep it in a separate `tear_down()` method. – undetected Selenium Jul 01 '20 at 20:27
  • Sure, but since the `driver` is defined in the local scope of `selenium_get_time`, and I call `driver_close` in a separate `tear_down() ` function, then I will get `NameError: name 'driver' is not defined` – MisterButter Jul 01 '20 at 20:29
  • @MisterButter Checkout the updated answer and let me know the if you need further help. – undetected Selenium Jul 01 '20 at 21:03
  • 1
    Thank you DebanjanB! I solved it be having a method `start_driver()` that returns a driver and set it as `driver = start_driver()`. I then pass the driver to `selenium_get_time(driver, ort)` and then pass the driver to a method `close_driver(driver)` that terminates it. In this way I do not need to define it as a global variable. Thank you for taking your time to explain and provide resourses! – MisterButter Jul 01 '20 at 21:18
  • @MisterButter That was also a possible approach, however I wanted to suggest the minimal changes required. Glad that your issue got solved. – undetected Selenium Jul 01 '20 at 21:21
  • 1
    please also note I just tried your solution and I found it to be much more stable than the one I tried. No connection-time outs and not more than one instance of a driver at the time, thank you! – MisterButter Jul 01 '20 at 21:25
  • @MisterButter Thanks for the feedback, definitely it helps. – undetected Selenium Jul 01 '20 at 21:27