Skip to content

Instantly share code, notes, and snippets.

@stachjankowski
Last active July 5, 2023 02:35
Show Gist options
  • Save stachjankowski/455672437b0f1285514f2f7200d28a49 to your computer and use it in GitHub Desktop.
Save stachjankowski/455672437b0f1285514f2f7200d28a49 to your computer and use it in GitHub Desktop.
Get the xpath of an element. This is useful for creating selectors for selenium.
from typing import List
from selenium.common import InvalidSelectorException
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
def get_xpath_for_selenium_element(element: WebElement, use_index: bool = True,
attributes: List[str] = ['id']) -> str:
"""Get the xpath of an element. This is useful for creating selectors for selenium.
:param element: The element to get the xpath of.
:param use_index: Whether to use the index of the element in the xpath.
:param attributes: The attributes to use in the xpath.
:return: The xpath of the element.
"""
xpath_parts: List[str] = []
while element:
# Get the attributes selector of the element
attributes_selector = get_attributes_selector(element, attributes)
tag_name = element.tag_name
if attributes_selector:
# If there are attribute parts, add them to the xpath
xpath_parts.insert(0, f"{tag_name}{attributes_selector}")
elif use_index:
# Find all sibling elements with the same tag name
previous_elements = element.find_elements(By.XPATH, f"preceding-sibling::{tag_name}")
next_elements = element.find_elements(By.XPATH, f"following-sibling::{tag_name}")
index = len(previous_elements) + 1
if previous_elements or next_elements:
xpath_parts.insert(0, f"{tag_name}[{index}]")
else:
xpath_parts.insert(0, f"{tag_name}")
else:
xpath_parts.insert(0, f"{tag_name}")
# Try to find the parent element
try:
element = element.find_element(By.XPATH, "..")
except InvalidSelectorException:
break
return "/" + "/".join(xpath_parts)
def get_attributes_selector(element: WebElement, attributes: List[str]) -> str:
"""Get the attributes selector of an element.
:param element: The element to get the attributes selector of.
:param attributes: The attributes to use in the selector.
:return: The attributes selector of the element.
"""
attributes_parts = [
f"[@{attribute}='{escaped_value}']"
for attribute in attributes
if (value := element.get_attribute(attribute)) and (escaped_value := value.replace("'", "\\'"))
]
return "".join(attributes_parts)
@stachjankowski
Copy link
Author

Example usage:

>>> element = driver.find_element(By.XPATH, "//*[@id='dijit_ProgressBar_2']/img")

>>> get_xpath_for_selenium_element(element)
'/html/body/div[3]/table/tbody/tr/td/div[2]/div[@id='dijit_ProgressBar_2']/img'

>>> get_xpath_for_selenium_element(element, use_index=False)
'/html/body/div/table/tbody/tr/td/div/div[@id='dijit_ProgressBar_2']'

>>> get_xpath_for_selenium_element(element, attributes=['class'])
"/html[@class='dj_webkit dj_chrome dj_contentbox']/body[@class='claro']/div[@class='splashscreen']/table/tbody/tr/td/div/div[@class='dijitProgressBar dijitProgressBarEmpty']/img[@class='dijitProgressBarIndeterminateHighContrastImage']"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment