'Skip clicking on an element in the While loop, when opening a popup with selenium. Problem with While loop

This loop scrolls through the list of names on a web page and clicks on each "Add" button next to the name. It works well and correctly

wait = WebDriverWait(driver, 30)
i = 1

while i <= limit:
    i = i + 1

    row = wait.until(EC.visibility_of_element_located((By.XPATH, f"(//div[@data-visualcompletion='ignore-dynamic' and not (@role) and not (@class)])[{i}]")))
    add = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Add']"))).click()
    
    driver.execute_script("arguments[0].scrollIntoView(true);", row, add)
 

Some buttons create problems, because if I click on them I get an error popup who says I can't add that person to friends. So to solve i created this close_popup = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Ok']"))).click() to automatically click "Ok" and close the popup. This code works well and correctly, click correctly on the "Ok" button and close the popup correctly. For information to the reader, if I don't use "close_popup", I get the error ElementClickInterceptedError enter image description here

DESCRIPTION OF THE PROBLEM: The problem is when the popup closes,because the button changes from "Cancel Request" to "Add Friend" and you will click again (indefinitely) the latter "Add Friend" which I cannot add. When the popup closes, the loop will always click on the same "Add" button (for example John, the button that gives an error and opens the popup) without ever going forward and never continuing to click on the other buttons. IMPORTANT: This happens because a certain "Add Friend" button (for example John only), when I click on it, changes to "Cancel Request" and the popup opens. So when I click "Ok" on the popup and it closes, then the "Cancel Request" button will return to "Add Friend".

Consequently the script will continue normally and every "Add Friend" button will be clicked, including John's that I can't add and that opens the popup.

enter image description here

Consequently, I will indefinitely obtain these operations in the following order:

  1. Click on the "Add" button
  2. Opening the popup
  3. Closing the popup, thanks to the code that clicks on "Ok"
  4. Click on the "Add" button again, because when I close the popup the button will change name from "Cancel Request" to "Add"
  5. Beginning at infinity of points 1 to 4 above

I would like to skip the click on the "Add Friend" button that blocks the loop (on it or on other successive ones) and opens the popup.

Precisely I would like that if the popup opens, then when the popup closes and returns to the list of people's names, I would like to resume clicking on the "Add Friend" buttons BUT by skipping a single button (1 only, only the one that opens the popup and blocks the loop) and then resuming clicking all the "Add Frriend" buttons again of the first While, without skipping any more (unless the same problem occurs with a popup).

So first execute the first While, then execute the second While of the exception (which skips a single button), then return to the first While that continues to click on subsequent buttons (after the jumped button). The purpose of the second While is to call it whenever no "Add Friend" button can be pressed, so its purpose is to skip an "Add Friend" button, then resume clicking subsequent buttons (after skipping a button). This was my idea, but I'm not sure I wrote the second While code well

I tried adding a try and except to the above code. The exception is this (i approach the solution, but there is something wrong):

except ElementClickInterceptedException:
    print("A button cannot be pressed. You go to the next button ...")

    close_popup = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Ok']"))).click()

    #I skip a button, add only one (1) friend, then stop, and it's back to the main loop for to add and click on all friends
    while True:
        x = x + 2
        row_skip = wait.until(EC.visibility_of_element_located((By.XPATH, f"(//div[@data-visualcompletion='ignore-dynamic' and not (@role) and not (@class)])[{x}]")))
        addd_skip = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Add Friend']"))).click()
        break
            

I had thought of this solution, but I don't know if it is right. I had thought that when I get that popup, it can momentarily start an increment of 2 to skip the button. After this, we close the increment of 2 and this while loop, and then we return to the main while loop. Maybe another solution would be that if the same button is clicked twice, then you have to skip it

How can I solve the problem? Can you suggest a code answer please? Thanks

