Selenium click() function (seemingly) randomly doesn't click the box [No errors][Python]

浪子不回头ぞ 提交于 2020-02-16 09:53:52

问题


I am new to selenium and am making a bot to automatically do trivia. The login works great, and so do most of the clicking boxes and going to the next question.

Randomly they do not click the button, and also do not click for the next loop. This happens a lot towards the final questions (9 and over) but this could be coincidence.

The very confusing this is that there is no error shown? The code finishes without errors

Does anyone have any idea of why this is happening?

Main Code

from selenium import webdriver
from time import *
from data import *
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class TriviaBot():
    def __init__(self):
        self.driver = webdriver.Chrome()
        self.driver.get("https://www.freekigames.com/educational-trivia")
    def login(self):
        # selects and clicks the "Login/Sign Up" button on the home-page
        self.login_btn = self.driver.find_element_by_xpath('//*[@id="loginContainer"]/a')
        self.login_btn.click()

        sleep(2) # allows username + password box to load in

        self.driver.switch_to.frame(bot.driver.find_element_by_xpath('//*[@id="jPopFrame_content"]')) # switches to correct frame for login box

        # selects username-box and types in my username and then does the same for password
        self.username_box = self.driver.find_element_by_xpath('//*[@id="userName"]') #username
        self.username_box.send_keys(username)

        self.username_box = self.driver.find_element_by_xpath('//*[@id="password"]') #password
        self.username_box.send_keys(password)

        # selects login box and then clicks
        self.login_box = self.driver.find_element_by_xpath('//*[@id="bp_login"]')
        self.login_box.click()

        sleep(2) # allows main page to load again
    def AmericanPresidents(self):
        self.i = 0
        # sends browser to first trivia
        self.driver.get("https://www.freekigames.com/american-presidents-trivia")

        while True: #there are ? # of questions, so I loop it 12 times
            self.i += 1
            if self.i == 13:
                break
            print("run: "+str(self.i))

            # sets the variable to the current question so that the correct answer can be located through answer lists
            self.current_question = ((self.driver.find_element_by_xpath('//*[@id="quizContainer"]/div[2]')).text)

            #setting variables for boxes and their text values. The text can be corrolated to the button allowing the bot to click the correct answer

            self.button_1 = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH,('/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[1]/span[@class="answerBox"]/a[@name="checkboxtag"]'))))
            self.text_1   = (self.driver.find_element_by_xpath('//*[@id="quizContainer"]/div[3]/div[1]/span[2]').text)


            self.button_2 = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH,('/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[2]/span[@class="answerBox"]/a[@name="checkboxtag"]'))))
            self.text_2   = (self.driver.find_element_by_xpath('//*[@id="quizContainer"]/div[3]/div[2]/span[2]').text)


            self.button_3 = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH,('/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[3]/span[@class="answerBox"]/a[@name="checkboxtag"]'))))
            self.text_3   = (self.driver.find_element_by_xpath('//*[@id="quizContainer"]/div[3]/div[3]/span[2]').text)


            self.button_4 = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH,('/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[4]/span[@class="answerBox"]/a[@name="checkboxtag"]'))))
            self.text_4   = (self.driver.find_element_by_xpath('//*[@id="quizContainer"]/div[3]/div[4]/span[2]').text)


            for i in range(0, len(american_presidents)):
                if american_presidents[i]["question"] == self.current_question:
                    self.dataLine = i
            if self.text_1 == american_presidents[self.dataLine]["answer"]:
                self.button_1.click()
            if self.text_2 == american_presidents[self.dataLine]["answer"]:
                self.button_2.click()
            if self.text_3 == american_presidents[self.dataLine]["answer"]:
                self.button_3.click()
            if self.text_4 == american_presidents[self.dataLine]["answer"]:
                self.button_4.click()

            sleep(0.5) # to stop the code from beating itself. Going to quick

            # selects and clicks the "Next Question!" button
            nextquestion_btn = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH,('//*[@id="nextQuestion"]'))))
            nextquestion_btn.click()
bot = TriviaBot()
bot.login()
bot.AmericanPresidents()

data.py

