Python : Ошибка индекса: индекс списка вне диапазона после изменения кода

#python #web-scraping #beautifulsoup #index-error

Вопрос:

Мой код должен обеспечивать вывод в приведенном ниже формате.

Я попытался изменить код и сломал его.

 import pandas as pd
from bs4 import BeautifulSoup as bs
from selenium import webdriver
import threading
from multiprocessing.pool import ThreadPool

class Driver:
    def __init__(self):
        options = webdriver.ChromeOptions()
        options.add_argument("--headless")
        # Un-comment next line to supress logging:
        # options.add_experimental_option('excludeSwitches', ['enable-logging'])
        self.driver = webdriver.Chrome(options=options)

    def __del__(self):
        self.driver.quit()  # clean up driver when we are cleaned up
        # print('The driver has been "quitted".')


threadLocal = threading.local()


def create_driver():
    the_driver = getattr(threadLocal, 'the_driver', None)
    if the_driver is None:
        the_driver = Driver()
        setattr(threadLocal, 'the_driver', the_driver)
    return the_driver.driver


class GameData:

    def __init__(self):
        self.date = []
        self.time = []
        self.game = []
        self.score = []
        self.home_odds = []
        self.draw_odds = []
        self.away_odds = []
        self.country = []
        self.league = []


def parse_data(url):
    try:
        browser = create_driver()
        browser.get(url)
        df = pd.read_html(browser.page_source)[0]
    except KeyError:
        print('KeyError')
        return None
    html = browser.page_source
    soup = bs(html, "lxml")
    cont = soup.find('div', {'id': 'wrap'})
    content = cont.find('div', {'id': 'col-content'})
    content = content.find('table', {'class': 'table-main'}, {'id': 'tournamentTable'})
    main = content.find('th', {'class': 'first2 tl'})
    if main is None:
        return None
    count = main.findAll('a')
    country = count[1].text
    league = count[2].text
    game_data = GameData()
    game_date = None
    for row in df.itertuples():
        if not isinstance(row[1], str):
            continue
        elif ':' not in row[1]:
            game_date = row[1].split('-')[0]
            continue
        game_data.date.append(game_date)
        game_data.time.append(row[1])
        game_data.game.append(row[2])
        game_data.score.append(row[3])
        game_data.home_odds.append(row[4])
        game_data.draw_odds.append(row[5])
        game_data.away_odds.append(row[6])
        game_data.country.append(country)
        game_data.league.append(league)
    return game_data


# URLs go here
urls = {

    "https://www.oddsportal.com/matches/soccer/20210903/",

}

if __name__ == '__main__':
    results = None
    # To limit the number of browsers we will use
    # (set to a large number if you don't want a limit):
    MAX_BROWSERS = 5
    pool = ThreadPool(min(MAX_BROWSERS, len(urls)))
    for game_data in pool.imap(parse_data, urls):
        if game_data is None:
            continue
        result = pd.DataFrame(game_data.__dict__)
        if results is None:
            results = result
        else:
            results = results.append(result, ignore_index=True)

    print(results)
    # ensure all the drivers are "quitted":
    del threadLocal
    import gc

    gc.collect()  # a little extra insurance

print(results.head())
 

Я получил эту ошибку:

 Traceback (most recent call last):
  File "C:UsersharshAppDataRoamingJetBrainsPyCharmCE2021.2scratchesscratch_13.py", line 107, in <module>
    for game_data in pool.imap(parse_data, urls):
  File "C:Program FilesPython39libmultiprocessingpool.py", line 870, in next
    raise value
  File "C:Program FilesPython39libmultiprocessingpool.py", line 125, in worker
    result = (True, func(*args, **kwds))
  File "C:UsersharshAppDataRoamingJetBrainsPyCharmCE2021.2scratchesscratch_13.py", line 72, in parse_data
    league = count[2].text
IndexError: list index out of range
 

Результаты обычно представлены в следующем формате:

      date   time                          game score home_odds draw_odds away_odds   country          league
    0          None  15:30       Wolves - Manchester Utd   0:1   393/100     69/25     39/50   England  Premier League
    1          None  13:00               Burnley - Leeds   1:1   231/100     64/25   123/100   England  Premier League
    2          None  13:00           Tottenham - Watford   1:0     23/50     87/25   709/100   England  Premier League
    3   28 Aug 2021  16:30           Liverpool - Chelsea   1:1     29/20     59/25   207/100   England  Premier League
    4   28 Aug 2021  14:00       Aston Villa - Brentford   1:1   109/100     58/25     74/25   England  Premier League
    5   28 Aug 2021  14:00            Brighton - Everton   0:2     33/25    113/50   239/100   England  Premier League
    6   28 Aug 2
