Устаревший элемент — Selenium — Python

Итак, я начну с того, что это превратилось в беспорядок, когда я пытался решить эту проблему, в других случаях мне удавалось решить проблему с устаревшим элементом.

Проблема начинается после того, как статистика первых игроков сохранена (все, что должно быть сделано до этого момента), а затем, когда она возвращается к циклу и находит следующего игрока, у нас возникает проблема.

Я не уверен, вызвано ли это вложенными циклами или чем-то еще. Я пытаюсь восстановить переменную, которая вызывает у меня проблемы, которые я предполагаю во всем коде. player_stats

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

(Мы даже не можем напечатать («Найден элемент playerCol») во второй раз)

Все операторы печати работают до тех пор, пока не зависнут в цикле while после первой итерации.

Вот полный код (с комментариями):

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions importStaleElementReferenceException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import pandas as pd
import re
import time

# Initialize the webdriver
driver = webdriver.Firefox()

# Navigate to the website
url = "https://www.hltv.org/stats/players"
driver.get(url)

WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.ID, "CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll"))).click()

# Find the elements containing the player statistics
player_stats = WebDriverWait(driver, 10).until(
    EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".playerCol, .statsDetail"))
)


# Extract the relevant data from the elements
players = []

for i, player_stat in enumerate(player_stats):
    try:
        WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
        while True:
            player_stats = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
            try:    
                if "playerCol" in player_stat.get_attribute("class"):
                    print("Found playerCol element")
                    name = player_stat.find_element(By.CSS_SELECTOR, "a").text if player_stat.find_elements(By.CSS_SELECTOR, "a") else player_stat.text
                    print(f"Name: {name}")
                elif "statsDetail" in player_stat.get_attribute("class"):
                    stats = player_stat.text.split()
                    if len(stats) >= 1 and re.search(r"\d+\.\d+", stats[0]):
                        kd_ratio = stats[0]
                break
            except StaleElementReferenceException as e:
                player_stats = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
                player_stats = driver.find_elements(By.CSS_SELECTOR, ".playerCol, .statsDetail")
                print(f"An error occurred while processing match stats: {e}")
                break

        # Extract the player stats
        if "statsDetail" in player_stat.get_attribute("class"):
            stats = player_stat.text.split()
            if len(stats) >= 1 and re.search(r"\d+\.\d+", stats[0]):
                kd_ratio = stats[0]

                # Process match stats for the player
                try:
                    time.sleep(1)
                    WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
                    player_link = driver.find_element(By.XPATH, f"//a[contains(text(), '{name}')]")
                    print(player_link.get_attribute('outerHTML'))
                    driver.execute_script("arguments[0].click();", player_link)
                    time.sleep(1)
                    player_stats = driver.find_elements(By.CSS_SELECTOR, ".playerCol, .statsDetail")
                    player = [name, kd_ratio]

                    # Extract additional player stats
                    headshot_percentage = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//span[contains(text(), 'Headshot %')]/following-sibling::span"))).text
                    player.append(headshot_percentage)

                    kpr = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//span[contains(text(), 'Kills / round')]/following-sibling::span"))).text
                    player.append(kpr)

                    dpr = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//span[contains(text(), 'Deaths / round')]/following-sibling::span"))).text
                    player.append(dpr)

                    # Extract match stats for the player
                    matches_link = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "a[href*='/stats/players/matches/'][data-link-tracking-destination='Click on Matches -> Individual -> Overview [subnavigation]']")))
                    driver.execute_script("arguments[0].click();", matches_link)
                    
                    match_stats = WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.group-2, tr.group-1")))
                    match_scores = []
                    num_of_matches = 0
                    rounds_won = 0
                    rounds_played = 0
                    # Process match stats for the player
                    for i, match_stat in enumerate(match_stats):
                        player_name = player[0]
                        player_team = driver.find_element(By.CSS_SELECTOR, ".gtSmartphone-only span:last-of-type").text
                        try:
                            team_name = ""
                            score = ""
                            while team_name == "" or score == "":
                                try:
                                    team = match_stat.find_element(By.CSS_SELECTOR, ".gtSmartphone-only span:last-of-type").text
                                    team_name = team.strip()
                                    
                                    score_span = match_stat.find_element(By.XPATH, ".//div[contains(@class, 'gtSmartphone-only')]//*[contains(text(), '(')]")
                                    score_text = score_span.text.strip()
                                
                                    score = re.search(r'\((\d+)\)', score_text).group(1)
                                    
                                except:
                                    time.sleep(1)
                                    match_stats = WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.group-2, tr.group-1")))
                                    match_stat = match_stats[i]
                            team_data = match_stat.find_elements(By.CSS_SELECTOR, ".gtSmartphone-only span")
                            print("Team data:", team_data[3].text)
                            if team_name.lower() == player_team.lower():
                                player_score = score
                                opposing_team_name = team_data[2].text.strip()
                                print(opposing_team_name)
                                opposing_team_score = team_data[3].text.strip('()')
                                print("Score strip: ", opposing_team_score)
                                rounds_won += int(player_score)
                                rounds_played += int(player_score) + int(opposing_team_score)
                            else:
                                player_score = team_data[1].text.strip('()')
                                print(player_score)
                                opposing_team_score = score
                                print(opposing_team_score)
                                opposing_team_name = team_data[0].text.strip()
                                print(opposing_team_name)
                                rounds_won += int(opposing_team_score)
                                rounds_played += int(player_score) + int(opposing_team_score)

                            match_scores.append((team_name, opposing_team_name, player_score, opposing_team_score))
                            num_of_matches += 1

                            if num_of_matches == 5: # exit loop after 5 iterations
                                break

                        except:
                            # Refresh the page if the element can't be found
                            driver.back()
                            player_stats = driver.find_elements(By.CSS_SELECTOR, ".playerCol, .statsDetail")
                            time.sleep(1)
                            match_stats = WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.group-2, tr.group-1")))

                except Exception as e:
                    print(f"An error occurred while processing data for player {name}: {e}")
                    continue

                players.append([name, kd_ratio, headshot_percentage, kpr, dpr, rounds_won, rounds_played])
                print(players)
                print(f"{player_name}: {rounds_won} rounds won out of {rounds_played} rounds played in {num_of_matches} matches")
                driver.get(url)
                time.sleep(1)
    except StaleElementReferenceException as e:
    # handle the exception here
        print(f"An error occurred while processing match stats: {e}")
        break
