0

I'm pretty much brand new to coding in Python, this is my first python code I've written. I'm coding a bot that will click a a web element via the xpath, I'm trying to make it go back to the original web page if the element is not there or if a different element is present Here's a segment of the code

# Wait for the second web element to be clickable
        followbutton = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, "/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/button"))
        )
        followbutton.click()

However, sometimes the follow button has a slightly different xpath, and instead the Xpath is /html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/div/button instead of

"/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/button"

How can I implement an if then statement so that if the xpath is this one:

/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/div/button

then it does driver.back()

but if it's this one: /html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/button then it does followbutton.click()?

I'm looking for any suggestions, Here's what the full segment of code looks like

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
from selenium.common.exceptions import NoSuchElementException
import time

# Create a new instance of the Firefox driver
driver = webdriver.Firefox()

# Set the starting page number
page_num = 32

while True:
    # Navigate to the URL with the current page number
    url = f"https://urlhere.com/page={page_num}"
    driver.get(url)

    time.sleep(20)

    try:
        # Wait for the first web element to be clickable
        user1 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, "/html/body/div[3]/div/div[2]/div/main/div/div[1]/div/div"))
        )
        user1.click()

        # Wait for 10 seconds
        time.sleep(10)

        # Wait for the second web element to be clickable
        followbutton = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, "/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/button"))
        )
        followbutton.click()

        time.sleep(3)
        driver.back()

        # Wait for 5 seconds
        time.sleep(5)
    # 2nd user
        user2 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, "/html/body/div[3]/div/div[2]/div/main/div/div[2]/div/div"))
        )
        user2.click()
        time.sleep(2)
# 2nd users follow button
        followbutton2 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, "/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/button"))
        )
        followbutton2.click()
        driver.back()

    except:
        # If any exception occurs, break out of the loop
        break

    # Increment the page number
    page_num += 1

# Close the browser window
driver.quit()
  • Instead of using absolute XPATH, why don't you use relative XPATH. The latter will me more reliable and most likely will work even if the DOM structure change. – Shawn Mar 22 '23 at 15:45
  • 1
    Check the answers in this link, this may help - https://stackoverflow.com/questions/38022658/selenium-python-handling-no-such-element-exception – Shawn Mar 22 '23 at 15:51
  • Appreciate it, I don't know the difference between absolute XPATH and relative XPATH and don't know how to implement relative over absolute – Drew Hansen Mar 22 '23 at 15:52
  • This is a two parted question. See [here](https://meta.stackoverflow.com/questions/423699/should-two-part-questions-be-closed). Make sure your question fits the criteria of that answer. – Blue Robin Mar 22 '23 at 16:37

2 Answers2

0
  1. First of all. Using such long absoulte paths is not a good idea if you want to build something reliable. You should further investigate if you can identify the element with a unique id or class.
  2. If you really have an element that can be two different xpaths but does the same and can not appear twice. You can make an union in your xpath with the pipe |: For example:
//div/span/div|//div/span/span
  1. If for some reason the elements are not available explore the expected_conditions methods. You have more elements like EC.presence_of_element_located or EC.visibility_of_element_located that may be useful if you also want to check that everything was loaded as expected. Specially if there are a lot of JS animations in the page.
  2. If you really want to go the way of catching exceptions. Do not make a try except of everything. Catch only a specific exception. In this scenario if you are not able to find an element surround the search of the element with something like:
try:
    followbutton = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.XPATH, "/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/button"))
    )
    followbutton.click()
except NoSuchElementException:
    # Handle what you want to happen here.
    ...

In general this is a too broad question. You have to investigate the DOM of the page and act accordingly in your code. There is no magic bullet for this.

nck
  • 1,673
  • 16
  • 40
0

@nck's union solution is great and will definitely work, but it's also fun to look at what Selenium offers for solving problems like this.

Selenium allows you to create custom wait conditions to use with WebDriverWait. You can create a custom condition by creating a class with a __call__ method that accepts the driver as an argument, and returns False when the condition isn't met and something truthy when the condition is met. Because the successful return value just has to be truth-y, we can customize the return type of our class to give us a little extra information:

from selenium.common.exceptions import NoSuchElementException

class has_good_or_bad_element():
    def __init__(self, good_locator, bad_locator):
        self.good_locator = good_locator
        self.bad_locator = bad_locator

    def __call__(self, driver):
        try:
            good_element = driver.find_element(*self.good_locator)
            return 'good', good_element
        except NoSuchElementException:
            pass

        try:
            bad_element = driver.find_element(*self.bad_locator)
            return 'bad', bad_element
        except NoSuchElementException:
            pass

        return False

Now we can call this like so:

element_type, element = WebDriverWait(driver, 10).until(
    has_good_or_bad_element(
        (By.XPATH, "/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/button"), # selector for good element
        (By.XPATH, "/html/body/div[3]/div/div[2]/div/header[1]/div/div[1]/aside[2]/div/div/div[1]/div[1]/div[2]/div/button"), # selector for bad element
    )
)

if element_type == 'good':
    element.click()
else:
    driver.back()

Since you're new to python, there's some syntax here that you might be unfamiliar with. Here's some info on returning multiple values (return 'good', good_element), unpacking tuples (element_type, element = ...), and the __init__ and __call__ class methods.

rpm
  • 1,266
  • 14