Лучший опыт

Извлечение JSON из HTML с использованием Beautiful Soup в Python: Руководство

Извлечение JSON из HTML с использованием Beautiful Soup в Python: Руководство

Введение в извлечение JSON из HTML с Beautiful Soup

Что такое JSON и почему его извлекают из HTML?

JSON (JavaScript Object Notation) – это легковесный формат обмена данными, который легко читается как людьми, так и машинами. Он часто используется для передачи данных между сервером и веб-приложением. В контексте web scraping, JSON часто встраивается в HTML-код веб-страниц, особенно в теги <script>, для передачи конфигурационных данных, результатов API-запросов или данных для динамической генерации контента.

Извлечение JSON из HTML необходимо, когда стандартные API недоступны или когда требуется получить данные, которые динамически генерируются на стороне клиента (браузера) с использованием JavaScript. Например, это могут быть данные о товарах в интернет-магазине, параметры рекламных кампаний или результаты поиска.

Обзор библиотеки Beautiful Soup и её возможностей

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

Основные возможности Beautiful Soup:

  • Парсинг невалидного HTML: Beautiful Soup достаточно устойчива к плохо сформированному HTML, который часто встречается на практике.
  • Навигация по дереву: Предоставляет методы для навигации по HTML-дереву (потомки, родители, братья/сестры).
  • Поиск элементов: Позволяет искать элементы по тегу, атрибутам, тексту и другим критериям.
  • Извлечение данных: Позволяет извлекать текст, атрибуты и другие данные из найденных элементов.

Необходимые инструменты и установка библиотек (Beautiful Soup, requests)

Для работы с Beautiful Soup и извлечения JSON из HTML вам понадобятся:

  • Python (версия 3.6 или выше).
  • Библиотека requests для загрузки HTML-страниц.
  • Библиотека beautifulsoup4 для парсинга HTML.

Установить библиотеки можно с помощью pip:

pip install requests beautifulsoup4

Основы работы с HTML и Beautiful Soup

Загрузка HTML-страницы с использованием requests

Сначала необходимо загрузить HTML-код страницы, из которой вы хотите извлечь JSON. Это делается с помощью библиотеки requests:

import requests

def download_html(url: str) -> str:
    """Загружает HTML-код страницы по URL."""
    try:
        response = requests.get(url)
        response.raise_for_status()  # Проверка на ошибки HTTP (4xx, 5xx)
        return response.text
    except requests.exceptions.RequestException as e:
        print(f"Ошибка при загрузке страницы: {e}")
        return ""

# Пример использования:
url = "https://example.com"
html_content = download_html(url)
if html_content:
    print("HTML успешно загружен.")

Создание объекта Beautiful Soup и выбор парсера (lxml, html.parser)

После загрузки HTML, необходимо создать объект Beautiful Soup для его парсинга. Важно выбрать подходящий парсер. lxml обычно быстрее, но требует установки ( pip install lxml). html.parser – это встроенный парсер Python, который не требует дополнительных зависимостей, но может быть медленнее и менее толерантным к ошибкам.

from bs4 import BeautifulSoup

def create_soup_object(html: str, parser: str = "lxml") -> BeautifulSoup:
    """Создает объект BeautifulSoup для парсинга HTML."""
    try:
        soup = BeautifulSoup(html, parser)
        return soup
    except Exception as e:
        print(f"Ошибка при создании объекта BeautifulSoup: {e}")
        return None

# Пример использования:
soup = create_soup_object(html_content, "lxml")
if soup:
    print("Объект BeautifulSoup успешно создан.")

Поиск элементов HTML, содержащих JSON (например, теги )

JSON чаще всего находится внутри тегов <script>. Иногда он может быть спрятан в атрибутах элементов, но это менее распространенный случай.

def find_script_tags_with_json(soup: BeautifulSoup) -> list:
    """Находит все теги <script>, которые, вероятно, содержат JSON."""
    script_tags = soup.find_all("script", type="application/json")
    # Иногда JSON находится и в тегах без type="application/json"
    script_tags.extend(soup.find_all("script", string=lambda text: text and "{" in text and "}" in text)) # add possible tags to list 
    return script_tags

# Пример использования:
script_tags = find_script_tags_with_json(soup)
print(f"Найдено {len(script_tags)} тегов <script>, содержащих JSON.")

Извлечение JSON данных из HTML

Идентификация тегов , содержащих JSON

Мы уже идентифицировали теги <script> в предыдущем разделе. Теперь нужно убедиться, что они действительно содержат JSON, а не просто JavaScript-код.

Получение текста JSON из найденных тегов

После идентификации тегов, содержащих JSON, необходимо извлечь текст из этих тегов.

def extract_json_text_from_script_tag(script_tag: BeautifulSoup.Tag) -> str:
    """Извлекает текст JSON из тега <script>."""
    return script_tag.string or ""

# Пример использования:
if script_tags:
    json_text = extract_json_text_from_script_tag(script_tags[0])
    print(f"Текст JSON: {json_text[:100]}..." if json_text else "JSON текст не найден.") # Print first 100 chars

Удаление лишних символов и форматирование текста JSON

Иногда текст JSON может содержать лишние символы, такие как комментарии JavaScript, переменные или обрамляющий код. Эти символы необходимо удалить, чтобы получить чистый JSON.