021  14:00       Newcastle - Southampton   2:2     73/50   257/100   189/100   England  Premier League
 

Как я могу получить эти данные?

многословный:

У меня есть код, который циклически запускает URL-адрес для следующих совпадений, и я хочу его изменить . Xpath для следующих совпадений для oddsportal: //*[@id=»col-контент»]/div[3]/div/div/охватывает это изображение. Я хочу, чтобы этот код прошел через все эти страницы и получил значение объединенного кадра данных

Пожалуйста, помогите

Комментарии:

1. @booboo Я пытаюсь подражать поведению этого кода для вашего решения, которое было так полезно.

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

3. Согласен, однако, это другое решение/проблема

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

Ответ №1:

Единственная дата, которую я вижу, находится вверху страницы с подписью «Следующие футбольные матчи:».

Я не вижу никакого смысла в вашем первоначальном создании df = pd.read_html(browser.page_source)[0] фрейма данных и последующей итерации этого фрейма данных; вы должны просто повторять непосредственно теги основной таблицы. Если все сделано правильно, вы получите правильные значения в столбцах «Страна» и «лига».

Я также изменил несколько имен переменных, чтобы более точно отразить, какое значение они имеют. Кроме того, я немного упростил вашу навигацию по иерархии HTML, признав, что элемент с атрибутом id должен быть уникальным в документе, чтобы вы могли напрямую извлечь его по этому идентификатору и не нужно было сначала извлекать его родителя.

 import pandas as pd
from bs4 import BeautifulSoup as bs
from selenium import webdriver
import threading
from multiprocessing.pool import ThreadPool
import re

class Driver:
    def __init__(self):
        options = webdriver.ChromeOptions()
        options.add_argument("--headless")
        # Un-comment next line to supress logging:
        options.add_experimental_option('excludeSwitches', ['enable-logging'])
        self.driver = webdriver.Chrome(options=options)

    def __del__(self):
        self.driver.quit()  # clean up driver when we are cleaned up
        # print('The driver has been "quitted".')


threadLocal = threading.local()


def create_driver():
    the_driver = getattr(threadLocal, 'the_driver', None)
    if the_driver is None:
        the_driver = Driver()
        setattr(threadLocal, 'the_driver', the_driver)
    return the_driver.driver


class GameData:

    def __init__(self):
        self.date = []
        self.time = []
        self.game = []
        self.score = []
        self.home_odds = []
        self.draw_odds = []
        self.away_odds = []
        self.country = []
        self.league = []

def generate_matches(table):
    tr_tags = table.findAll('tr')
    for tr_tag in tr_tags:
        if 'class' not in tr_tag.attrs:
            continue
        tr_class = tr_tag['class']
        if 'dark' in tr_class:
            th_tag = tr_tag.find('th', {'class': 'first2 tl'})
            a_tags = th_tag.findAll('a')
            country = a_tags[0].text
            league = a_tags[1].text
        elif 'deactivate' in tr_class:
            td_tags = tr_tag.findAll('td')
            yield td_tags[0].text, td_tags[1].text, td_tags[2].text, td_tags[3].text, 
            td_tags[4].text, td_tags[5].text, country, league