This is the complete and executable code (including GUI) in case anyone would like to test and help me. I don't want spam problems, so I created the possibility to choose for example 5 people to send the request to. This script is for my own study purposes only:

    import tkinter as tk                    
    from tkinter import ttk
    from tkinter import *
    from time import sleep
    import time
    import tkinter as tk
    from tkinter import ttk
    
    from selenium.webdriver import Firefox
    from selenium.webdriver.firefox.service import Service
    from selenium.webdriver.firefox.options import Options
    from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
    import os
    
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from selenium import webdriver
    from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
    
    root = tk.Tk()
    root.title("...")
    root.geometry('530x500')
    root.configure(bg='white')
    
    topbar = tk.Frame(root, bg='#3b589e', height=42, width = 660)
    topbar.place(x=1, y=1)
    
    secondbar = tk.Frame(root, bg='#f0f2f5', height=42, width = 660)
    secondbar.place(x=1, y=40)
    
    #GUI
    label_email = Label(root, text="email", bg='#3b589e', foreground="white")
    label_email.place(x=2, y=10)
    email = tk.Entry(root)
    email.place(x=50, y = 9)
    
    label_password = Label(root, text="password", bg='#3b589e', foreground="white")
    label_password.place(x=260, y = 10)
    password = tk.Entry(root)
    password.place(x=335, y = 9)
    
    link_label = Label(root, text="Post Link", bg='#f0f2f5', foreground="black")
    link_label.place(x=2, y = 52)
    link = tk.Entry(root, width = 50)
    link.place(x=97, y = 50)
    
    link.insert(tk.END, "https:/www.facebook.com/FranzKafkaAuthor/posts/3985338151528881") #example link
    
    number_requests_label = Label(root, text="How many requests to send?", bg='white', foreground="black")
    number_requests_label.place(x=2, y = 110)
    number_requests = tk.Entry(root)
    number_requests.place(x=4, y = 130)
    number_requests.insert(tk.END, "5")
    
    time_label = Label(root, text="Seconds between requests?", bg='white', foreground="black")
    time_label.place(x=2, y = 180)
    time_requests = tk.Entry(root)
    time_requests.place(x=4, y = 200)
    
    
    def start_with_limits(): 
        delay = int(time_requests.get())
        limit = int(number_requests.get())
    
        #Access Facebook
        profile_path = '/usr/bin/firefox/firefox'
    
        options=Options()
        options.set_preference('profile', profile_path)
        options.set_preference('network.proxy.type', 4)
        options.set_preference('network.proxy.socks', '127.0.0.1')
        options.set_preference('network.proxy.socks_port', 9050)
        options.set_preference("network.proxy.socks_remote_dns", False)
    
        service = Service('/home/xxxx/bin/geckodriver') ########### CHANGE YOUR PATH
        driver = Firefox(service=service, options=options)
        driver.set_window_size(600, 990)
        driver.set_window_position(1, 1)
        driver.get("http://www.facebook.com")
    
        #Cookies before login
        WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[data-cookiebanner="accept_button"]'))).click()
    
        #Login
        username_box = driver.find_element(By.ID, 'email')
        username_box.send_keys(email.get())
        password_box = driver.find_element(By.ID, 'pass')
        password_box.send_keys(password.get())
    
        WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, '/html/body/div[1]/div[2]/div[1]/div/div/div/div[2]/div/div[1]/form/div[2]/button'))).click()
    
        # --OPTIONAL
        #Cookies before login (Sometimes it is required while sometimes not
        #WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[aria-label='Allow all cookies'] span span"))).click()
    
    
        #Open link
        driver.get(link.get())
    
        #Click on icon-like
        WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[@class='j1lvzwm4']"))).click()
        time.sleep(1)
    
        #Click su ALL
        WebDriverWait(driver, 2000).until(EC.element_to_be_clickable((By.XPATH, '/html/body/div[1]/div/div[1]/div/div[4]/div/div/div[1]/div/div[2]/div/div/div/div[1]/div/div/div/div/div[2]/div[2]'))).click()
        time.sleep(1)
    
        #Scroll down and press "Add friend" buttons
        wait = WebDriverWait(driver, 30)
        i = 1

        try:
    
            while i <= limit:
                i = i + 1
    
                row = wait.until(EC.visibility_of_element_located((By.XPATH, f"(//div[@data-visualcompletion='ignore-dynamic' and not (@role) and not (@class)])[{i}]")))
                add = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Add Friend']"))).click()
                
                driver.execute_script("arguments[0].scrollIntoView(true);", row, add)
                time.sleep(delay)
    
                #code for close popup
                #close_popup = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Ok']"))).click()
    
    
        except ElementClickInterceptedException:
            print("A button cannot be pressed. You go to the next button ...")

            close_popup = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Ok']"))).click()

            #I skip a button, add only one (1) friend, then stop, and it's back to the main loop for to add and click on all friends
            while True:
                x = x + 2
                row_skip = wait.until(EC.visibility_of_element_located((By.XPATH, f"(//div[@data-visualcompletion='ignore-dynamic' and not (@role) and not (@class)])[{x}]")))
                addd_skip = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Add Friend']"))).click()
                break


begin = Button(root, text="Start", bg='#3b589e', foreground='white', width=7, command= start_with_limits)
begin.place(x=1, y=420)

root.mainloop()


Solution 1:[1]

since you are changing the site as you scrape the information, your loop wont work the way you want it to work.

i would recomment to first get the information you need and then work trough it:

rows = driver.find_elements(
    by=By.XPATH,
    value=f"(//div[@data-visualcompletion='ignore-dynamic' and not (@role) and not (@class)])"
)

for row in rows:
    driver.execute_script("arguments[0].scrollIntoView(true);", row)
    add_button = driver.find_element(
        by=By.XPATH,
        value="//span[text()='Add Friend']"
    )

    add_button.click()
    
    time.sleep(delay)

    try:
        close_popup_button = driver.find_element(
            by=By.XPATH,
            value="//span[text()='Ok']"
        )
        close_popup_button.click()
    except NoSuchElementException:
        pass

this might not scale very well with content that is dynamically loaded in, but for this purpose it is enough.

if you ever want to add such behavior, i would take a look at sets, which ensure that there are no duplicate values and are somewhat more efficient.

you could then have a loop that does the following until it does not find any new values.

  1. find desired elements
  2. for each element:
    1. test if element is in set of already clicked elements
    2. if yes, do nothing, if not click on element and add it to the set
  3. scroll to the bottom of the page

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 aarondiel