american_presidents = [
    {"question" : "Who was the 1st president of the United States?",
     "answer"   : "George Washington"},
    {"question" : "Who was the 2nd president of the United States?",
     "answer"   : "John Adams"},
    {"question" : "Who was the 3rd president of the United States?",
     "answer"   : "Thomas Jefferson"},
    {"question" : "Who was the 4th president of the United States?",
     "answer"   : "James Madison"},
    {"question" : "Who was the 5th president of the United States?",
     "answer"   : "James Monroe"},
    {"question" : "Who was the 6th president of the United States?",
     "answer"   : "John Quincy Adams"},
    {"question" : "Who was the 7th president of the United States?",
     "answer"   : "Andrew Jackson"},
    {"question" : "Who was the 8th president of the United States?",
     "answer"   : "Martin Van Buren"},
    {"question" : "Who was the 9th president of the United States?",
     "answer"   : "William Henry Harrison"},
    {"question" : "Who was the 10th president of the United States?",
     "answer"   : "John Tyler"},
    {"question" : "Who was the 11th president of the United States?",
     "answer"   : "James K. Polk"},
    {"question" : "Who was the 12th president of the United States?",
     "answer"   : "Zachary Taylor"},
    {"question" : "Who was the 13th president of the United States?",
     "answer"   : "Millard Fillmore"},
    {"question" : "Who was the 14th president of the United States?",
     "answer"   : "Franklin Pierce"},
    {"question" : "Who was the 15th president of the United States?",
     "answer"   : "James Buchanan"},
    {"question" : "Who was the 16th president of the United States?",
     "answer"   : "Abraham Lincoln"},
    {"question" : "Who was the 17th president of the United States?",
     "answer"   : "Andrew Johnson"},
    {"question" : "Who was the 18th president of the United States?",
     "answer"   : "Ulysses S. Grant"},
    {"question" : "Who was the 19th president of the United States?",
     "answer"   : "Rutherford B. Hayes"},
    {"question" : "Who was the 20th president of the United States?",
     "answer"   : "James A. Garfield"},
    {"question" : "Who was the 21st president of the United States?",
     "answer"   : "Chester A. Arthur"},
    {"question" : "Who was the 22nd president of the United States?",
     "answer"   : "Grover Cleveland"},
    {"question" : "Who was the 23rd president of the United States?",
     "answer"   : "Benjamin Harrison"},
    {"question" : "Who was the 24th president of the United States?",
     "answer"   : "Grover Cleveland"},
    {"question" : "Who was the 25th president of the United States?",
     "answer"   : "William McKinley"},
    {"question" : "Who was the 26th president of the United States?",
     "answer"   : "Theodore Roosevelt"},
    {"question" : "Who was the 27th president of the United States?",
     "answer"   : "William Howard Taft"},
    {"question" : "Who was the 28th president of the United States?",
     "answer"   : "Woodrow Wilson"},
    {"question" : "Who was the 29th president of the United States?",
     "answer"   : "Warren G. Harding"},
    {"question" : "Who was the 30th president of the United States?",
     "answer"   : "Clavin Coolidge"},
    {"question" : "Who was the 31st president of the United States?",
     "answer"   : "Herbert Hoover"},
    {"question" : "Who was the 32nd president of the United States?",
     "answer"   : "Frankin D. Roosevelt"},
    {"question" : "Who was the 33rd president of the United States?",
     "answer"   : "Harry S. Truman"},
    {"question" : "Who was the 34th president of the United States?",
     "answer"   : "Dwight D. Eisenhower"},
    {"question" : "Who was the 35th president of the United States?",
     "answer"   : "John F. Kennedy"},
    {"question" : "Who was the 36th president of the United States?",
     "answer"   : "Lyndon B. Johnson"},
    {"question" : "Who was the 37th president of the United States?",
     "answer"   : "Richard Nixon"},
    {"question" : "Who was the 38th president of the United States?",
     "answer"   : "Gerald Ford"},
    {"question" : "Who was the 39th president of the United States?",
     "answer"   : "Jimmy Carter"},
    {"question" : "Who was the 40th president of the United States?",
     "answer"   : "Ronald Reagan"},
    {"question" : "Who was the 41st president of the United States?",
     "answer"   : "George W. H. Bush"},
    {"question" : "Who was the 42nd president of the United States?",
     "answer"   : "Bill Clinton"},
    {"question" : "Who was the 43rd president of the United States?",
     "answer"   : "George W. Bush"},
    {"question" : "Who was the 44th president of the United States?",
     "answer"   : "Barack Obama"}

]

