Читаем таблицы веб-сайтов с помощью Pandas

Данные для Data Science проектов можно получать ото всюду, в том числе и с веб-сайтов, например, страниц Википедии. Сегодня мы расскажем, как извлечь все таблицы из веб-страницы с помощью функции read_html Python-библиотеки Pandas, а также обработать полученные данные, включая нормализацию и приведение типов.

Как работает парсинг сайтов

В Pandas есть функция — read_html, которая использует одну из библиотек для парсинга веб-страниц: BeautifulSoup4, html5lib или lxml. По умолчанию в Pandas стоит lxml, однако, в случае ее отсутствия будет применяться другая. Поэтому для корректного выполнения хотя бы одна из них должна быть установлена. Установить lxml можно, выполнив следующую операцию в командной строке:

pip install lxml

Одна из перечисленных Python-библиотек ищет на указанной веб-странице все таблицы под тэгом <table>. Внутри таблицы могут быть заголовки и сами данные под тэгами <th> и <td>. В результате, Pandas-функция read_html ищет все таблицы на сайте и возвращает их в виде списка в формате DataFrame.

Не всегда все таблицы получается получить в приемлемом виде: могут быть проблемы с заголовками, типами данных, кодировкой. Поэтому прежде всего их необходимо будет обработать. Мы покажем, как в Python получить таблицы с Википедии со страницы пандемии COVID-19 и обработать их.

Извлечение таблиц

Вызовем функцию read_html, передав аргументом ссылку на страницу. Ниже приведён код в Python. Всего библиотека lxml нашла 17 таблиц.

import pandas as pd

# Пандемия_COVID-19
tables = pd.read_html(
  'https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D0%BD%D0%B4%D0%B5%D0%BC%D0%B8%D1%8F_COVID-19')
len(tables)
# 17

Выберем на странице таблицу со статистикой заболеваний по странам и территориям. Поскольку искать её среди 17 таблиц утомительно, мы воспользуемся регулярными выражениями. Для этого передадим аргумент match с подходящим регулярным выражением, например, «стран». Код на Python выглядит следующим образом:

tables = pd.read_html(
    'https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D0%BD%D0%B4%D0%B5%D0%BC%D0%B8%D1%8F_COVID-19',
    match='стран')
DataFrame Pandas
Статистика заболеваний по странам и территориям

Всего нашлось 3 таблицы, которые содержат в своем заголовке слово «стран». Нужная нам находится под индексом 1. Однако таблица выглядит не лучшим образом: появился ещё один столбец, заполненный только NaN, название одного из столбцов содержит HTML-код, возможно нам не требуется результирующий заголовок на 2 уровне и ещё много чего. Исправим это.

Обрабатываем таблицы

В первую очередь избавимся от лишнего столбца, вызвав метод drop. Ещё мы удалим последние две строчки под номером 254 и 255, так как они содержат примечания, а не данные. Ниже представлен код на Python. Поскольку у нас двухуровневый заголовок, то и в аргументе он указывается в виде кортежа (tuple).

df.drop(('Страны и территории', 'Всего 000000000000000'), axis=1, inplace=True)
df.drop(axis=0, index=[254, 255], inplace=True)

Теперь отбросим нижний результирующий уровень, вызвав метод droplevel, а после этого переименуем нечитаемое название одного из столбцов в нормальное с помощью метода rename. В Python это выглядит следующим образом:

df.columns = df.columns.droplevel(-1)
df.rename(columns={'.mw-parser-output .ts-comment-commentedText{border-bottom:1px dotted;cursor:help}@media(hover:none){.mw-parser-output .ts-comment-commentedText:not(.rt-commentedText){border-bottom:0;cursor:auto}}Летал.': 'Летал.'}, inplace=True)

Кроме того, следует убрать источники, заключённые в квадратные скобки. Для этого мы воспользуемся методом replace, указав регулярное выражение и regex=True. Теперь таблица выглядит более приемлемо.

df.replace({'\[[0-9]+\]': ''}, regex=True, inplace=True)
DataFrame Pandas
Обработанная таблица

Нормализация и указание типов

Часто типом таблиц после парсинга веб-страниц является строка (str), которая в DataFrame указывается как object. Кроме того, может быть указана неизвестная кодировка. Это также следует исправить.

Исходная таблица включает подразделы: непризнанные государства, морские суда и т.д. Каждый подраздел имеет заголовок «Справочно» или «Морские суда». Мы воспользуемся этой информацией и разделим DataFrame. Прежде всего определим индексы этих заголовков и в цикле будем делить DataFrame на части, причем мы добавляем к индексу 1 каждый раз, чтобы не включать сам заголовок. Вот так выглядит Python-код:

indices = df[
    (df['Страны и территории'].str.contains('Справочно') |
     df['Страны и территории'].str.contains('Морские суда'))
].index

dfs = []
cur = 0
for idx in indices:
    if idx != indices[-1]:
        part = df.iloc[cur:idx, :]
    else:
        part = df[idx+1: ]
    cur = idx + 1
    dfs.append(part)

Строки могут содержать нечитаемые символы, поэтому их следует нормализовать. Мы нормализуем по типу NFKC. После нормализации следует привести к соответствующему типу данных, например, float32. Но прежде всего нужно избавиться от нечисловых символов, а пробелы и «н/д» нужно заменить на Nan. В итоге, для одного из DataFrame код на Python имеет следующий вид:

temp_df = dfs[0]
temp_df.columns = temp_df.columns.str.normalize('NFKC')
cols = temp_df.columns.drop(temp_df.columns[0]) # Выкинуть “Страны и территории”

for col in cols:
    temp_df.loc[:, col] = temp_df.loc[:, col].str.normalize('NFKC')
    temp_df.loc[:, col] = temp_df.loc[:, col].replace(
        {' ': np.nan, 'н/д': np.nan}, regex=True
    ).astype('float32')

Весь приведенный код можно посмотреть в репозитории на Github.

 

А о том, как парсить сайты и обрабатывать полученные данные в Pandas на практических примерах Data Science, вы узнаете на нашем специализированном курсе по Python «DPREP: Подготовка данных для Data Mining на Python» в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.

Добавить комментарий

Поиск по сайту