在打开带有 selenium 的弹出窗口时,跳过单击 While 循环中的元素。While 循环的问题

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

提问人: 提问时间:4/23/2022 更新时间:5/3/2022 访问量:1101

问:

此循环滚动浏览网页上的名称列表,然后单击名称旁边的每个“添加”按钮。它工作良好且正确

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)
 

有些按钮会产生问题,因为如果我单击它们,我会收到一个错误弹出窗口,说我无法将该人添加到朋友中。因此,为了解决此问题,我创建了它以自动单击“确定”并关闭弹出窗口。此代码运行良好且正确,正确单击“确定”按钮并正确关闭弹出窗口。对于读者的信息,如果我不使用“close_popup”,我会收到错误close_popup = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Ok']"))).click()ElementClickInterceptedError enter image description here

问题描述: 问题是当弹出窗口关闭时,因为按钮从“取消请求”变为“添加朋友”,您将再次(无限期)单击后者“添加朋友”,我无法添加。当弹出窗口关闭时,循环将始终单击相同的“添加”按钮(例如 John,给出错误并打开弹出窗口的按钮),而不会继续前进,也永远不会继续单击其他按钮。重要提示:发生这种情况是因为当我单击某个“添加朋友”按钮(例如仅限 John)时,它更改为“取消请求”并打开弹出窗口。因此,当我在弹出窗口中单击“确定”并关闭时,然后“取消请求”按钮将返回“添加朋友”。

因此,脚本将正常继续,并且将单击每个“添加朋友”按钮,包括我无法添加并打开弹出窗口的 John。

enter image description here

因此,我将按以下顺序无限期地获取这些操作:

  1. 点击“添加”按钮
  2. 打开弹出窗口
  3. 关闭弹出窗口,感谢单击“确定”的代码
  4. 再次单击“添加”按钮,因为当我关闭弹出窗口时,该按钮会将名称从“取消请求”更改为“添加”
  5. 从上面 1 到 4 点的无穷大开始

我想跳过单击“添加朋友”按钮,该按钮会阻止循环(在它或其他连续的循环上)并打开弹出窗口。

确切地说,我希望如果弹出窗口打开,那么当弹出窗口关闭并返回到人名列表时,我想继续单击“添加朋友”按钮,但跳过一个按钮(仅 1 个,只有打开弹出窗口并阻止循环的按钮),然后再次恢复单击第一个按钮的所有“添加 Frriend”按钮, 不再跳过(除非弹出窗口出现同样的问题)。While

因此,首先执行第一个 While,然后执行异常的第二个 While(跳过单个按钮),然后返回到第一个 While,继续单击后续按钮(在跳转按钮之后)。第二个 While 的目的是在无法按下“添加朋友”按钮时调用它,因此其目的是跳过“添加朋友”按钮,然后继续单击后续按钮(跳过按钮后)。这是我的想法,但我不确定我是否写了第二个 While 代码

我尝试添加一个 try 和 除了上面的代码。例外是这样的(我接近解决方案,但有问题):

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
            

我曾想过这个解决方案,但我不知道它是否正确。我以为当我得到那个弹出窗口时,它可以暂时开始以 2 的增量跳过该按钮。在此之后,我们关闭 2 和 this while 循环的增量,然后我们返回主 while 循环。也许另一种解决方案是,如果单击同一按钮两次,则必须跳过它

我该如何解决问题?你能建议一个代码答案吗?谢谢

这是完整且可执行的代码(包括 GUI),以防有人想测试和帮助我。我不想出现垃圾邮件问题,因此我创造了选择的可能性,例如向 5 个人发送请求。此脚本仅供我自己的研究目的:

    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()
python-3.x selenium while 循环

评论

