Анализ данных таблицы с использованием Selenium с Python в обобщенном виде - PullRequest
1 голос
/ 03 июля 2019

Я пытаюсь почистить этот веб-сайт: https://fmdataba.com/19/p/621/toni-kroos/, который содержит статистику некоторых игр футболиста, использующего селен.

driver = webdriver.Chrome('chromedriver.exe')
driver.implicitly_wait(3)
driver.get('https://fmdataba.com/19/p/621/toni-kroos/')
# wait page to load
sleep(1)

data = driver.find_element_by_class_name('panel-body')
print(data.text)

Благодаря этому я смог получить некоторую полезную информацию, выполнив data.text.split('\n') и распечатав их.

for i, text in enumerate(data.text.split('\n')):
    print(i, text)

Это дает мне,

...more...
34 Value € 69.0M
35 Wage € 20,000,000 p/a
36 Status First Team
37 Contrat 30/6/2022
38 Pre. Foot Either
39 Position DM, M (C)
40 Best Alternatives
41 * Players with similar attributes order by value, each attributes (3-) / (3+)
42 TECHNICAL
43 Corners 18
44 Crossing 18
45 Dribbling 14
46 Finishing 13
47 First Touch 18
48 Free Kick 14
49 Heading 7
50 Long Shots 17
51 Long Throws 8
52 Marking 8
53 Passing 20
54 Penalty Taking 13
55 Tackling 9
56 Technique 16
...more...

Затем я сделал что-то вроде ниже, чтобы проанализировать информацию, которая мне нужна

# 20: Age
bdate = player_info[20]

# 28: Nation
nation = player_info[28]

# 37: Foot
foot = player_info[37]

# 51 - 64: Tech

technical = {}
for stat in player_info[51:65]:
    item = stat.split(' ')
    if len(item) == 2:
        ability, rate = item[0], item[1]
    if len(item) == 3:
        ability, rate = '{} {}'.format(item[0], item[1]), item[2]
    technical[ability] = int(rate)

и, наконец, сделал что-то вроде

player_obj = {
    'profile_img': img_url,
    'name': name,
    'birth_date': bdate,
    'nation': nation,
    'position': pos,
    'foot': foot,
    'abilities': abilities
}

чтобы завершить объект, который мне нужен.

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

Как я могу сделать это более обобщенным?

Последний объект, который я хочу для каждого игрока, выглядит следующим образом:

{
    "profile_img": "https://fmdataba.com/images/p/3771.png",
    "name": "Eden Hazard",
    "birth_date": "7/1/1991",
    "nation": "Belgium",
    "position": "AM (RLC)",
    "foot": "Either",
    "abilities": {
      "technical": {
        "Corners": 12,
        "Crossing": 12,
        "Dribbling": 20,
        "Finishing": 14,
        "First Touch": 17,
        "Free Kick": 13,
        "Heading": 7,
        "Long Shots": 11,
        "Long Throws": 5,
        "Marking": 3,
        "Passing": 15,
        "Penalty Taking": 19,
        "Tackling": 4,
        "Technique": 18
      },
      "mental": {
        "Aggression": 8,
        "Anticipation": 12,
        "Bravery": 17,
        "Composure": 15,
        "Concentration": 13,
        "Decisions": 16,
        "Determination": 15,
        "Flair": 18,
        "Leadership": 6,
        "Off The Ball": 14,
        "Positioning": 7,
        "Teamwork": 9,
        "Vision": 16,
        "Work Rate": 12
      },
      "physical": {
        "Acceleration": 17,
        "Agility": 20,
        "Balance": 16,
        "Jumping Reach": 8,
        "Natural Fitness": 16,
        "Pace": 16,
        "Stamina": 17,
        "Strength": 11
      }
    }
  }

Заранее спасибо!

1 Ответ

0 голосов
/ 03 июля 2019

Редактировать: Просто понял, что вы не используете BeautifulSoup (или любой другой html-парсер). При очистке веб-страниц использование манипуляции со строками просто вызывает проблемы, когда вы можете просто проанализировать HTML и использовать его. Проверьте это: https://www.crummy.com/software/BeautifulSoup/bs4/doc/. Попробуйте включить его в свой рабочий процесс. Это вам очень поможет.


