0

I am very new to web scraping and just learned it today. I am trying to get data about current stocks yet I get a weird error. Any help? Error:

Traceback (most recent call last): File "c:\Users\Heage\Coding\Python\Selenium\WebScraping\Popular TV Shows\main.py", line 17, in company = companies.find_element_by_class_name("tv-data-table__row tv-data-table__stroke tv-screener-table__result-row") AttributeError: 'list' object has no attribute 'find_element_by_class_name'

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

PATH = "C:\Program Files (x86)\chromedriver.exe"
driver = webdriver.Chrome(PATH)
driver.get("https://www.tradingview.com/markets/stocks-usa/market-movers-active/")

try:
    main = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "js-screener-container")))
    companies = main.find_elements_by_class_name("tv-data-table__tbody")
    for company in companies:
        company = companies.find_element_by_class_name("tv-data-table__row tv-data-table__stroke tv-screener-table__result-row")
        for companyInfo in company:
            companyInfo = company.find_element_by_class_name("tv-data-table__cell apply-common-tooltip tv-screener-table__cell tv-screener-table__cell--left tv-screener-table__cell--big tv-screener-table__cell--with-marker")
            for companyInfoMore in companyInfo:
                companyInfoMore = companyInfo.find_element_by_class_name("tv-screener-table__symbol-container ")
                for companyTitle in companyInfoMore:
                    companyTitle = companyInfoMore.find_element_by_class_name("tv-screener__symbol apply-common-tooltip")
                    print(companyTitle)
            for companyChangePercent in companyInfo:
                companyChangePercent = companyInfo.find_element_by_class_name("tv-data-table__cell tv-screener-table__cell tv-screener-table__cell--up tv-screener-table__cell--big tv-screener-table__cell--with-marker")
                print(companyChangePercent)

finally:
    driver.quit()

2 Answers 2

1

The answer by Nikita has an explanation why your code produces this error. But the current implementation is very wrong.

1 You use multiple for loops. You do not need this

2 You use find_element_by_class_name for multiple class names. But this is not supported. You have to use either XPath or css selectors for multiple class names.

3 You are waiting like this: main = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "js-screener-container"))) But it will unlikely wait for the table contents.

4 You are waiting for elements to be present, but it is more reliable to wait till they are visible

Answering your main question: 'list' object has no attribute 'find_element_by_class_name' means that you are calling find_element_by_class_name method from list, which does not have it. You should use it like this:

driver. find_element_by_class_name("some_class")

I've started your code, so it would be easier for you to understand how the code really should 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

driver = webdriver.Chrome(executable_path='/snap/bin/chromium.chromedriver')
driver.get("https://www.tradingview.com/markets/stocks-usa/market-movers-active/")


main = WebDriverWait(driver, 10).until(
    EC.visibility_of_all_elements_located((By.CSS_SELECTOR, ".tv-data-table__tbody .tv-data-table__row.tv-data-table__stroke.tv-screener-table__result-row")))
companies = driver.find_elements_by_css_selector(".tv-data-table__tbody .tv-data-table__row.tv-data-table__stroke.tv-screener-table__result-row")
result = []
for company in companies:
    name_short = company.find_element_by_css_selector(".tv-screener__symbol.apply-common-tooltip").text
    name_long = company.find_element_by_css_selector(".tv-screener__description").text
    last = company.find_element_by_css_selector(".tv-data-table__cell.tv-screener-table__cell.tv-screener-table__cell--big.tv-screener-table__cell--with-marker:nth-of-type(2)>span").text
    result.append([name_short, name_long, last])
for p in result:
    print(p, sep='\n')

First 5 lines of the output:

['GERN', 'GERON CORPORATION', '1.83']
['AMC', 'AMC ENTERTAINMENT HOLDINGS, INC.', '59.26']
['XLF', 'SPDR SELECT SECTOR FUND - FINANCIAL ETF', '35.23']
['WISH', 'CONTEXTLOGIC INC.', '11.40']
['SNDL', 'SUNDIAL GROWERS INC.', '0.9230']

Some hints:

  • Learn locators, specifically XPATH and CSS
  • Learn how to work with lists
  • Go through this tutorial to understand Selenium better https://selenium-python.readthedocs.io/
  • Avoid multiple inner loops as much as you can. Otherwise your programs will be very slow and difficult to read.
Sign up to request clarification or add additional context in comments.

Comments

1
company = companies.find_element_by_class_name("tv-data-table__row tv-data-table__stroke tv-screener-table__result-row")

Have a look at this line. companies(note it is in plural form) is defined from find_elements_by_class_name whose return value is list. Thus, companies is a list and it doesn't have such an attribute.

Guess it can be like this.

company = company.find_element_by_class_name("tv-data-table__row tv-data-table__stroke tv-screener-table__result-row")

This assigns a new element to company. And it has method - find_element_by_class_name

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.