0赞 4/24/2022
对于那些阅读的人,有没有人可以帮助我?谢谢
0赞 pcalkins 4/26/2022
最后,Facebook会在您点击足够多的机器人朋友后暂停您的帐户。(去过那里,做到了。您需要使用 findElements 来获取按钮(webelement 引用),并使用另一个数组跟踪您单击了哪些按钮......但同样,您的帐户很快就会被暂停。
0赞 4/26/2022
@pcalkins我知道 Facebook 会暂停我的帐户。事实上,我构建这个脚本是为了学习目的,学习 python、selenium 和一般的自动化。我将通过设置添加几个人并大暂停来做一些测试,以免被 Facebook 暂停。我不想发送垃圾邮件或违反规则。万一Facebook暂停了我,没关系,这不是问题。感谢您的警告:)
0赞 Kevin 5/2/2022
我想知道是否有可能找到属于每个用户的某种唯一 ID。然后,您可以创建一组您已经尝试加好友的用户 ID。然后,每当您遇到“添加朋友”按钮时,您可以检查该设置以查看您之前是否单击过它。这将确保您永远不会两次单击相同的按钮。
0赞 5/2/2022
@Kevin 也许这样太难了。我只需要在关闭弹出窗口后跳过一个按钮。在一些好心的用户回复后,我注意到我可以跳过按钮,但是在我跳过按钮后,按钮点击不会发生。你能在赏金用完之前帮我吗?

答:

0赞 aarondiel 4/29/2022 #1

由于您在抓取信息时正在更改站点,因此您的循环将无法按照您希望的方式工作。

我会重新评论以首先获取您需要的信息,然后通过它进行工作:

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

对于动态加载的内容,这可能不能很好地扩展,但就此目的而言,这已经足够了。

如果你想添加这样的行为,我会看看集合,它确保没有重复的值,并且效率更高。

然后,您可以有一个循环来执行以下操作,直到它找不到任何新值。

  1. 查找所需元素
  2. 对于每个元素:
    1. 测试元素是否在已单击的元素集中
    2. 如果是,则不执行任何操作,如果不是,请单击“元素”并将其添加到集合中
  3. 滚动到页面底部

评论

0赞 4/30/2022
我的问题与其说是异常捕获,不如说是我的问题主要是如果我无法添加一个人,如何跳过他们(以及他们的“添加朋友”按钮)。所以问题出在第二个 While 循环的 +2。我不知道我是否正确创建了它。我需要这方面的帮助,正如我在应用程序图片中所示。你能告诉我跳过一个人的代码吗?(关于我解释的关于按钮中名称更改的问题)
0赞 4/30/2022
你不明白我在问什么。你说的是对的,但这就是问题所在。当我无法将某人添加为朋友时,脚本会卡在我无法添加的同一个人的按钮上,并尝试无休止地添加他们。该脚本单击每个“添加朋友”按钮。当我添加一个人时,按钮会变为“取消请求”。如果我无法添加一个人,在关闭弹出窗口后,按钮会变回“添加朋友”,然后会尝试点击(在另一条评论中继续)——
0赞 4/30/2022
(延续上一条评论)因此,弹出窗口将再次打开,然后关闭,按钮将再次将其名称从“取消请求”更改为“添加朋友”。所以脚本总是阻止同一个人并且不会继续,因为我无法添加它,但它会一次又一次地尝试。出于这个原因,我在异常中创建了一个增量为 +2 的 While 循环,因此只有在关闭弹出窗口后才能跳过一个人。我不确定我写得好不好,你能帮我吗?谢谢
0赞 aarondiel 4/30/2022
我不太明白第二个 while 循环的目的,因为每当无法按下任何“添加朋友”按钮时,就会调用它。也许您打算做的是将 try 块放在第一个 while 循环中。至于如何“跳过一个人”,我认为 for 循环方法可以解决这个问题,因为在遍历它们之前查询所有行,而不是在遍历它们时查询单个行。也可以使用关键字跳到循环的下一次迭代continue
0赞 4/30/2022
第二个 While 的目的是在无法按下“添加朋友”按钮时调用它,因此其目的是跳过“添加朋友”按钮,然后继续单击后续按钮(跳过按钮后)。这是我的想法,但我不确定我是否写好了第二个 While 代码。你能帮我编辑你的答案吗?(尺寸快用完了)。也许你可以给我 2 个版本的解决方案,一个是我要求的第二个 While,另一个是你写给我的那个。但是您的代码给了我一个错误(我在下一条评论中继续,因为没有空格)