Reputation: 57
I'm trying to generate data from this website https://brainbooks.pk/newtest-system/free_mcqs.php . The form is dynamic. In the form there are four drop down menus. I'm able to select first 3 (Board, Class and Subject) then there is the unit drop down the last one. The one with the check box. I want to loop through and check them one by one. I'm unable to loop through unit element. Here's my code:
import time
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select, WebDriverWait
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get('https://brainbooks.pk/newtest-system/free_mcqs.php')
def get_options(driver, class_name):
drop_down = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, class_name)))
select = Select(drop_down)
return select.options
board_option = driver.find_element_by_xpath('//*[@id="board_id"]/option[2]')
board_option.click()
time.sleep(0.5)
class_options = ['//*[@id="class_id"]/option[2]','//*[@id="class_id"]/option[3]','//*[@id="class_id"]/option[4]','//*[@id="class_id"]/option[5]']
for class_select in class_options:
select_class = driver.find_element_by_xpath(class_select)
print(select_class.text)
select_class.click()
if class_select == '//*[@id="class_id"]/option[2]':
subject_options = get_options(driver, 'subject')
for subj in subject_options[1:]:
print(subj.text)
subj.click()
topic_options = get_options(driver, 'q_unit')
time.sleep(1.5)
for topic in topic_options:
print(topic)
topic.click()
time.sleep(1.5)
break
break
break
break
EDIT 1
The break
are temporary. If one loop execute successfully them I'm going to remove them and let the script run completely.
I tried to loop through check box using this code "//input[@type='checkbox'][@data-chid='" + i+ "']"
and got this error TypeError: 'WebElement' object is not iterable
import time
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select, WebDriverWait
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get('https://brainbooks.pk/newtest-system/free_mcqs.php')
def get_options(driver, class_name):
drop_down = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, class_name)))
select = Select(drop_down)
return select.options
board_option = driver.find_element_by_xpath('//*[@id="board_id"]/option[2]')
board_option.click()
time.sleep(0.5)
class_options = ['//*[@id="class_id"]/option[2]','//*[@id="class_id"]/option[3]','//*[@id="class_id"]/option[4]','//*[@id="class_id"]/option[5]']
for class_select in class_options:
select_class = driver.find_element_by_xpath(class_select)
print(select_class.text)
select_class.click()
if class_select == '//*[@id="class_id"]/option[2]':
subject_options = get_options(driver, 'subject')
for subj in subject_options[1:]:
print(subj.text)
subj.click()
topic_options = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'q_unit')))
time.sleep(1.5)
for topic in topic_options:
select_topic = driver.find_element_by_xpath("//input[@type='checkbox'][@data-chid='" + topic + "']").click()
print(select_topic.text)
select_topic.click()
time.sleep(1.5)
break
break
break
break
EDIT 2
It's working perfectly fine for 1 iteration after that I got this error element not interactable
import time
import pyautogui
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select, WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get('https://brainbooks.pk/newtest-system/free_mcqs.php')
def get_options(driver, class_name):
drop_down = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, class_name)))
select = Select(drop_down)
return select.options
board_option = driver.find_element_by_xpath('//*[@id="board_id"]/option[2]')
board_option.click()
time.sleep(0.5)
class_options = ['//*[@id="class_id"]/option[2]','//*[@id="class_id"]/option[3]','//*[@id="class_id"]/option[4]','//*[@id="class_id"]/option[5]']
for class_select in class_options:
select_class = driver.find_element_by_xpath(class_select)
select_class.click()
time.sleep(2)
if class_select == '//*[@id="class_id"]/option[2]':
try:
subject_options = get_options(driver, 'subject')
except:
select_class = driver.find_element_by_xpath('//*[@id="class_id"]/option[1]')
select_class.click()
select_class = driver.find_element_by_xpath(class_select)
select_class.click()
time.sleep(2)
finally:
subject_options = get_options(driver, 'subject')
for subj in subject_options[1:]:
print(subj.text)
sub_name = subj.text
subj.click()
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'mutliSelect')))
units_container = driver.find_element_by_class_name("mutliSelect")
topic_options = units_container.find_elements_by_xpath("//input[@type='checkbox']")
time.sleep(1.5)
for select_topic in topic_options:
topic_name = select_topic.find_element_by_xpath("..")
print(topic_name.text)
select_topic.click()
time.sleep(1.5)
generate_button = (driver.find_element_by_xpath('/html/body/div[2]/div/div[2]/div[1]/div/div[1]/div/button[1]')).click()
time.sleep(3)
view_result_button = (driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div[1]/div/div[1]/div/button[3]')).click()
#Saving Html Pages
topic_title = topic_name.text
topic_title = topic_title.replace(':', '-')
topic_title = topic_title.replace(' ', ' ')
save_file_name = sub_name+'-'+topic_title
pyautogui.hotkey("ctrlleft", "s") # Saving File
time.sleep(.5)
pyautogui.press('f4') # Opening Address Bar
time.sleep(.5)
pyautogui.hotkey("ctrlleft", "a") # Selecting Previous Address
time.sleep(.5)
pyautogui.press('delete') # Removing Previous Address
time.sleep(.5)
pyautogui.write('C:\\Users\Ali Abdullah\Downloads\BrainFreeMCQS') # Entering New Address
time.sleep(.5)
pyautogui.press('enter')
time.sleep(.5)
pyautogui.click(496,342)
time.sleep(.5)
pyautogui.hotkey("ctrlleft", "a") # Selecting File Name
time.sleep(.5)
pyautogui.press('delete') # Deleting Previous File Name
time.sleep(.5)
pyautogui.write(save_file_name) # Entering New File Name
time.sleep(.5)
pyautogui.click(448,451) # Clicking Save
EDIT 3
When I try to add pyguiauto code to save the complete webpage. The select_topic.click()
Which I use for De Selecting Previous CheckBox does not work. Error: 'WebElement' object is not iterable
import time
import pyautogui
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select, WebDriverWait
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get('https://brainbooks.pk/newtest-system/free_mcqs.php')
def get_options(driver, class_name):
drop_down = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, class_name)))
select = Select(drop_down)
return select.options
board_option = driver.find_element_by_xpath('//*[@id="board_id"]/option[2]')
board_option.click()
time.sleep(0.5)
class_options = ['//*[@id="class_id"]/option[2]','//*[@id="class_id"]/option[3]','//*[@id="class_id"]/option[4]','//*[@id="class_id"]/option[5]']
for class_select in class_options:
select_class = driver.find_element_by_xpath(class_select)
print(select_class.text)
select_class.click()
time.sleep(3)
if class_select == '//*[@id="class_id"]/option[2]':
try:
subject_options = get_options(driver, 'subject')
except:
select_class = driver.find_element_by_xpath('//*[@id="class_id"]/option[1]')
select_class.click()
select_class = driver.find_element_by_xpath(class_select)
select_class.click()
time.sleep(2)
finally:
subject_options = get_options(driver, 'subject')
for subj in subject_options[1:]:
print(subj.text)
subj.click()
time.sleep(3)
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'mutliSelect')))
units_container = driver.find_element_by_class_name("mutliSelect")
topic_options = units_container.find_elements_by_xpath("//input[@type='checkbox']")
time.sleep(1.5)
for select_topic in topic_options:
topic_name = select_topic.find_element_by_xpath("..")
print(topic_name.text)
select_topic.click()
time.sleep(1.5)
generate_button = (driver.find_element_by_xpath('/html/body/div[2]/div/div[2]/div[1]/div/div[1]/div/button[1]')).click()
time.sleep(3)
select_topic.click() #De Selecting Previous CheckBox
time.sleep(1)
view_result_button = (driver.find_element_by_xpath('/html/body/div[3]/div/div[2]/div[1]/div/div[1]/div/button[3]')).click()
# Saving Html Pages
sub_name = subj.text
topic_title = topic_name.text
topic_title = topic_title.replace(':', '-')
topic_title = topic_title.replace(' ', ' ')
save_file_name = sub_name + '-' + topic_title
time.sleep(1)
pyautogui.hotkey("ctrlleft", "s") # Saving File
time.sleep(.5)
pyautogui.press('f4') # Opening Address Bar
time.sleep(.5)
pyautogui.hotkey("ctrlleft", "a") # Selecting Previous Address
time.sleep(.5)
pyautogui.press('delete') # Removing Previous Address
time.sleep(.5)
pyautogui.write('C:\\Users\Ali Abdullah\Downloads\BrainFreeMCQS') # Entering New Address
time.sleep(.5)
pyautogui.press('enter')
time.sleep(.5)
pyautogui.click(496, 342)
time.sleep(.5)
pyautogui.hotkey("ctrlleft", "a") # Selecting File Name
time.sleep(.5)
pyautogui.press('delete') # Deleting Previous File Name
time.sleep(.5)
pyautogui.write(save_file_name) # Entering New File Name
time.sleep(.5)
pyautogui.click(448, 451) # Clicking Save
time.sleep(1.5)
try:
if driver is not None:
driver.quit()
except Exception as e:
print(e)
Upvotes: 0
Views: 389
Reputation: 83
The issue with the code was that sometimes when you click on Class, the Subject select does not shows up. So, you were trying to click on an element what was not there. You can also use "mutliSelect" as classname and use that class name to fetch all the checkboxes. This could be more robust instead of getting all the checkboxes. Since this will also help you to get the names of the subjects. I also added a try except block because when trying it manually, sometimes the Subject Select won't appear after selecting class. So I figured a way around it.
import time
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select, WebDriverWait
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get('https://brainbooks.pk/newtest-system/free_mcqs.php')
def get_options(driver, class_name):
drop_down = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, class_name)))
select = Select(drop_down)
return select.options
board_option = driver.find_element_by_xpath('//*[@id="board_id"]/option[2]')
board_option.click()
time.sleep(0.5)
class_options = ['//*[@id="class_id"]/option[2]','//*[@id="class_id"]/option[3]','//*[@id="class_id"]/option[4]','//*[@id="class_id"]/option[5]']
for class_select in class_options:
select_class = driver.find_element_by_xpath(class_select)
print(select_class.text)
select_class.click()
time.sleep(3)
if class_select == '//*[@id="class_id"]/option[2]':
try:
subject_options = get_options(driver, 'subject')
except:
select_class = driver.find_element_by_xpath('//*[@id="class_id"]/option[1]')
select_class.click()
select_class = driver.find_element_by_xpath(class_select)
select_class.click()
time.sleep(2)
finally:
subject_options = get_options(driver, 'subject')
for subj in subject_options[1:]:
print(subj.text)
subj.click()
time.sleep(3)
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'mutliSelect')))
units_container = driver.find_element_by_class_name("mutliSelect")
topic_options = units_container.find_elements_by_xpath("//input[@type='checkbox']")
time.sleep(1.5)
for select_topic in topic_options:
topic_name = select_topic.find_element_by_xpath("..")
print(topic_name.text)
select_topic.click()
time.sleep(1.5)
break
break
break
break
try:
if driver is not None:
driver.quit()
except Exception as e:
print(e)
If you use q_unit, it will also work the same way. Since you are directly getting element from WebDriverWait, and using ID, it assumes there is only one element with the ID as that is how WebriverWait returns elements. So instead of getting element from WebDriverWait, you need to use it like this:
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'q_unit')))
topic_options = driver.find_elements_by_id("q_unit")
And to get names using this method, it will be similar to what we did above:
for select_topic in topic_options:
topic_name = select_topic.find_element_by_xpath("..")
print(topic_name.text)
select_topic.click()
time.sleep(1.5)
I am getting the parent element from input type since li contains the text and not input tag. So how you are getting the text will not work.
This code is working for all the subjects now. Please look at the output if that is what was needed.
Upvotes: 1
Reputation: 1026
use below XPath where data_child value i is the variant and loop through it.
driver.find_element_by_xpath("//input[@type='checkbox'][@data-chid='"+i+"']").click()
Update: data_id are not in sequence, hence it wont loop for all Like Part grammar etc. This is the code I used that worked for me
public class BrainBooks {
public static final String Board= "//select[@id='board_id']";
public static final String Class= "//div[@style='display: block;']/select[@id='class_id']";
public static final String Subject= "//select[@id='subject']";
public void ClickAllTopic(String BoardName,String ClassName,String SubjectName){
driver.findElement(By.xpath(Board)).dropdown().selectByVisibleText(BoardName);
driver.findElement(By.xpath(Class)).dropdown().selectByVisibleText(ClassName);
driver.findElement(By.xpath(Subject)).dropdown().selectByVisibleText(SubjectName);
int TopicCnt= driver.findElements(By.id("q_unit")).size();
for(int i=0;i<TopicCnt;i++){
driver.findElements(By.id("q_unit")).get(i).click();
}
}
}
Again this is Java. But to ur code change topic_options to below
driver.find_elements_by_id('q_unit')
in for loop iterate as below
driver.find_elements_by_id('q_unit').get(topic).click()
Upvotes: 0