Ошибка Селена при очистке веб-страниц: «с ошибкой атрибута: объект «список» не имеет атрибута «find_element_by_class_name»»

#python #selenium #selenium-webdriver #web-scraping #selenium-chromedriver

Вопрос:

Я очень новичок в веб-скребке и только сегодня узнал об этом. Я пытаюсь получить данные о текущих акциях, но получаю странную ошибку. Какая-нибудь помощь? Ошибка:

Обратная связь (последний последний вызов): Файл «c:UsersHeageCodingPythonSeleniumWebScrapingPopular ТЕЛЕВИЗОР Showsmain.py», строка 17, в company = companies.find_element_by_class_name(«тв-таблица данных__строка тв-таблица данных__строка ТВ-экран__строка результатов») Ошибка атрибута: объект «список» не имеет атрибута «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()
 

Ответ №1:

В ответе Никиты есть объяснение, почему ваш код выдает эту ошибку. Но нынешняя реализация очень неверна.

1 Вы используете несколько циклов for. Вам это не нужно

2 Вы используете find_element_by_class_name для нескольких имен классов. Но это не поддерживается. Вы должны использовать XPath css селекторы или или для нескольких имен классов.

3 Вы ждете вот так: main = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "js-screener-container"))) Но вряд ли дождетесь содержимого таблицы.

4 Вы ждете, когда появятся элементы, но надежнее подождать, пока они не станут видны

Отвечая на ваш главный вопрос: 'list' object has no attribute 'find_element_by_class_name' означает, что вы вызываете find_element_by_class_name метод из списка, в котором его нет. Вы должны использовать его вот так:

 driver. find_element_by_class_name("some_class")
 

Я запустил ваш код, чтобы вам было легче понять, как на самом деле должен выглядеть код.

 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')
 

Первые 5 строк вывода:

 ['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']
 

Некоторые намеки:

  • Изучите локаторы, в частности XPATH и CSS
  • Узнайте, как работать со списками
  • Пройдите этот урок, чтобы лучше понять Селен https://selenium-python.readthedocs.io/
  • Избегайте нескольких внутренних петель, насколько это возможно. В противном случае ваши программы будут очень медленными и трудными для чтения.

Ответ №2:

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

Взгляните на эту строку. компании(обратите внимание, что это во множественном числе) определяется из find_element s_by_class_name, возвращаемое значение которого-список. Таким образом, компании-это список, и у него нет такого атрибута.

Думаю, это может быть так.

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

Это присваивает компании новый элемент. И у него есть метод — find_element_by_class_name