Тема 2: Скрытые данные: Что такое Next.js Data / Nuxt State и как вытащить JSON прямо из HTML без парсинга тегов?
Забудь про soup.find('div', class_='price-v2-red'). Это для школьников. Классы меняются, верстка едет, дизайнеры двигают кнопки.
Данные ("State" или "Состояние") — вечны.
Если сайт написан на Next.js (React) или Nuxt.js (Vue), разработчики оставляют нам подарок. Они обязаны передать браузеру "слепок" всех данных, чтобы JS-фреймворк знал, что рисовать. И этот слепок лежит в HTML в виде текста.
Наша задача: Хирургически вырезать этот кусок и превратить в Python Dictionary.
1. Где лежит клад? (Анатомия)
Тип А: Next.js (Самый популярный)
Разработчики Next.js — святые люди. Они кладут данные в тег с фиксированным ID. Это валидный JSON.
-
Как выглядит:
-
Что внутри: Вообще всё. Данные о товаре, категории, хлебные крошки, и часто даже то, что скрыто на сайте (остатки на складе, себестоимость, внутренние ID).
Тип Б: Nuxt.js / Redux State / Global Variables
Тут сложнее. Это не чистый JSON, а JavaScript-объект. Ключи могут быть без кавычек, могут быть undefined.
-
Как выглядит: Или:
2. Чем добывать? (Инструментарий)
Для Next.js (Чистый JSON)
Тебе нужны только стандартный json и любой HTML-парсер (хоть BeautifulSoup, хоть Selectolax), чтобы найти тег.
Для Nuxt.js и "Грязного JS" (Библиотека chompjs)
Стандартный json.loads() упадет с ошибкой, потому что {key: "value"} (без кавычек у ключа) — это валидный JS, но невалидный JSON.
Тут на сцену выходит библиотека chompjs.
- Что делает: Она берет строку с кривым JS-объектом и превращает её в валидный Python dict. Она игнорирует функции и мусор, вытаскивая только данные.
- Установка:
pip install chompjs
3. Техника исполнения (Code Snippets)
Сценарий 1: Идеальный Next.js
import json
from bs4 import BeautifulSoup # Или Selectolax, неважно
import httpx
html = httpx.get("<https://example-nextjs-site.com>").text
soup = BeautifulSoup(html, "lxml")
# 1. Ищем тег по ID
script_tag = soup.find("script", id="__NEXT_DATA__")
if script_tag:
# 2. Берем текст внутри
json_text = script_tag.string
# 3. Превращаем в словарь
data = json.loads(json_text)
# 4. Радуемся. Путь до данных обычно длинный, но надежный:
product = data['props']['pageProps']['product']
print(f"Товар: {product['name']}, Цена: {product['price']}")
Сценарий 2: Грязный JS (Nuxt/Other)
import chompjs
import httpx
from bs4 import BeautifulSoup
html = httpx.get("<https://example-nuxt-site.com>").text
soup = BeautifulSoup(html, "lxml")
# 1. Ищем скрипт, содержащий ключевую переменную
# Тут придется поискать глазами в Ctrl+U, как называется переменная:
# __NUXT__, __INITIAL_STATE__, window.App
scripts = soup.find_all("script")
target_script = None
for script in scripts:
if script.string and "window.__NUXT__" in script.string:
target_script = script.string
break
if target_script:
# 2. Скармливаем ВЕСЬ кусок кода в chompjs
# Он сам найдет объект и распарсит его
data = chompjs.parse_js_object(target_script)
print(data) # Это уже Python Dict
🕵️♂️ Почему это лучше, чем парсить HTML?
- Полнота данных: В HTML может быть написано "Цена со скидкой", а в JSON лежат поля
price_regular,price_discount,currency,vat_rate. Ты получаешь сырые данные из базы. - Типизация: В HTML "1 000 руб." — это строка с пробелом и мусором. В JSON это
1000(integer). Не надо чистить данные. - Стабильность: Верстку (CSS-классы) меняют раз в месяц. Структуру данных (
props) меняют раз в год, потому что это ломает фронтенд.
Резюме:
Перед тем как парсить классы div'ов, потрать 5 минут на поиск __NEXT_DATA__ или window.__STATE__. Если найдешь — сэкономишь 5 часов отладки.