# Close the webdriver
driver.quit()
# Store the data in a Pandas dataframe
df = pd.DataFrame(players, columns=["Name", "K/D", "HS %", "KPR", "DPR", "RW", "RP"])

# Clean the data
df["K/D"] = df["K/D"].str.extract(r"(\d+\.\d+)").astype(float)
df["HS %"] = df["HS %"].str.extract(r"(\d+\.\d+)").astype(float)
df["KPR"] = df["KPR"].str.extract(r"(\d+\.\d+)").astype(float)
df["DPR"] = df["DPR"].str.extract(r"(\d+\.\d+)").astype(float)



# Drop any rows that have missing or invalid data
df.dropna(subset=["Name", "K/D", "HS %", "KPR", "DPR"], inplace=True)


# Save the data to a CSV file
df.to_csv("player_stats.csv", index=False, sep='\t')

# Close the webdriver
driver.quit() 

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

sound wave 20.02.2023 10:09

@soundwave верно, но видите ли, раньше он работал только с помощью driver.back() (который я очистил некоторый код и вместо этого использовал driver.get(), но это не имеет значения, и я также повторно устанавливаю переменную, поскольку а также поиск элементов. Который я не уверен, что изменилось. Сейчас лучше с управлением проектом, и подтолкнуть к github каждое исправление.

Blue 20.02.2023 19:05
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Потяните за рычаг выброса энергососущих проектов
Потяните за рычаг выброса энергососущих проектов
На этой неделе моя команда отменила проект, над которым я работал. Неделя усилий пошла насмарку.
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Привет, люди RPA, это снова я и я несу подарки! В очередном моем приключении о том, как создавать ботов для облегчения рутины. Вот, думаю, стоит...
2
2
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

В общем, когда ваш парсинг-проект требует загрузки большого количества веб-страниц (как в этом случае) и/или если код селена медленный, вам следует подумать о том, чтобы попробовать requests или BeautifulSoup. Эти две библиотеки намного быстрее, чем селен, но их недостаток не позволяет загружать javascript и другие вещи. Однако данные, которые вы хотите очистить на hltv.org, не загружены javascript, поэтому мы можем очистить их с помощью requests или BeautifulSoup.

Следующий код очищает данные для первых 10 игроков. Если вы хотите больше, измените значение limit. Пришлось поставить time.sleep(1), чтобы не забанили на сайте.

import re, time, requests, lxml, lxml.html

url = 'https://www.hltv.org/stats/players'
html_home = lxml.html.fromstring(requests.get(url).text)

players = {key:[] for key in ["Name", "K/D", "HS %", "KPR", "DPR", "RW", "RP"]}
limit = 10
players["Name"] = html_home.xpath("//td[contains(@class,'playerCol')]//text()")[:limit]
players["K/D"]  = html_home.xpath("//td[contains(@class,'statsDetail')][3]/text()")[:limit]

for idx,href in enumerate(html.xpath("//td[contains(@class,'playerCol')]/a/@href")[:limit]): # href is for example '/stats/players/11893/zywoo'
    
    time.sleep(1)
    url = 'https://www.hltv.org' + href
    html_player = lxml.html.fromstring(requests.get(url).text)
    players["HS %"] += [html_player.xpath("//span[contains(text(), 'Headshot %')]/following-sibling::span")[0].text]
    players["KPR"]  += [html_player.xpath("//span[contains(text(), 'Kills / round')]/following-sibling::span")[0].text]
    players["DPR"]  += [html_player.xpath("//span[contains(text(), 'Deaths / round')]/following-sibling::span")[0].text]
    
    time.sleep(1)
    url = url.replace('/players/', '/players/matches/') # url is for example https://www.hltv.org/stats/players/matches/11893/zywoo
    html_matches = lxml.html.fromstring(requests.get(url).text)
    match_stats = html_matches.xpath("//tr[contains(@class,'group-1') or contains(@class,'group-2')]")
    player_team = html_matches.xpath("//div[@class='gtSmartphone-only']/a/span/text()")[0]
    num_of_matches = 5
    rounds_won = 0
    rounds_played = 0

    for match_stat in match_stats[:num_of_matches]:
        team_data = match_stat.xpath(".//div[@class='gtSmartphone-only']//span/text()")
        team_name = team_data[0].strip()
        player_score        = re.search(r'\((\d+)\)', team_data[1]).group(1)
        opposing_team_score = re.search(r'\((\d+)\)', team_data[3]).group(1)
        rounds_played += int(player_score) + int(opposing_team_score)
        if team_name.lower() == player_team.lower():
            rounds_won += int(player_score)
        else:
            rounds_won += int(opposing_team_score)
    
    players["RW"] += [rounds_won]
    players["RP"] += [rounds_played]
    
    print(f'{players["Name"][idx]}: {rounds_won} rounds won out of {rounds_played} rounds played in {num_of_matches} matches')

Моему компьютеру потребовалось 36 секунд, чтобы выполнить его с помощью limit = 10. Наконец, запустив pd.DataFrame(players), мы получим

Я начну с того, что приношу свои извинения за то, что получилось так беспорядочно (это произошло из-за попыток исправить эту ошибку несколькими способами), также сначала я использовал только bs. Но я переключил его из-за необходимости переходить на разные страницы (думаю, в этом не было необходимости), и я полагаю, что тогда мне не понадобится селен, если я смогу сделать так много с помощью bs. Итак, вариант использования селена — это просто 100% динамически генерируемый js?

Blue 23.02.2023 01:28

Другие вопросы по теме

Selenium не находит настраиваемый атрибут «data-target-section-id»
Результатом выражения xpath "html/body/div/text()[1]" является: [текст объекта]. Это должна быть ошибка элемента при печати текста элемента с использованием Selenium
После обновления хрома с 108 до 110: WebDriverException: Сообщение: неизвестная ошибка: невозможно обнаружить открытые страницы
Selenium ChromeDriver не выполняет навигацию с --headless в консольном приложении С#
SessionNotCreatedException: Не удалось начать новый сеанс. Код ответа 500 с использованием ChromeDriver на Apache Tomcat/10.0.23 на удаленном сервере
Не удалось запустить Selenium Chrome: произошел сбой при использовании chromedriver в Apache Tomcat/10.0.23 на удаленном сервере
Я получаю тот же результат в цикле for
Не удается нажать кнопку с селеном (тип = кнопка)
Элемент не найден с помощью XPATH
Как получить все результаты с помощью BeautifulSoup с селеном и отключить автоматическую тестовую страницу?