Error

Traceback (most recent call last):
  File ".\trivia-bot.py", line 83, in <module>
    bot.AmericanPresidents()
  File ".\trivia-bot.py", line 80, in AmericanPresidents
    nextquestion_btn.click()
  File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\webelement.py", line 80, in click
    self._execute(Command.CLICK_ELEMENT)
  File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute
    return self._parent.execute(command, params)
  File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable
  (Session info: chrome=79.0.3945.130)

回答1:


The most likely reason you are not seeing an error is because your xpath's that you are using are actually finding elements, but possibly not the elements you intended. The page loads with an animation style for each question and you could be doing your searches before they are done. What you should be doing is using waits for the element to be found and using an xpath more specific to what you need. Here is a link to more info on waits: https://selenium-python.readthedocs.io/waits.html

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC   

self.button_1 = WebDriverWait(driver, 10).until(EC.presence_of_element_located(By.XPATH,'/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[1]/span[@class="answerBox"]/a[@name="checkboxtag"]))

Do this for the rest of your elements. I would also use these locators:

#setting variables for boxes and their text values. The text can be corrolated to the button allowing the bot to click the correct answer
self.button_1 = (self.driver.find_element_by_xpath('/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[1]/span[@class="answerBox"]/a[@name="checkboxtag"]))
self.text_1   = (self.driver.find_element_by_xpath(/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[1]/span[@class="answerText"]).text)

self.button_2 = (self.driver.find_element_by_xpath(/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[2]/span[@class="answerBox"]/a[@name="checkboxtag"]))
self.text_2   = (self.driver.find_element_by_xpath(/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[2]/span[@class="answerText"]).text)

self.button_3 = (self.driver.find_element_by_xpath(/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[3]/span[@class="answerBox"]/a[@name="checkboxtag"]))
self.text_3   = (self.driver.find_element_by_xpath(/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[3]/span[@class="answerText"]).text)

self.button_4 = (self.driver.find_element_by_xpath(/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[4]/span[@class="answerBox"]/a[@name="checkboxtag"]))
self.text_4   = (self.driver.find_element_by_xpath(/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[4]/span[@class="answerText"]).text)

Another option for finding the checkbox to click is to use these locators instead:

self.button_1 = WebDriverWait(driver, 10).until(EC.presence_of_element_located(By.XPATH, '/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[1]/span[@class="answerBox"]/input[@name="answers"]'))
self.button_2 = WebDriverWait(driver, 10).until(EC.presence_of_element_located(By.XPATH, '/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[2]/span[@class="answerBox"]/input[@name="answers"]'))
self.button_3 = WebDriverWait(driver, 10).until(EC.presence_of_element_located(By.XPATH, '/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[3]/span[@class="answerBox"]/input[@name="answers"]'))
self.button_4 = WebDriverWait(driver, 10).until(EC.presence_of_element_located(By.XPATH, '/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[4]/span[@class="answerBox"]/input[@name="answers"]'))

Your error message refers to the next question button and it not being interactable. Use this instead:

 nextquestion_btn = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH,('//*[@id="nextQuestion"]'))))



回答2:


In my experience, a click could go astray if the element is not ready to be clicked. I would try using WebDriverWait to ensure this does not happen.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

button = WebDriverWait(self.driver, 15).until(
  EC.element_to_be_clickable((By.XPATH, '//*[@id="quizContainer"]/div[3]/div[1]/span[1]/a'))
)

button.click()



回答3:


As you are working with clicking to moving to new question there is more chance that next question or element is not loaded properly.

Try to click with webdriver wait for element to be clickable so element able to receive click.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

element = WebDriverWait(driver, 40).until(
EC.element_to_be_clickable((By.XPATH, "//*[@id="bp_login"]")))
element.click()

OR (try to click with Java script but without wait as it can be fail on wait)

element= driver.find_element(By.XPATH, "//*[@id='bp_login']")
driver.execute_script("arguments[0].click();", element)



回答4:


I am not sure how, but I just re-coded everything and it works amazingly

Big Thanks to @Muzzamil and @RKelley for the patience and information.

Here is the (somehow) fixed code

from selenium import webdriver
from time import *
from data import *
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException


class TriviaBot():
    def __init__(self):
        self.driver = webdriver.Chrome()
        self.driver.get("https://www.freekigames.com/educational-trivia")
        self.i = 0
    def login(self):
        # selects and clicks the "Login/Sign Up" button on the home-page
        self.login_btn = self.driver.find_element_by_xpath('//*[@id="loginContainer"]/a')
        self.login_btn.click()

        sleep(2) # allows username + password box to load in

        self.driver.switch_to.frame(bot.driver.find_element_by_xpath('//*[@id="jPopFrame_content"]')) # switches to correct frame for login box

        # selects username-box and types in my username and then does the same for password
        self.username_box = self.driver.find_element_by_xpath('//*[@id="userName"]') #username
        self.username_box.send_keys(username)

        self.username_box = self.driver.find_element_by_xpath('//*[@id="password"]') #password
        self.username_box.send_keys(password)

        # selects login box and then clicks
        self.login_box = self.driver.find_element_by_xpath('//*[@id="bp_login"]')
        self.login_box.click()

        sleep(2) # allows main page to load again
    def AmericanPresidents(self):
        # sends browser to first trivia
        self.driver.get("https://www.freekigames.com/american-presidents-trivia")

        # sets the variable to the current question so that the correct answer can be located through answer lists
        self.current_question = ((self.driver.find_element_by_xpath('//*[@id="quizContainer"]/div[2]')).text)

        #setting variables for boxes and their text values. The text can be corrolated to the button allowing the bot to click the correct answer
        #/html//div[@id="quizContainer"]/div[@class="answersContainer"]/div[1]/span[@class="answerBox"]/a[@name="checkboxtag"]

        for i in range(0, len(american_presidents)):
            if american_presidents[i]["question"] == self.current_question:
                self.current_answer = american_presidents[i]["answer"]


        self.xpath_1  = '//*[@id="quizContainer"]/div[3]/div[1]/span[1]/a'
        self.text_1   = ((WebDriverWait(self.driver, 10)).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="quizContainer"]/div[3]/div[1]/span[2]')))).text


        self.xpath_2  = '//*[@id="quizContainer"]/div[3]/div[2]/span[1]/a'
        self.text_2   = ((WebDriverWait(self.driver, 10)).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="quizContainer"]/div[3]/div[2]/span[2]')))).text


        self.xpath_3  = '//*[@id="quizContainer"]/div[3]/div[3]/span[1]/a'
        self.text_3   = ((WebDriverWait(self.driver, 10)).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="quizContainer"]/div[3]/div[3]/span[2]')))).text


        self.xpath_4  = '//*[@id="quizContainer"]/div[3]/div[4]/span[1]/a'
        self.text_4   = ((WebDriverWait(self.driver, 10)).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="quizContainer"]/div[3]/div[4]/span[2]')))).text


        if self.text_1 == self.current_answer:
            self.button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, self.xpath_1)))
        if self.text_2 == self.current_answer:
            self.button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, self.xpath_2)))
        if self.text_3 == self.current_answer:
            self.button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, self.xpath_3)))
        if self.text_4 == self.current_answer:
            self.button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, self.xpath_4)))

        self.button.click()

        # selects and clicks the "Next Question!" button
        nextquestion_btn = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH,('//*[@id="nextQuestion"]'))))
        nextquestion_btn.click()

bot = TriviaBot()
bot.login()
for i in range(1, 13):
    print("Run: "+str(i))
    bot.AmericanPresidents()

print("will it work afterwards?")

Thank you again for all your effort and patience. I can finally finish this darn code :D



来源:https://stackoverflow.com/questions/60045804/selenium-click-function-seemingly-randomly-doesnt-click-the-box-no-errors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!