def parse_data(url):
    browser = create_driver()
    browser.get(url)
    soup = bs(browser.page_source, "lxml")
    div = soup.find('div', {'id': 'col-content'})
    table = div.find('table', {'class': 'table-main'})
    h1 = soup.find('h1').text
    m = re.search(r'd  w  d{4}

С принтами:

             date   time                                         game score home_odds draw_odds away_odds     country            league
0    03 Sep 2021  00:00                      Petrolera - Dep. Pasto    2:3      -128       245       334    Colombia     Copa Colombia
1    03 Sep 2021  00:00                       Jalapa - Export Sebaco   0:2      -137       266       307   Nicaragua      Liga Primera
2    03 Sep 2021  00:00                        Venezuela - Argentina   1:3       799       376      -270       World    World Cup 2022
3    03 Sep 2021  00:05                            Canada - Honduras   1:1      -196       290       597       World    World Cup 2022
4    03 Sep 2021  01:00                               Peru - Uruguay   1:1       231       204       140       World    World Cup 2022
..           ...    ...                                          ...   ...       ...       ...       ...         ...               ...
219  03 Sep 2021  23:00                   Greenville - Toronto FC II   3:0      -147       263       363         USA    USL League One
220  03 Sep 2021  23:30                 Nashville SC - New York City   3:1       166       235       166         USA               MLS
221  03 Sep 2021  23:30  Philadelphia Union - New England Revolution   0:1       164       256       154         USA               MLS
222  03 Sep 2021  23:30                   Louisville City - FC Tulsa   0:1      -233       394       459         USA  USL Championship
223  03 Sep 2021  23:30                    Tampa Bay - Oakland Roots   3:0      -227       320       573         USA  USL Championship

[224 rows x 9 columns]
 


Комментарии:

1. Как бы я заставил столбец даты отражать дату события?

2. Спасибо! Однако это не совсем то конечное решение, которое я ищу. У меня есть этот код, который циклически повторяет все следующие совпадения, перечисленные на целевой странице , и предоставляет вывод, аналогичный вашей дате вывода, разрешенной в этом формате фрейма данных. Если вы можете помочь мне добраться туда. Я сделаю все, что должен. Большое вам спасибо!). Если вы можете помочь мне добраться туда. Я сделаю все, что должен. Большое вам спасибо!

3. Это мое полное объяснение

4. Я обновил ответ. Кстати, ваши ссылки на pastebin.com ничего не показывайте.

5. (Stackoverflow имеет ограничение на количество символов и, следовательно, эту платформу.) [У меня есть этот код ]( pastebin.com/73sXCQRq ) в качестве моего конечного продукта [Он очищает этот фрейм данных ]( gofile.io/d/arEqxI ) Ваш код безголовый, и я хотел бы его использовать. Однако я не программист. Я могу зайти только так далеко, лол. Итак, я бы очень хотел, чтобы ваш код очистил этот фрейм данных. Это возможно, потому что страницы(ы) (являются) одинаковыми. ( imgur.com/a/GUI5m1G ) Пожалуйста, дайте мне знать, как я могу вам помочь. -Свалка Пастебина

, h1)
game_date = m[0]
game_data = GameData()
for row in generate_matches(table):
game_data.date.append(game_date)
game_data.time.append(row[0])
game_data.game.append(row[1])
game_data.score.append(row[2])
game_data.home_odds.append(row[3])
game_data.draw_odds.append(row[4])
game_data.away_odds.append(row[5])
game_data.country.append(row[6])
game_data.league.append(row[7])
return game_data

# URLs go here
urls = {

"https://www.oddsportal.com/matches/soccer/20210903/",

}

if __name__ == '__main__':
results = None
# To limit the number of browsers we will use
# (set to a large number if you don't want a limit):
MAX_BROWSERS = 5
pool = ThreadPool(min(MAX_BROWSERS, len(urls)))
for game_data in pool.imap(parse_data, urls):
result = pd.DataFrame(game_data.__dict__)
if results is None:
results = result
else:
results = results.append(result, ignore_index=True)

print(results)
#print(results.head())
# ensure all the drivers are "quitted":
del threadLocal
import gc
gc.collect() # a little extra insurance
С принтами:


Комментарии:

1. Как бы я заставил столбец даты отражать дату события?

2. Спасибо! Однако это не совсем то конечное решение, которое я ищу. У меня есть этот код, который циклически повторяет все следующие совпадения, перечисленные на целевой странице , и предоставляет вывод, аналогичный вашей дате вывода, разрешенной в этом формате фрейма данных. Если вы можете помочь мне добраться туда. Я сделаю все, что должен. Большое вам спасибо!). Если вы можете помочь мне добраться туда. Я сделаю все, что должен. Большое вам спасибо!

3. Это мое полное объяснение

4. Я обновил ответ. Кстати, ваши ссылки на pastebin.com ничего не показывайте.

5. (Stackoverflow имеет ограничение на количество символов и, следовательно, эту платформу.) [У меня есть этот код ]( pastebin.com/73sXCQRq ) в качестве моего конечного продукта [Он очищает этот фрейм данных ]( gofile.io/d/arEqxI ) Ваш код безголовый, и я хотел бы его использовать. Однако я не программист. Я могу зайти только так далеко, лол. Итак, я бы очень хотел, чтобы ваш код очистил этот фрейм данных. Это возможно, потому что страницы(ы) (являются) одинаковыми. ( imgur.com/a/GUI5m1G ) Пожалуйста, дайте мне знать, как я могу вам помочь. -Свалка Пастебина