Beautiful Soup и извлечение данных из таблиц: как это работает?

Что такое Beautiful Soup и зачем он нужен?

Beautiful Soup – это Python-библиотека, предназначенная для парсинга HTML и XML документов. Она создает дерево разбора из исходного кода, позволяя легко перемещаться по документу и извлекать нужные данные. В контексте извлечения табличных данных, Beautiful Soup позволяет нам эффективно разбирать структуру HTML-таблиц и получать содержащуюся в них информацию, будь то цены товаров, курсы валют или статистические данные. Библиотека особенно полезна, когда API для получения данных недоступен, и необходимо напрямую парсить HTML-код веб-страницы.

Краткий обзор HTML-структуры таблицы (\

, \

, \

) и извлечение данных из ячеек (\

, \

)

Прежде чем приступить к извлечению данных, важно понимать структуру HTML-таблицы:

  • <table>: Корневой тег, определяющий таблицу.
  • <tr>: Определяет строку таблицы (table row).
  • <th>: Определяет заголовочную ячейку таблицы (table header).
  • <td>: Определяет ячейку данных таблицы (table data).

Таблица состоит из строк, а каждая строка – из ячеек. <th> обычно используется для первой строки, обозначающей заголовки столбцов.

Установка и настройка Beautiful Soup в Python

Установить Beautiful Soup можно с помощью pip:

pip install beautifulsoup4

Кроме того, потребуется установить парсер, например, lxml или html.parser (входит в стандартную библиотеку Python):

pip install lxml

Вот пример импорта и инициализации Beautiful Soup:

from bs4 import BeautifulSoup

html_doc: str = """ 
<html><body><table><tr><th>Header 1</th><th>Header 2</th></tr><tr><td>Data 1</td><td>Data 2</td></tr></table></body></html>
"""

soup: BeautifulSoup = BeautifulSoup(html_doc, 'lxml')
print(soup.prettify())

Основы извлечения данных из таблиц с использованием Beautiful Soup

Поиск таблицы по тегу, атрибуту или классу

Первый шаг – найти нужную таблицу на странице. Это можно сделать с помощью метода find() или find_all():

from bs4 import BeautifulSoup

html_doc: str = """ 
<html><body><table id='my_table'><tr><th>Header 1</th><th>Header 2</th></tr><tr><td>Data 1</td><td>Data 2</td></tr></table></body></html>
"""

soup: BeautifulSoup = BeautifulSoup(html_doc, 'lxml')

table = soup.find('table', id='my_table') # Поиск по id
#table = soup.find('table', class_='data-table') # Поиск по классу
print(table)

Итерация по строкам таблицы (\

и \

)

После того, как таблица найдена, можно перебирать её строки и ячейки:

from bs4 import BeautifulSoup
from typing import List

html_doc: str = """ 
<html><body><table id='my_table'><tr><th>Header 1</th><th>Header 2</th></tr><tr><td>Data 1</td><td>Data 2</td></tr></table></body></html>
"""

soup: BeautifulSoup = BeautifulSoup(html_doc, 'lxml')

table = soup.find('table', id='my_table')

if table:
    for row in table.find_all('tr'): # Итерация по строкам
        cells: List[str] = [cell.text for cell in row.find_all(['td', 'th'])] # Извлечение текста из ячеек
        print(cells)
else:
    print("Table not found")

Получение текста из ячеек таблицы

Для получения текста из ячейки используйте атрибут .text:

cell_text: str = cell.text # text returns string

Продвинутые методы извлечения данных

Извлечение данных из таблиц со сложной структурой (colspans, rowspans)

Таблицы могут иметь ячейки, объединенные по столбцам (colspan) или строкам (rowspan). Обработка таких случаев требует более сложной логики, например, учета атрибутов colspan и rowspan при построении структуры данных. В большинстве случаев, оптимально будет преобразовать такую структуру в более плоскую для удобства дальнейшей обработки.

Обработка пропущенных значений и пустых ячеек

В таблицах часто встречаются пропущенные или пустые ячейки. Необходимо предусмотреть их обработку, например, заменяя их на None или на какое-либо значение по умолчанию.

if cell.text.strip() == "":
    value = None
else:
    value = cell.text.strip()

Фильтрация данных на основе содержимого ячеек

Можно фильтровать строки таблицы на основе содержимого ячеек. Например, извлечь только строки, содержащие определенное слово или значение:

for row in table.find_all('tr'):
    cells = row.find_all('td')
    if len(cells) > 0 and "example" in cells[0].text:
        # Обработка строки
        print("Row found")

Использование CSS-селекторов для более точного поиска

Beautiful Soup поддерживает CSS-селекторы, что позволяет более точно находить нужные элементы:

table = soup.select_one("#my_table") # Поиск по id
rows = soup.select("#my_table tr") # Поиск всех tr внутри таблицы с id my_table

Практические примеры извлечения данных из таблиц

Пример 1: Извлечение таблицы курсов валют с веб-сайта

(Предположим, что структура HTML известна)

import requests
from bs4 import BeautifulSoup
from typing import List, Dict

def get_currency_rates(url: str) -> List[Dict[str, str]]:
    """Извлекает таблицу курсов валют с заданного URL.

    Args:
        url: URL веб-страницы.

    Returns:
        Список словарей, где каждый словарь представляет строку таблицы.
    """
    try:
        response = requests.get(url)
        response.raise_for_status() # Проверка на ошибки HTTP

        soup = BeautifulSoup(response.text, 'lxml')
        table = soup.find('table', class_='currency-table')
        data: List[Dict[str, str]] = []

        if table:
            for row in table.find_all('tr')[1:]:
                cells = row.find_all('td')
                if len(cells) == 3:
                    currency: str = cells[0].text.strip()
                    rate: str = cells[1].text.strip()
                    unit: str = cells[2].text.strip()
                    data.append({'currency': currency, 'rate': rate, 'unit': unit})
        return data

    except requests.exceptions.RequestException as e:
        print(f"Error fetching URL: {e}")
        return []

# Пример использования
url: str = 'https://www.example.com/currency'
rates: List[Dict[str, str]] = get_currency_rates(url)
if rates:
    for rate in rates:
        print(rate)

Пример 2: Извлечение таблицы с данными о товарах из интернет-магазина

(Аналогично предыдущему примеру, требуется знание структуры HTML)

Пример 3: Извлечение исторических данных из таблицы на странице Википедии

Википедия часто использует стандартную структуру таблиц, что упрощает парсинг.

Обработка ошибок и распространенные проблемы

Обработка исключений при отсутствии таблицы или элементов в ней

Всегда оборачивайте код парсинга в блоки try...except для обработки возможных ошибок:

try:
    table = soup.find('table', id='...')
    # ...
except AttributeError as e:
    print(f"Table not found: {e}")

Решение проблем с кодировкой и неверным отображением символов

Убедитесь, что кодировка страницы соответствует кодировке, используемой в вашем коде. Используйте response.encoding = 'utf-8' для установки кодировки.

Советы по оптимизации кода для повышения производительности

  • Используйте CSS-селекторы для более быстрого поиска элементов.
  • Избегайте излишних вызовов find() и find_all().
  • По возможности, используйте кэширование результатов парсинга.

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