Заказчик - футбольная федерация
Цель проекта - собрать информацию об игроках, принимавших участие в чемпионатах России по футболу в сезоне 2020/21
Задачи проекта:
Исходные данные - ссылки на страницы турниров на сайте Championat.com.
# Импортируем библиотеки
import pandas as pd
import requests
from bs4 import BeautifulSoup
import json
import re
import warnings
# Настраиваем параметры отображения
warnings.filterwarnings("ignore")
pd.options.display.float_format = '{:,.2f}'.format
pd.options.display.max_columns = 100
# Пишем заголовок с данными агента, чтобы сайт не блокировал обращения
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.3'}
# Создаём массив со ссылками на турниры
tournaments_array = ['https://www.championat.com/football/_russiapl/tournament/3953/teams/',
'https://www.championat.com/football/_russiapl/tournament/4103/teams/',
'https://www.championat.com/football/_russia1d/tournament/3959/teams/',
'https://www.championat.com/football/_russia2d/tournament/4613/teams/',
'https://www.championat.com/football/_russia2d/tournament/4617/teams/',
'https://www.championat.com/football/_russia2d/tournament/4619/teams/',
'https://www.championat.com/football/_russia2d/tournament/4615/teams/'
]
# Создаём пустой датафрейм для записи команд
teams_summary = pd.DataFrame()
# Запускаем цикл, перебирающий все команды в списке турниров и записывающий их в общий датафрейм
for t in tournaments_array:
req = requests.get(t, headers=headers)
soup = BeautifulSoup(req.text, 'lxml')
# Создаём массив с ссылками на страницы команд (class - teams-item__link)
teams_link = []
for row1 in soup.find_all(
'a', attrs={'class': 'teams-item__link'}
):
teams_link.append(row1['href'])
# Создаём массив с названиями команд (class - teams-item__name)
teams_array = []
for row2 in soup.find_all(
'div', attrs={'class': 'teams-item__name'}
):
teams_array.append(row2.text)
# Создаём датафрейм из массивов ссылок и команд, добавляем поле с ссылкой на турнир
df_teams = pd.DataFrame({'team_link': teams_link, 'tournament': t, 'team_name': teams_array})
# Записываем в датафрейм информацию из датафрейма, получаемую на каждой итерации цикла
teams_summary = teams_summary.append(df_teams)
# Заменяем относительные ссылки на абсолютные
teams_summary['team_link'] = 'https://www.championat.com' + teams_summary['team_link']
# Сбрасываем индексы
teams_summary = teams_summary.reset_index()
# Функция для сбора гражданства и даты рождения игрока
def parse_table(table):
# Создаём временный датафрейм, в который записываем данные игроков
players_tmp_df = pd.DataFrame()
# Перебираем в цикле все строки таблицы с игроками на странице состава команды
for row in table.find_all('tr'):
# Создаём условие для исключения строки с заголовками таблицы
if not row.find_all('th'):
# Находим поле, где записано гражданство игрока (class - table-item__flag)
country = row.find_all('span', {'class': 'table-item__flag'})
country = str(country)
# Вырезаем название страны из кода
country = country.replace('[<span class="table-item__flag">', '').strip()
country = country.replace('<img alt="', '').strip()
country = (country.split('\"')[0]).strip()
# Находим поле, где записана дата рождения игрока (class - table-responsive__row-item _order_4 _desktop)
birthday = row.find('td', {'class': 'table-responsive__row-item _order_4 _desktop'})
birthday = str(birthday)
# Вырезаем дату рождения из кода
birthday = birthday.replace('<td class="table-responsive__row-item _order_4 _desktop" data-label="ДР">', '').strip()
birthday = birthday.replace('</td>', '').strip()
# Находим поле с ссылкой на страницу игрока
link = row.find('a').get('href')
# Формируем датафрейм с ссылкой, страной и датой рождения
players_tmp_df = players_tmp_df.append(pd.DataFrame([[link, country, birthday]], columns = ['link','country','birthday']), ignore_index=True)
return players_tmp_df
# Создаём датафрейм, в который записываются гражданство, дата рождения и ссылка на страницу игрока
players_info = pd.DataFrame()
# Запускаем цикл, проходящий все команды в имеющимся датафрейме с командами
for i in teams_summary.index:
# Заменяем ссылку со страницы результатов команды на страницу состава
URL = teams_summary['team_link'][i].replace('/result/', '/players/')
req = requests.get(URL, headers=headers)
soup = BeautifulSoup(req.text, 'lxml')
# Находим таблицу, в которой построчно представлен состав команды
tables = soup.find_all('table', {'class': 'table table-responsive table-row-hover table-stripe js-table-responsive'})
# Запускаем цикл, чтобы учитывались ушедшие из команды игроки, которые на сайте даны отдельной таблицей
for item in tables:
players_tmp_df = parse_table(item)
players_info = players_info.append(players_tmp_df, ignore_index=True)
# Сбрасываем индексы
players_info = players_info.reset_index()
del players_info['index']
# Заменяем относительные ссылки на абсолютные
players_info['link'] = 'https://www.championat.com' + players_info['link']
# Создаём пустой датафрейм для формирования информации об игроках
players_stat = pd.DataFrame()
# Запускаем цикл, проходящий все команды в имеющемся датафрейме
for i in teams_summary.index:
# Заменяем ссылку со страницы результатов команды на страницу статистики игроков
URL = teams_summary['team_link'][i].replace('/result/', '/pstat/')
req = requests.get(URL, headers=headers)
soup = BeautifulSoup(req.text, 'lxml')
# Находим на странице уже готовый JSON с данными всех игроков команды
team_json = soup.find_all('script', attrs={'class': 'js-table-data-json'})
team_json = str(team_json)
# Создаём исключение, т.к. у команды «Смоленск» почему-то не считываются корректно данные
try:
# Обрезаем данные до необходимого JSON
reprint = re.search('\[{[\s\S]*\}]', team_json).group()
except:
continue
# Загружаем данные из JSON
response_parsed = json.loads(reprint)
# Формируем временный датафрейм на основе загруженных данных
stat_tmp_df = pd.DataFrame.from_dict(response_parsed, orient='columns')
# Оставляем в датафрейме только нужные поля
stat_tmp_df = stat_tmp_df[['name', 'link', 'total_game', 'total_min']]
# Добавляем в датафрейм ссылки и название команд и турнира
stat_tmp_df['team_link'] = teams_summary['team_link'][i]
stat_tmp_df['team_name'] = teams_summary['team_name'][i]
stat_tmp_df['tournament'] = teams_summary['tournament'][i]
# Записываем в датафрейм информацию из датафрейма, получаемую на каждой итерации цикла
players_stat = players_stat.append(stat_tmp_df)
# Сбрасываем индексы
players_stat = players_stat.reset_index()
del players_stat['index']
# Заменяем относительные ссылки на абсолютные
players_stat['link'] = 'https://www.championat.com' + players_stat['link']
# Объединяем таблицы (датафреймы) со статистикой игроков и данными игроков
players_summary = pd.merge(players_stat, players_info, 'left', on = 'link')
# Удаляем дубликаты
players_summary = players_summary.drop_duplicates().reset_index(drop=True)
# Заменяем пустые значения в игровых минутах на 0 и приводим поле к целочисленному типу
players_summary['total_min'] = players_summary['total_min'].fillna(value=0).astype('int')
# Меняем порядок полей в соответствии с ТЗ
players_summary = players_summary[['team_name', 'name', 'country', 'birthday', 'total_game', 'total_min', 'link']]
# Отображаем первые и последние строки итогового датафрейма
display(players_summary)
team_name | name | country | birthday | total_game | total_min | link | |
---|---|---|---|---|---|---|---|
0 | Арсенал | Гурам Аджоев | Россия | 27.02.1995 | 5 | 24 | https://www.championat.com/football/_russiapl/... |
1 | Арсенал | Ламек Банда | Замбия | 29.01.2001 | 2 | 36 | https://www.championat.com/football/_russiapl/... |
2 | Арсенал | Роберт Бауэр | Германия | 09.04.1995 | 22 | 1971 | https://www.championat.com/football/_russiapl/... |
3 | Арсенал | Максим Беляев | Россия | 30.09.1991 | 16 | 1380 | https://www.championat.com/football/_russiapl/... |
4 | Арсенал | Тарас Бурлак | Россия | 22.02.1990 | 15 | 1147 | https://www.championat.com/football/_russiapl/... |
... | ... | ... | ... | ... | ... | ... | ... |
3723 | Челябинск | Алексей Пипо | Россия | 14.09.2000 | 4 | 247 | https://www.championat.com/football/_russia2d/... |
3724 | Челябинск | Максим Руднев | Россия | 20.04.1997 | 3 | 82 | https://www.championat.com/football/_russia2d/... |
3725 | Челябинск | Илья Сальников | Россия | 05.05.2000 | 4 | 360 | https://www.championat.com/football/_russia2d/... |
3726 | Челябинск | Кирилл Сараев | Россия | 21.05.1997 | 4 | 332 | https://www.championat.com/football/_russia2d/... |
3727 | Челябинск | Марат Шайморданов | Россия | 14.04.1992 | 4 | 263 | https://www.championat.com/football/_russia2d/... |
3728 rows × 7 columns
# Сохраняем данные в CSV-файл
players_summary.to_csv('players_summary.csv', sep=',', encoding='utf-8')