pythonseleniumteachable-machine

selenium giving me NoSuchElementException when it's clear that it exists


I'm trying to upload pictures in google teachable machine using selenium in python, cuz there's too many for me to do it manually.

I keep getting NoSuchElementException when it's clear that it exists. Errors used to happen at

    driver.find_element(By.CSS_SELECTOR, ".add-classes").click()
 

and now it happens at: since i've added the code.

iframe=driver.find_element(By.TAG_NAME,"HTMLCollection []") WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.NAME,iframe))) WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".add-classes"))).click()

how can I get this working?

https://teachablemachine.withgoogle.com/train/image <<--- this is the webpage

i've tried switching to iframe.

document.getElementsByTagName("iframe") 

running this in the console of the page gives me a suspicious answer: HTMLCollection []. I've tried to switch to it as you can see below and the full code: iframe=driver.find_element(By.TAG_NAME,"HTMLCollection []")

i've also tried waiting for the page to load:

WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.NAME,iframe)))
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".add-classes"))).click()

here's my full code:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import urllib.request
from selenium.webdriver.common.by import By
from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException
import os
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

names1=['Dwayne Johnson', 'Taylor Swift', 'Brad Pitt', 'Jennifer Aniston', 'Kim Kardashian', 'Jessica Alba', 'Tom Cruise', 'Cardi B', 'Tom Hanks', 'Kanye West', 'Justin Bieber', 'Elon Musk', 'George Clooney', 'Emma Watson', 'Will Smith', 'Angelina Jolie', 'Johnny Depp', 'Jennifer Lopez', 'Kirstie Alley', 'Sandra Bullock', 'Julia Roberts', 'Leonardo DiCaprio', 'Steven Spielberg', 'Mel Gibson', 'Ellen DeGeneres', 'Christina Aguilera', 'Natalie Portman', 'Scarlett Johansson', 'Robert Downey Jr.', 'Harrison Ford', 'Robin Williams', 'Bruce Willis', 'Mark Wahlberg', 'Elvis Presley', 'Amy Adams', 'Madonna', 'Kobe Bryant', 'Jim Carrey', 'Drake', 'Rihanna', 'Halle Berry', 'Lady Gaga', 'Michael Jackson', 'Katy Perry', 'Beyoncé', 'Emma Stone', 'Steve Carell', 'Arnold Schwarzenegger']
names2=["방탄 진", "정국","방탄 지민","뷔","슈가","RM","제이홉","conan o'brien","kevin hart","dave chappel","donald trump","joe biden","hilery clinton","j cole","kendrick lamar"]
names=names1+names2

JS_DROP_FILE = """
    var target = arguments[0],
        offsetX = arguments[1],
        offsetY = arguments[2],
        document = target.ownerDocument || document,
        window = document.defaultView || window;

    var input = document.createElement('INPUT');
    input.type = 'file';
    input.onchange = function () {
      var rect = target.getBoundingClientRect(),
          x = rect.left + (offsetX || (rect.width >> 1)),
          y = rect.top + (offsetY || (rect.height >> 1)),
          dataTransfer = { files: this.files };

      ['dragenter', 'dragover', 'drop'].forEach(function (name) {
        var evt = document.createEvent('MouseEvent');
        evt.initMouseEvent(name, !0, !0, window, 0, 0, 0, x, y, !1, !1, !1, !1, 0, null);
        evt.dataTransfer = dataTransfer;
        target.dispatchEvent(evt);
      });

      setTimeout(function () { document.body.removeChild(input); }, 25);
    };
    document.body.appendChild(input);
    return input;
"""

def drag_and_drop_file(drop_target, path):
    driver = drop_target.parent
    file_input = driver.execute_script(JS_DROP_FILE, drop_target, 0, 0)
    file_input.send_keys(path)

PATH=R"C:\Users\tmdwn\OneDrive\Documents\웹개발_ai\selenium\chromedriver.exe"
driver=webdriver.Chrome()
driver.get("https://teachablemachine.withgoogle.com/train/image")
driver.implicitly_wait(15)
# ㅅㅂ 있는데 왜 안돼냐?
    # Store iframe web element

iframe=driver.find_element(By.TAG_NAME,"HTMLCollection []")
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.NAME,iframe)))
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".add-classes"))).click()



for name in names:
    driver.find_element(By.CSS_SELECTOR, ".add-classes").click()
    time.sleep(0.1)
    driver.find_elements(By.CSS_SELECTOR,"button.sample-source-btn").click()

h3s=driver.find_elements(By.TAG_NAME, "h3")
uploads=driver.find_elements(By.ID,"file-input")
for h3, upload, name in zip(h3s, uploads, names):
    h3.click()
    h3.send_keys(f"{name}")
    drag_and_drop_file(upload, R"C:\Users\tmdwn\OneDrive\Documents\웹개발_ai\first_impression\downloads\{}".format(name))


Solution

  • The errors are happening because you are not handling any shadow root element properly. You need to move down to the tree and handle them one by one using the shadow root class. To learn more Selenium Shadow Root Handling, please have a look.

    Below is a code example of me trying to add Model Class by clicking the button. Hope you understand how to move down the element tree.

    import time
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from webdriver_manager.chrome import ChromeDriverManager
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    driver = webdriver.Chrome(ChromeDriverManager().install())
    
    
    driver.get("https://teachablemachine.withgoogle.com/train/image")
    
    time.sleep(5)
    
    
    try:
       element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#tmApp')))
    except Exception as e:
       print(e)
    
    shadow_root = element.shadow_root
    print(shadow_root)
    
    try:
       root2 = WebDriverWait(driver, 10).until(EC.visibility_of(shadow_root.find_element(By.ID, 'classifier-list')))
    except Exception as e:
       print(e)
    
    time.sleep(10) ###PLEASE handle the popup its also inside shadow root.  I did it manually and successfully added the extra class###
    
    shadow_root2 = root2.shadow_root
    
    
    button = shadow_root2.find_element(By.CSS_SELECTOR, '.add-classes')
    button.click()
    
    
    time.sleep(10)
    
    

    BTW I did close the initial popup manually. You should automate it too. FYI Its also inside a shadow root too