4

Sometimes when I'm using selenium to click on a particular link on a page, the click goes through but the website does not respond to the click. For example, here is the situation when I try to navigate between dates on the statistics page on nba.com.

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

def go_to_next_day(driver, next_date):
    for elem in driver.find_elements_by_css_selector('.date-selector i'):
        if 'right' in elem.get_attribute('class'):
            print 'Found next day!'
            elem.click()
            break
    else:
        raise ValueError('Unable to navigate to the next day')

    # wait 30 seconds until the next day loads
    WebDriverWait(driver, 30).until(
        ec.text_to_be_present_in_element((By.CSS_SELECTOR, '.date-selector > span'), next_date.strftime('%m/%d/%Y'))
    )


if __name__ == '__main__':
    # go to nba.com
    driver = webdriver.Firefox()
    driver.set_window_size(2560, 1600)
    driver.get('http://stats.nba.com/scores/#!/10/03/2014')

    print 'NBA.com loaded. Clicking to next day!!!'
    end_date = datetime.datetime.now()
    current_date = datetime.datetime.strptime('2014-10-03', '%Y-%m-%d')

    # after page loads, just click right until you get to current date
    while current_date <= end_date:
        # do something interesting on the page, modeled as a sleep
        time.sleep(1)

        next_date = current_date + datetime.timedelta(days=1)
        go_to_next_day(driver, next_date)
        current_date = next_date
        print 'Went to day {}'.format(current_date)

    driver.quit()
    print 'Done'

Why is it that the script always clicks, but the website only changes its page sometimes? Is it something to do with angular? I doubt it has anything to do with the OS, but I'm on a Mac OS X.

I'm not sure and would really like to figure out how to avoid the click failing, especially because I think I click and wait in the selenium way.

Community
  • 1
  • 1

1 Answers1

3

The problem is that the click does not make it go to the next day if the current day's data is still loading. In other words, if the "loading spinner" is visible - clicking the > button has no effect.

To solve it: wait for invisibility of the div.loader element containing the spinner:

def go_to_next_day(driver, next_date):
    wait = WebDriverWait(driver, 10)
    actions = ActionChains(driver)
    try:
        next_button = wait.until(ec.element_to_be_clickable((By.CSS_SELECTOR, '.date-selector i[class*=right]')))
        actions.move_to_element(next_button).click().perform()
    except TimeoutException:
        raise ValueError('Unable to navigate to the next day')

    # THIS IS THE KEY FIX
    wait.until(ec.invisibility_of_element_located((By.CSS_SELECTOR, "div.loader")))

    # wait until the next day loads
    wait.until(
        ec.text_to_be_present_in_element((By.CSS_SELECTOR, '.date-selector > span'), next_date.strftime('%m/%d/%Y'))
    )

I'm also operating with the next button a bit differently, feel free to continue with your own approach or switch to mine - the key fix is in the waiting for "spinner" invisibility. Works for me.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • So in this case there is an element I could directly look at to make sure the page loaded properly. What if there was no element like div.loader? Also what is the inherent advantage of using ActionChains over clicks? –  Sep 26 '15 at 04:52
  • @AkshayDongaonkar this problem is actually web-site specific. I usually use this "trick" that waits for invisibility of the "loading" icon/spinner etc when there is some loading process in action. And more often I just wait for the desired content to appear - explicit wait for the visibility/presence of the container with the desired data. – alecxe Sep 26 '15 at 05:05
  • @AkshayDongaonkar action chains in this case help to mouse over the element and then click - sometimes it helps to tackle the "clicking" issues. I'm pretty sure you actually don't need action chains here. – alecxe Sep 26 '15 at 05:06
  • yupp I usually wait for an explicit element indicating that the content has loaded. However, in some cases I'm done processing the page so fast that navigating away doesn't work because the page hasn't fully loaded. I was just wondering if there was a silver bullet telling me whether the DOM was completely stable and the javascript had stopped running. –  Sep 26 '15 at 05:20
  • 1
    @AkshayDongaonkar yeah, strictly speaking, no silver bullet..webdriverwait with expected conditions is the most practical approach I think. – alecxe Sep 26 '15 at 05:24