Перейти к содержанию

Тема 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.

  • Как выглядит:

    <script id="__NEXT_DATA__" type="application/json">
    {"props":{"pageProps":{"product":{"id":123,"name":"Iphone","price":999}}}}
    </script>
    
  • Что внутри: Вообще всё. Данные о товаре, категории, хлебные крошки, и часто даже то, что скрыто на сайте (остатки на складе, себестоимость, внутренние ID).

Тип Б: Nuxt.js / Redux State / Global Variables

Тут сложнее. Это не чистый JSON, а JavaScript-объект. Ключи могут быть без кавычек, могут быть undefined.

  • Как выглядит: Или:

    <script>window.__NUXT__=(function(a,b){return {state:{product:{name:"Iphone"}}...}});</script>
    
    <script>window.initialState = {user: {id: 1, name: 'Alex'}}</script>
    

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?

  1. Полнота данных: В HTML может быть написано "Цена со скидкой", а в JSON лежат поля price_regular, price_discount, currency, vat_rate. Ты получаешь сырые данные из базы.
  2. Типизация: В HTML "1 000 руб." — это строка с пробелом и мусором. В JSON это 1000 (integer). Не надо чистить данные.
  3. Стабильность: Верстку (CSS-классы) меняют раз в месяц. Структуру данных (props) меняют раз в год, потому что это ломает фронтенд.

Резюме: Перед тем как парсить классы div'ов, потрать 5 минут на поиск __NEXT_DATA__ или window.__STATE__. Если найдешь — сэкономишь 5 часов отладки.