Как конвертировать HTML-документ в JSON: пишем рабочую программу на JavaScript
{"subtitle": "Дерзкая сериализация одного формата в другой — без последствий и проблем с законом."}
Иллюстрация: Polina Vari для Skillbox Media
У языка гипертекстовой разметки веб-страниц HTML и формата данных JSON есть общая черта: оба они помогают структурировать информацию, хотя в веб-разработке их используют для разных задач. HTML необходим для отображения разметки сайтов, а JSON — для обмена данными с сервером.
То есть, отправляя с сервера информацию в формате JSON, мы делаем её независимой от формата отображения на устройстве пользователя: он может листать экраны нативного мобильного приложения (где не используется HTML), запрашивать данные из консольной утилиты или смотреть в браузере. Да и для отображения в браузере подчас нужна «многоэтажная» JavaScript-логика, которую в сгенерированный сервером HTML просто не упаковать.
Вообще, библиотеки для работы с JSON есть практически в любом современном языке программирования, то есть это универсальный формат. Однако сама аббревиатура JSON изначально является сокращением от JavaScript Object Notation, а потому в JS есть встроенные методы для работы с JSON. Кроме того, вид данных в последнем почти идентичен синтаксису объектов в JavaScript. То есть JavaScript идеально поддерживает свой «дочерний» формат.
Где встречается этот ваш JSON
Итак, многие веб-разработчики предпочитают выдавать по запросу с фронтенда HTML-код, упакованный в JSON, и уже в браузере конвертировать его в обычную HTML-разметку. Так работает и наш сайт — Skillbox Media (мы анализировали это в статье о парсинге данных).
Такой же способ подгрузки дополнительных статей в рубриках отдельных стран реализован и на сайте организации «Репортёры без границ». Выберем случайную страну — пусть это будет Италия. При нажатии на кнопку Show more posts загружаются три более старых материала. Если перейти по ссылке в кнопке, то внутри элемента <textarea> мы увидим JSON-вставку с кодом статей:
Произвести конвертацию HTML в JSON несложно, если знать особенности обоих форматов. Сначала мы кратко разберём эти особенности, а потом напишем небольшое приложение, которое будет конвертировать один формат в другой.
Кратко о разнице HTML и JSON
Вспомним, что HTML-разметка сайта состоит из элементов, которые, как правило, состоят из трёх частей: открывающий тег, закрывающий тег и контент:
Внутри открывающего тега могут располагаться различные атрибуты в формате attribute="value":
JSON устроен иначе: он состоит из набора пар "ключ": "значение". Ключи и значения заключены в кавычки, после ключа следует двоеточие, пары разделены запятыми и упакованы внутрь круглых скобок.
В качестве значений в JSON-разметке могут использоваться числа, строки, логические значения, объекты, массивы и null (нам далее пригодится знание этого факта, потому что мы будем создавать пример с объектами и массивами).
Последняя пара не отделяется запятой — висящие запятые (trailing commas) в JSON запрещены.
Кажется, что различия ощутимые, но это лишь на первый взгляд: на деле представить HTML-код в формате JSON относительно несложно, следите за руками:
Любой HTML-элемент легко превращается в JSON-пару "ключ": "значение", где ключом выступает название элемента, а значением — его текстовое содержимое или вложенный элемент следующего уровня.
Теперь попробуем с помощью JavaScript сделать нечто подобное на примере одной страницы сайта.
Проводим конвертацию
Процесс упаковки данных в JSON называется сериализацией, а обратный процесс — десериализацией. Перевести HTML-разметку в JSON можно двумя способами:
- Преобразовать разметку «в лоб», запихнув весь HTML-код в одну пару "ключ": "значение" — то есть как значение ключа <html>, (как на скриншоте разметки Skillbox Media).
- Написать более сложный вариант JSON, чтобы структура JSON-строки полностью соответствовала структуре HTML-документа.
Выбор способа будет зависеть от ваших задач. Мы же в образовательных целях разберём более сложный, второй способ — это позволит нам опробовать возможности языка JavaScript и сохранить структуру страницы. Возьмём для примера следующий код:
Здесь есть все базовые составляющие шаблона HTML-страницы: служебные элементы и контент в теле страницы. У некоторых элементов есть атрибуты и вложенные элементы, что тоже нужно учесть при конвертировании.
Вернёмся к типичной ситуации, как на первых скриншотах: каркас страницы статичен, а при подгрузке с сервера меняется только тело, которое и нужно скормить JSON-конвертеру. Следовательно, нас интересует содержимое элемента <body>:
Сейчас на странице несколько произвольных абзацев и картинка. Наш учебный конвертер будет почти универсальным и сможет упаковать в JSON эти и любые другие HTML-элементы в теле — вплоть до третьего уровня вложенности.
Алгоритм будет следующим:
- Создадим JavaScript-объект для записи HTML-элементов, их атрибутов и содержимого.
- По очереди запишем в объект все элементы.
- Преобразуем объект в JSON-формат.
Чуть ниже мы рассмотрим указанные шаги в деталях, но для начала нужно создать два файла:
- Первый назовём «HTML в JSON.html» (по ссылке — содержимое файла на сервисе Pastebin) и поместим в него собственно страницу для преобразования.
- Второй будет называться «script.js» (по ссылке — содержимое файла на сервисе Pastebin) — в него мы вынесем написанный на JavaScript конвертер страницы в JSON.
Скрипт будет вызываться после загрузки нашей страницы, поэтому мы поместим в тело HTML-файла элемент <script> со ссылкой на JS-файл (см. первую вставку кода).
Теперь рассмотрим пошагово, как будет устроен наш конвертер в файле script.js.
Сначала объявим функцию с понятным названием — «конвертер HTML в JSON». В её теле будет находиться конвертер. После объявления функции заложим её вызов с задержкой в одну секунду с помощью встроенного метода setTimeout(): таким образом, после открытия HTML-файла в браузере мы сначала увидим исходную страницу, а потом она сотрётся и заменится на JSON-строку.
Теперь займёмся телом созданной функции и для красоты на каждом шаге её работы будем выводить понятные уведомления в консоль (скриншот с ними будет ниже). Начнём с этого:
Результаты поиска элементов, их атрибутов и содержимого также будут сопровождаться уведомлениями, чтобы мы не пропустили ничего важного.
Мы уже упоминали, что формат JSON похож на объекты в JavaScript. Именно поэтому, следуя алгоритму, создадим объект и запишем в него HTML-элементы.
После создания объекта начинается главное: цикл перебора и записи элементов и содержимого в объект, и тут самое время поговорить о том, что конкретно мы хотим в итоге получить.
Структура JSON-результата будет такой: название элемента первого уровня, потом его атрибуты, затем его содержимое — вложенные элементы или текст. Если содержимое — это вложенные элементы, то внутри будут их названия, атрибуты и содержимое (также вложенные элементы или текст). Каждая часть структуры будет иметь порядковый номер — начиная с нуля — и указание на уровень элемента.
К примеру, первый элемент в теле нашей страницы после перевода в JSON должен принять такой вид:
Переводим на человеческий язык: «элемент 0 (то есть первый — у программистов своя математика) первого уровня, имя P, есть атрибуты. Атрибут 1 class="myclass", атрибут 2 style="margin: 1px;". Содержит текст „Какой-то абзац“».
element0Level1 — это ключ первой пары "ключ": "значение", а всё остальное — содержимое значения этой пары, которое представляет собой массив вложенных объектов — объекта с названием элемента, объекта со всеми атрибутами и объекта с содержимым.
Следующие элементы 1-го уровня будут упакованы в такие же пары, а элементы 2-го и 3-го уровней повторят ту же структуру во вложениях. Короче, у нас чередуются объекты, вложенные массивы и вложенные в них объекты.
Теперь, когда мы представили себе результат, разберём реализацию. Цикл перебора HTML-элементов должен разобрать их на кусочки — как детальки лего. Находить составляющие элементов помогут следующие свойства:
- tagName найдёт названия элементов;
- attributes отдаст коллекцию атрибутов элемента, а обращения к attributes.name и attributes.value вернут названия и значения отдельных атрибутов;
- для получения вложенных элементов любого уровня и текста понадобятся свойства children и textContent соответственно.
На каждом из уровней вложенности HTML-элементов мы сначала будем измерять их количество, а потом перебирать с помощью цикла for.
Здесь и далее считаем количество HTML-элементов или атрибутов с помощью свойства length.
Название переменной e — это просто понятное сокращение от «element». Вы же можете выбрать любое, на свой вкус.
Самое простое — записать названия элементов. Для этого обращаемся к коллекции потомков элемента <body> и перебираем их, подставляя следующий порядковый номер элемента e. Затем обращаемся к свойству tagName:
Здесь и далее мы конструируем понятные названия ключей в квадратных скобках с помощью комбинации текста в кавычках и названий соответствующих переменных.
С получением атрибутов ситуация усложняется: сначала нужно проверять их наличие у элемента.
Реакция конвертера будет отличаться в зависимости от результата проверки. Если атрибуты найдены, нужно сосчитать их количество, перебрать по очереди и красиво записать в нужное место.
Для перебора атрибутов у нас отдельный вложенный цикл, где переменная a является сокращением от слова «attribute».
Если атрибуты не были найдены, записываем пустое значение.
После атрибутов нужно записать содержимое элемента — то есть текст, вложенные элементы или пустое значение. Для этого нужно измерить количество вложенных элементов, а если их нет, то проверить наличие текста. Это делается следующим образом:
После проверки происходит запись найденного содержимого элемента первого уровня:
…если содержимое — это элементы второго уровня.
…если содержимое — текст.
…если нет содержимого.
Не будем душнить и бомбить вас кодом дальше: элементы второго и третьего уровней записываются аналогичным образом во время перебора содержимого их родительских элементов первого уровня — это можно увидеть ниже, в финальной версии кода.
Для второго и третьего уровней в их собственных циклах for элементы перебираются с помощью переменных e2 и e3 соответственно, а в переборе их атрибутов фигурируют переменные a2 и a3 — всё по аналогии с первым уровнем.
Когда циклы перебора/записи в объект элементов всех уровней, их атрибутов и содержимого закончены, мы скармливаем получившийся JavaScript-объект встроенному методу JSON.stringify() для сериализации:
После этого нужно вывести результат на страницу с помощью команды на перезапись элемента <body> содержимым переменной result:
Итоговую версию кода можно посмотреть (и даже скопипастить, если лень разбираться) по ссылке. В результате его выполнения наша страница превращается в JSON-строку и принимает следующий вид:
В любом JSON-форматтере можно просмотреть результат в более читабельном виде.
На всякий случай прогоним результат и через валидатор JSON.
А в консоли — вот такая красота.
Итоги
Мы рассмотрели один из способов превращения HTML-файла в JSON-строку. Вариантов реализации может быть много, поэтому вид кода будет сильно зависеть от ваших задач. Руководствуясь указанным алгоритмом и встроенными методами языка JavaScript, вы сможете самостоятельно реализовать подобный конвертер. А о том, как выполнить обратную конвертацию, мы расскажем в следующей статье.