0

I'm trying to click on the following element with the class name equals "clean right":

<li class="clean right"></li>

How could I locate it by using driver.find_element_by_class_name()

DebanjanB
  • 118,661
  • 30
  • 168
  • 217

2 Answers2

4

You can't pass multiple classnames as argument through find_element_by_class_name() and doing so you will face an error as:

invalid selector: Compound class names not permitted

There are multiple approaches to solve this usecase and you can use either of the following Locator Strategies:

  • If the element is uniquely identified only through the classname clean you can use:

    driver.find_element_by_class_name("clean")
    
  • If the element is uniquely identified only through the classname right you can use:

    driver.find_element_by_class_name("right")
    
  • If both the classnames, clean and right are mandatory to identify the element, you can use as follows:

    driver.find_element_by_css_selector("li.clean.right")
    
  • As an alternative you can also use as follows:

    driver.find_element_by_xpath("//li[@class='clean right']")
    

tl; dr

Invalid selector: Compound class names not permitted error using Selenium


Reference

Find div element by multiple class names?

DebanjanB
  • 118,661
  • 30
  • 168
  • 217
0

The previous answer is partially incorrect. please check the source code at :

https://github.com/SeleniumHQ/selenium/blob/9160de55af9cc230f758f4ce6a2af8d1570f0614/py/selenium/webdriver/remote/webdriver.py

You can use class_name for multiple classes , just need to replace space with '.'

Example for using class with space:

from selenium import webdriver
from time import sleep

options = webdriver.ChromeOptions()
#options.headless = True
options.add_argument("--window-size=1920,1080")
options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument(
    "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
browser = webdriver.Chrome(options=options)
browser.get("https://www.instagram.com")
sleep(5)
#browser.refresh()
elem=browser.find_element_by_class_name('RP4i1.UVauz')
print(elem.get_attribute("outerHTML"))
browser.get_screenshot_as_file(f"screenshot.png")

Output:

<img class="RP4i1  UVauz" src="/static/images/homepage/screenshot1.jpg/d6bf0c928b5a.jpg" alt="">

If you check the exception from the by_class_name:

enter image description here

You can see that it is using css_class locator under the hood ( You can see it add . in frontautomatically)

Another Working example:

from selenium import webdriver

import time

from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://stackoverflow.com/questions/65579491/find-element-by-class-name-in-selenium-giving-error/65579606?noredirect=1#comment115946541_65579606")
time.sleep(5)
elem = driver.find_element_by_class_name('overflow-x-auto.ml-auto.-secondary.grid.ai-center.list-reset.h100')

print(elem.get_attribute("outerHTML"))
PDHide
  • 10,919
  • 2
  • 12
  • 26
  • I'm forced to downvote this answer as this answer is conceptually wrong. You are completely messing up the concept of locators. – DebanjanB Jan 05 '21 at 13:43
  • @DebanjanB could you explain why ? – PDHide Jan 05 '21 at 13:44
  • @DebanjanB in what level do you think the answer is wrong ? – PDHide Jan 05 '21 at 13:45
  • THe answer provides a working example also – PDHide Jan 05 '21 at 13:45
  • by_class_name just adds '.' infront of the locator provided so 'a' will be passed as .a and a.b will be passed as '.a.b' – PDHide Jan 05 '21 at 13:47
  • That is the equalent of finding multiple class by css locator – PDHide Jan 05 '21 at 13:47
  • _could you explain why_: The top most discussion from Frequently viewed question of Selenium will give you a crystal clear picture what happens under the hood. However that discussion is also linked within my answer for this discussion. Let me know if you still have any doubts. – DebanjanB Jan 05 '21 at 13:52
  • @DebanjanB You are referrring to your own answers which are incorrect , – PDHide Jan 05 '21 at 13:53
  • COuld you mention why do you think its not conceptually correct ? – PDHide Jan 05 '21 at 13:54
  • The exception it self mention that it uses css selector @DebanjanB and it shows it uses css class selector .class – PDHide Jan 05 '21 at 13:55
  • @DebanjanB your answer is correct for java – PDHide Jan 05 '21 at 14:08
  • The WebDriver specifications are identically implemented through Selenium's _java_ and _python_ client. If you observe any discrepancies between their implementation feel free to raise a bug in the SeleniumHQ queue. I will be happy to look into that. – DebanjanB Jan 05 '21 at 14:13
  • in java it is adding .overflow\-x\-auto\.ml\-auto a front slash to the '.' – PDHide Jan 05 '21 at 14:14
  • What is stopping you from referring to the client code _java_ or _python_? – DebanjanB Jan 05 '21 at 14:19
  • @DebanjanB elif by == By.CLASS_NAME: by = By.CSS_SELECTOR value = ".%s" % value this is python code in hood just replacing with .%s and css locator – PDHide Jan 05 '21 at 14:53
  • https://github.com/SeleniumHQ/selenium/blob/9160de55af9cc230f758f4ce6a2af8d1570f0614/py/selenium/webdriver/remote/webdriver.py – PDHide Jan 05 '21 at 14:55
  • You are looking at the right place but your interpretation is wrong. In short it's **`By.CLASS_NAME`** not `By.CLASS_NAMES` – DebanjanB Jan 05 '21 at 15:01
  • @DebanjanB where did we mention CLASS_NAMES ? can we use the method for finding multiple class "Answer is yes" – PDHide Jan 05 '21 at 15:04
  • Your answer says You can't pass multiple classnames as argument through find_element_by_class_name() and doing so you will face an error as: whioch is incorrect . Could you revert the downvote – PDHide Jan 05 '21 at 15:05