Python, Selenium find element with class and wait for class change

11,477

Solution 1

@LucasTierney's answer was in the right direction. However I still feel the solution can be optimized as follows:

As the wheel is visible, instead of presence_of_element_located() method you need to use visibility_of_element_located() method.

The node:

<div class="wheel spinning"></div>

Can't be located through the XPath containing a single class i.e. only wheel as in:

el = WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.XPATH, "//*[@class='wheel']")))

Instead you can use either of the Locator Strategies:

  • cssSelector:

    el = WebDriverWait(driver, 30).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.wheel.spinning")))
    WebDriverWait(driver, 10).until(lambda d: 'spinning' not in el.get_attribute('class'))
    
  • xpath:

    el = WebDriverWait(driver, 30).until(EC.visibility_of_element_located((By.XPATH, "//div[@class='wheel spinning']")))
    WebDriverWait(driver, 10).until(lambda d: 'spinning' not in el.get_attribute('class'))
    

Solution 2

You can wait for the class value to change. For example:

from selenium.webdriver.support.ui import WebDriverWait

# Wait longer than 10 seconds since you're getting occasional timeout
el = WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.XPATH, "//*[@class='wheel']")))


wait = WebDriverWait(driver, 10)
wait.until(lambda d: 'spinning' not in el.get_attribute('class'))

The until method passes the driver to the method given, so you can make your own expected condition pretty easily. The above uses an anonymous lambda function but you could also use a closure or a anything callable that takes in an argument (the ExpectedConditions library is just a set of callable classes). Here is the same with a closure:

from selenium.webdriver.support.ui import WebDriverWait


# Wait longer than 10 seconds since you're getting occasional timeout
el = WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.XPATH, "//*[@class='wheel']")))

def wait_not_spinning(driver):
    return 'spinning' not in el.get_attribute('class')

wait = WebDriverWait(driver, 10)
wait.until(wait_not_spinning)
Share:
11,477
lonerunner
Author by

lonerunner

I am Freelance Web Designer and Server Administrator with passion in Photography. My main area of expertise is web development and design. I love making cool stuff on the web, and mainly i work with WordPress and Joomla cms. I am experienced in customizing and developing WordPress driven websites. As a Server Administrator over past years i have built and setup various web servers from shared hosting solutions to dedicated servers for audio and video streaming, gaming servers, and web servers. I also love photography and in my free time i always take my camera out trying to snatch few cool photos. I also do product shots for catalogs, flyers, websites, etc...

Updated on June 25, 2022

Comments

  • lonerunner
    lonerunner almost 2 years

    I have a web page which loads content dynamically and while page loads, there is spinning wheel, I already found solution to grab content loaded immediately on page, but seems i can't find solution to grab content loaded later in dom.

    What i can think of is to find element with specific class of that wheel spinning, and wait for it to change, once it's changed, than it means content is loaded in dom.

    I am using Selenium with Firefox webdriver on Ubuntu.

    Here is the class i am looking to monitor:

    <div class="wheel spinning"></div>
    

    Once content is loaded, wheel stop spinning and class is changed to:

    <div class="wheel"></div>
    

    Anyone find solution to find and monitor class="wheel spinning" and once it's changed to class="wheel" to continue to grab data.

    Edit:

    The XPATH actually solved one part of solution, here's part of code

    try:
        element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.XPATH, "//*[@class='wheel']))
    )
    title = driver.find_element_by_xpath('/html/body/div[1]/div[1]/div[3]')
    print(title.text)
    

    But if element don't appear within 10 seconds it error's out, now to find a way to retry again and again until element is present on page.

    Is there a difference in use presence_of_element_located((By.XPATH)) and find_element_by_xpath

  • lonerunner
    lonerunner over 5 years
    Can you explain, what is meaning behind lambda d: ? Thanks
  • Lucas Tierney
    Lucas Tierney over 5 years
    Lambda is just an anonymous function. The until automatically calls the passed in driver to the method given so that's what the d argument for the lambda is. I'll update it to show the same with a closure