Для очистки таблицы найдите все строки <tr>, а для всех строк найдите все ячейки <td> и поместите их в список, как показано ниже.

def scrape_table(table: Tag) -> list:
    rows = []
    for row in table.find_all('tr'):
        cells = [cell.text.strip() for cell in row.find_all('td')]
        rows.append(cells)
    return rows

Несколько замечаний:

  1. Вам не нужно показывать большие орудия, такие как Selenium, когда достаточно requests с несколькими заголовками. Большинство веб-сайтов устанавливают базовый барьер, который блокирует запросы без заголовка User-Agent. Здесь добавление, которое позволяет нам просто очистить страницу. Отсутствие необходимости запуска браузера значительно ускоряет процесс.

  2. Если у вас есть список [key, value] пар, вы можете использовать функцию dict, чтобы упаковать их в словарь. Это работает для этой страницы, потому что все строки таблицы имеют только имя статистики и номер.

Здесь я намеренно продублировал некоторый код, но вы можете легко преобразовать поиск таблицы по заголовку в функцию find_table_by_title, например.

import requests
from bs4 import BeautifulSoup, Tag

def scrape_table(table: Tag) -> list:
    rows = []
    for row in table.find_all('tr'):
        cells = [cell.text.strip() for cell in row.find_all('td')]
        rows.append(cells)
    return rows


def scrape_technical(soup: BeautifulSoup) -> dict:
    # find table by column title
    col_title_el = soup.find('h3', text='TECHNICAL')

    # go up the parents until we find one that
    # contains both column title and the table, but separate for all columns.
    # .panel seems to fit our criteria
    panel_el = col_title_el.find_parent(class_='panel')

    # now we can find the table
    table_el = panel_el.find('table')
    rows = scrape_table(table_el)
    return dict(rows)


def scrape_mental(soup: BeautifulSoup) -> dict:
    col_title_el = soup.find('h3', text='MENTAL')
    panel_el = col_title_el.find_parent(class_='panel')
    table_el = panel_el.find('table')

    rows = scrape_table(table_el)
    return dict(rows)



def scrape_physical(soup: BeautifulSoup) -> dict:
    col_title_el = soup.find('h3', text='TECHNICAL')
    panel_el = col_title_el.find_parent(class_='panel')
    table_el = panel_el.find('table')

    rows = scrape_table(table_el)
    return dict(rows)


def scrape_profile_page(url) -> dict:
    res = requests.get(
        url=url,
        headers={
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0'
        }
    )
    res.raise_for_status()
    soup: BeautifulSoup = BeautifulSoup(res.text, 'html.parser')

    technical = scrape_technical(soup)
    mental = scrape_mental(soup)
    physical = scrape_physical(soup)

    return {
        'technical': technical,
        'mental': mental,
        'physical': physical,
    }

if __name__ == "__main__":
    stats = scrape_profile_page('https://fmdataba.com/19/p/621/toni-kroos/')

    from pprint import pprint
    pprint(stats)

этот дает вам:

{
    'technical': {'Corners': '18', 'Crossing': '18', 'Dribbling': '14', 'Finishing': '13', 'First Touch': '18', 'Free Kick': '14', 'Heading': '7', 'Long Shots': '17', 'Long Throws': '8', 'Marking': '8', 'Passing': '20', 'Penalty Taking': '13', 'Tackling': '9', 'Technique': '16'},
    'mental': {'Aggression': '9', 'Anticipation': '17', 'Bravery': '9', 'Composure': '18', 'Concentration': '14', 'Decisions': '18', 'Determination': '10', 'Flair': '11', 'Leadership': '12', 'Off The Ball': '14', 'Positioning': '12', 'Teamwork': '18', 'Vision': '18', 'Work Rate': '12'},
    'physical': {'Corners': '18', 'Crossing': '18', 'Dribbling': '14', 'Finishing': '13', 'First Touch': '18', 'Free Kick': '14', 'Heading': '7', 'Long Shots': '17', 'Long Throws': '8', 'Marking': '8', 'Passing': '20', 'Penalty Taking': '13', 'Tackling': '9', 'Technique': '16'}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...