Search code examples
pythonselenium-webdriverwebautomation

How to locate inputs that are placed inside several shadow-roots on Maersk.com


I am using Selenium with Python.

Here is the web site link https://www.maersk.com/schedules/pointToPoint

I'm working on a Python project and using Selenium to automate interactions with a webpage, specifically targeting the Maersk schedules page (link provided).

I'm trying to interact(click) with the "From (City, Country/Region)" and "To (City, Country/Region)" input boxes on this page.

Could someone provide guidance or an example code snippet in Python using Selenium that effectively locates and interacts(click) with these input boxes?

Here is my python code:

from webdriver_manager.chrome import ChromeDriverManager
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import ImageTk, Image
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import time
import os
import wget
import pandas as pd
import csv
from collections.abc import Iterable
import glob
import shutil
import re
import openpyxl
from selenium import webdriver
from PIL import Image
import pytesseract
from selenium.common.exceptions import TimeoutException

# Automatically download and set up the appropriate ChromeDriver
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get(f"https://www.maersk.com/schedules/pointToPoint")
#time.sleep(1)
driver.maximize_window()


allow_all = WebDriverWait(driver, 
10).until(EC.presence_of_element_located((By.XPATH, "//*[@id='coiPage- 
1']/div[2]/button[3]")))

allow_all.click()

form = driver.find_element(By.CLASS_NAME, "schedules-view__search--box")


# Find the input element within the form

input_element = form.find_elements(By.TAG_NAME, "input")

# Click on the first input element or perform any action needed
print(input_element)

OUTPUT:

[<selenium.webdriver.remote.webelement.WebElement (session="3fb48d1f542b127316d4cb737f238639", element="B037E6D01D955BB15533DAE9CA20FFB4_element_66")>, <selenium.webdriver.remote.webelement.WebElement (session="3fb48d1f542b127316d4cb737f238639", element="B037E6D01D955BB15533DAE9CA20FFB4_element_68")>, <selenium.webdriver.remote.webelement.WebElement (session="3fb48d1f542b127316d4cb737f238639", element="B037E6D01D955BB15533DAE9CA20FFB4_element_70")>, <selenium.webdriver.remote.webelement.WebElement (session="3fb48d1f542b127316d4cb737f238639", element="B037E6D01D955BB15533DAE9CA20FFB4_element_72")>]

Now I need to click "input_element[0]"

while I am clicking using input_element[0].click() its throw an ERROR("Element Not Interactable Exception").

Note: Targeted Inputbox located inside the form tag(form tag code provided below).

<form novalidate="" class="schedules-view__search--box">
  <mc-c-origin-destination class="origin-destination"></mc-c-origin-destination>
  <hr class="schedules-view__search--divider">
  <div class="form-group">
    <div class="date-section">
      <mc-select-native id="sortBy" name="sortBy" value="[{&quot;label&quot;:&quot;Departing&quot;,&quot;value&quot;:&quot;D&quot;}]"><input type="text" name="sortBy" style="display:none" aria-hidden="true" tabindex="-1" value="[{&quot;label&quot;:&quot;Departing&quot;,&quot;value&quot;:&quot;D&quot;}]" data-di-id="di-id-a9af2e59-782584d2"></mc-select-native>
      <mc-input-date data-test="ptp-date-picker"
        dontusenativeonmobile="" id="" name="ptpDatePicker" value="23/11/2023"><input type="text" name="ptpDatePicker" style="display:none" aria-hidden="true" tabindex="-1" value="23/11/2023" data-di-id="di-id-d08f07b1-d6f0dde0"></mc-input-date>
    </div>
    <hr class="schedules-view__search--divider">
    <div class="container-section">
      <div class="form-group">
        <mc-select-native id="containerType" name="containerType" value="[{&quot;label&quot;:&quot;40' Dry Standard&quot;,&quot;value&quot;:&quot;42G1&quot;}]"><input type="text" name="containerType" style="display:none" aria-hidden="true" tabindex="-1" value="[{&quot;label&quot;:&quot;40' Dry Standard&quot;,&quot;value&quot;:&quot;42G1&quot;}]" data-di-id="di-id-409271ab-305b2833"></mc-select-native>
        <mc-checkbox
          id="reeferCheckbox"></mc-checkbox>
      </div>
    </div>
  </div>
  <hr class="schedules-view__search--divider">
  <div class="form-group vessel-flag">
    <mc-c-typeahead clearbutton="" id="vesselFlag" name="vesselFlag" value=""><input type="text" name="vesselFlag" style="display:none" aria-hidden="true" tabindex="-1" value="" data-di-id="di-id-f906762b-b9bc3f2e"></mc-c-typeahead>
  </div>
  <div class="form-group">
    <mc-button data-test="ptp-search-button" fit="medium" appearance="primary" variant="filled" width="full-width">Search</mc-button>
  </div>
</form>

Error message:

input_element[0].click()

OUTPUT:

------------------------------------------------------------------------ 
---
ElementNotInteractableException           Traceback (most recent call 
last)
Cell In[26], line 1
----> 1 input_element[0].click()

File ~\anaconda3\lib\site- 
packages\selenium\webdriver\remote\webelement.py:93, in 
WebElement.click(self)
     91 def click(self) -> None:
     92     """Clicks the element."""
---> 93     self._execute(Command.CLICK_ELEMENT)

File ~\anaconda3\lib\site- 
packages\selenium\webdriver\remote\webelement.py:410, in 
WebElement._execute(self, command, params)
    408     params = {}
    409 params["id"] = self._id
--> 410 return self._parent.execute(command, params)

File ~\anaconda3\lib\site- 
packages\selenium\webdriver\remote\webdriver.py:444, in 
WebDriver.execute(self, driver_command, params)
    442 response = self.command_executor.execute(driver_command, params)
    443 if response:
--> 444     self.error_handler.check_response(response)
    445     response["value"] = self._unwrap_value(response.get("value", 
None))
    446     return response

File ~\anaconda3\lib\site- 
packages\selenium\webdriver\remote\errorhandler.py:249, in 
ErrorHandler.check_response(self, response)
247         alert_text = value["alert"].get("text")
248     raise exception_class(message, screen, stacktrace, alert_text)  
 # type: ignore[call-arg]  # mypy is not smart enough here
--> 249 raise exception_class(message, screen, stacktrace)

ElementNotInteractableException: Message: element not interactable
(Session info: chrome=119.0.6045.160)
Stacktrace:
GetHandleVerifier [0x003072A3+45731]
(No symbol) [0x00292D51]
(No symbol) [0x001886D0]
(No symbol) [0x001BCAC4]
(No symbol) [0x001B4FC0]
(No symbol) [0x001D7FDC]
(No symbol) [0x001B4A4E]
(No symbol) [0x001D8254]
(No symbol) [0x001EB7A2]
(No symbol) [0x001D7DD6]
(No symbol) [0x001B31F6]
(No symbol) [0x001B439D]
GetHandleVerifier [0x00610716+3229462]
GetHandleVerifier [0x006584C8+3523784]
GetHandleVerifier [0x0065214C+3498316]
GetHandleVerifier [0x00391680+611968]
(No symbol) [0x0029CCCC]
(No symbol) [0x00298DF8]
(No symbol) [0x00298F1D]
(No symbol) [0x0028B2C7]
BaseThreadInitThunk [0x7548FCC9+25]
RtlGetAppContainerNamedObjectPath [0x770A7C6E+286]
RtlGetAppContainerNamedObjectPath [0x770A7C3E+238]
(No symbol) [0x00000000]

Screen shots:

In this Web Page also contains "iframe"


Solution

  • Your input is placed inside several shadow-roots, to get internal shadow root structure, you should get it's host first and then get shadowRoot property.

    In your case you can write function that drills inside several shadow-roots.

    1st level has class origin-destination 2nd level has id origin

    get_shadow_root function gets shadow root from host, using JS executor.

    drill_into_shadow_roots goes inside every provided selector from array, makes it host and gets shadowRoot property, so next iteration would start from new host.

    More about Shadow Dom

    Similar question reference

    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
    
    driver = webdriver.Chrome()
    timeout = 10
    wait = WebDriverWait(driver, timeout)
    
    def get_shadow_root(element):
        return driver.execute_script('return arguments[0].shadowRoot', element)
    
    def drill_into_shadow_roots(selectors):
        host = None
        _wait = WebDriverWait(driver, timeout)
        for by, selector in selectors:
            host = _wait.until(EC.presence_of_element_located((by, selector)))
            host = get_shadow_root(host)
            _wait = WebDriverWait(host, timeout)
        return host
    
    driver.get(f"https://www.maersk.com/schedules/pointToPoint")
    driver.maximize_window()
    
    allow_all = wait.until(EC.presence_of_element_located((By.XPATH, "//*[@id='coiPage-1']/div[2]/button[3]")))
    allow_all.click()
    
    form = driver.find_element(By.CLASS_NAME, "schedules-view__search--box")
    
    selectors = [(By.CLASS_NAME, 'origin-destination'), (By.ID, 'origin')]
    from_input = drill_into_shadow_roots(selectors).find_element(By.CSS_SELECTOR, 'input')
    from_input.send_keys('Oslo')