import re

def clean_json_text(json_text: str) -> str:
    """Удаляет лишние символы из текста JSON."""
    # Удаляем комментарии JavaScript
    json_text = re.sub(r"\/\/[^\n]*|\/\*[\s\S]*?\*\/", "", json_text)
    # Удаляем присваивания переменным
    json_text = re.sub(r"\s*=\s*", "", json_text, count=1) # remove assignment operator and variable name
    return json_text.strip()

# Пример использования:
cleaned_json_text = clean_json_text(json_text)
print(f"Очищенный текст JSON: {cleaned_json_text[:100]}..." if cleaned_json_text else "Очищенный JSON текст не найден.")

Преобразование JSON строки в Python объекты

Использование модуля json для парсинга JSON

Python предоставляет встроенный модуль json для парсинга JSON строк в Python объекты (словари, списки и т.д.).

import json

def parse_json(json_text: str) -> dict or list or None:
    """Парсит JSON строку в Python объект."""
    try:
        data = json.loads(json_text)
        return data
    except json.JSONDecodeError as e:
        print(f"Ошибка при парсинге JSON: {e}")
        return None

# Пример использования:
data = parse_json(cleaned_json_text)
if data:
    print(f"JSON успешно распарсен. Тип данных: {type(data)}")
    # print(data) # uncomment to see all data
else:
    print("Не удалось распарсить JSON.")

Обработка ошибок парсинга JSON (например, json.JSONDecodeError)

Важно обрабатывать ошибки парсинга JSON, так как JSON может быть невалидным или содержать неожиданные символы. Функция parse_json демонстрирует обработку json.JSONDecodeError.

Примеры извлечения JSON из различных типов HTML структур

JSON, встроенный в тег (примеры с веб-сайтов)

Рассмотрим пример извлечения данных о товарах из HTML-кода интернет-магазина. Предположим, что информация о товаре хранится в теге <script> в формате JSON:

<script type="application/json">
{
  "product_id": 123,
  "name": "Футболка",
  "price": 19.99,
  "description": "Хлопковая футболка с логотипом."
}
</script>

Код для извлечения и парсинга этого JSON будет выглядеть следующим образом:

# (Используются функции download_html, create_soup_object, find_script_tags_with_json, extract_json_text_from_script_tag, clean_json_text, parse_json, определенные выше)

url = "https://example.com/product/123" # replaced URL by dummy for example purpose
html_content = download_html(url)
soup = create_soup_object(html_content, "lxml")
script_tags = find_script_tags_with_json(soup)

if script_tags:
    json_text = extract_json_text_from_script_tag(script_tags[0])
    cleaned_json_text = clean_json_text(json_text)
    product_data = parse_json(cleaned_json_text)

    if product_data:
        print(f"Название товара: {product_data['name']}")
        print(f"Цена товара: {product_data['price']}")
    else:
        print("Не удалось извлечь данные о товаре.")
else:
    print("Тег <script> с данными о товаре не найден.")

JSON, хранящийся в атрибутах HTML элементов (редкие случаи)

В редких случаях JSON может быть закодирован в атрибуте HTML элемента. Например:

<div data-product='{"id": 456, "name": "Кружка", "price": 9.99}'></div>

Для извлечения JSON из атрибута можно использовать следующий код:

# (Используются функции download_html, create_soup_object, parse_json, определенные выше)

# example URL replaced by dummy URL
url = "https://example.com/product/456"
html_content = download_html(url)
soup = create_soup_object(html_content, "lxml")

product_div = soup.find("div", attrs={"data-product": True})

if product_div:
    json_text = product_div["data-product"]
    product_data = parse_json(json_text)

    if product_data:
        print(f"Название товара: {product_data['name']}")
    else:
        print("Не удалось распарсить JSON из атрибута.")
else:
    print("Элемент с атрибутом data-product не найден.")

Извлечение нескольких JSON объектов из одного HTML документа

Иногда в HTML документе может быть несколько тегов <script>, содержащих JSON данные. В этом случае необходимо итерироваться по всем найденным тегам и извлекать JSON из каждого из них.

# (Используются функции download_html, create_soup_object, find_script_tags_with_json, extract_json_text_from_script_tag, clean_json_text, parse_json, определенные выше)
# example URL replaced by dummy URL
url = "https://example.com/products"
html_content = download_html(url)
soup = create_soup_object(html_content, "lxml")
script_tags = find_script_tags_with_json(soup)

product_list = []
if script_tags:
    for script_tag in script_tags:
        json_text = extract_json_text_from_script_tag(script_tag)
        cleaned_json_text = clean_json_text(json_text)
        product_data = parse_json(cleaned_json_text)

        if product_data:
            product_list.append(product_data)

    if product_list:
        print(f"Найдено {len(product_list)} товаров.")
        # print(product_list) # uncomment to print product list
    else:
        print("Не удалось извлечь данные о товарах.")
else:
    print("Теги <script> с данными о товарах не найдены.")

Обработка сложных случаев и распространенные ошибки

JSON с экранированными символами и их корректная обработка

JSON может содержать экранированные символы, такие как \", \\, \n. Модуль json автоматически обрабатывает эти символы при парсинге.