Пошаговые инструкции Для начинающих 13 мин

Markdown в n8n: как преобразовать черновик в HTML

Преобразуйте Markdown в HTML с заголовками, списком и таблицей. 12 шагов, готовый workflow и проверка структуры без публикации.

n8n HTML Markdown

Черновик из чата или редактора часто приходит в Markdown: решётки обозначают заголовки, звёздочки — выделение, строки с цифрами — список. Чтобы передать его в поле HTML, нужна не замена символов вручную, а преобразование с проверкой результата. В этой инструкции сделаем его штатным Markdown-узлом n8n.

Результат инструкции

Соберём ручной workflow на вымышленной сводке. На входе будет текст Markdown, на выходе — один JSON с HTML-фрагментом и статусом needs_review. Проверим один заголовок H2, два H3, два пункта нумерованного списка, выделение, таблицу и ссылку. approved и action_performed останутся false.

Мы преобразуем формат, а не пишем текст моделью. AI-ключи и внешние API не нужны. Итог не публикуется и не отправляется. Проверка рассчитана на конкретный учебный черновик: она не оценивает факты текста и не делает HTML безопасным для вставки на сайт.

Что понадобится

  • Работающий n8n с доступом к новому учебному workflow. Проверено на n8n 2.42.4, Markdown версии 1 и HTML версии 1.2.
  • Code на JavaScript и собственный учебный текст. Чужие документы и пользовательский HTML для первого запуска не нужны.
  • Возможность смотреть JSON в Output. Отдельный сайт, браузерный предпросмотр HTML и доступ к CMS в этой схеме не используются.

Установить n8n локально через Docker

Шаг 1. Импортируйте готовый workflow

Скачать n8n-markdown-html-preview.json

Создайте отдельный workflow. В меню редактора выберите Import from File и загрузите JSON. После импорта должны появиться семь соединённых узлов:

text
Manual Trigger → Sample Draft → Check Draft → Render HTML
→ Check HTML → Inspect HTML → Draft Preview

Render HTML — Markdown, Inspect HTML — HTML. Остальные узлы кроме триггера — Code. При ручной сборке сохраните эти имена: проверки обращаются к Check Draft и Check HTML по имени. JSON не содержит credentials и ID чужой схемы, workflow остаётся неактивным.

Шаг 2. Подготовьте Markdown-черновик

Скачать исходный n8n-demo-draft.md

Файл нужен для сверки и не загружается узлом автоматически. В Sample Draft выберите Mode: Run Once for All Items, Language: JavaScript. Вставьте код:

javascript
const markdown = `# Учебная сводка

**Статус:** черновик.

## Следующие шаги

1. Проверить цифры.
2. Согласовать текст.

## Проверка пакета

| Этап | Статус |
| --- | --- |
| Проверка | Готово |
| Отправка | Не выполнена |

[Термины](https://ezgpt.ru/glossary)
`;
return [{ json: { draft_id: 'md-demo-001', markdown } }];

Запустите от Manual Trigger. В Output ожидается один item с draft_id md-demo-001 и строкой markdown. Не удаляйте пустые строки между заголовками, списком и таблицей. Строка с дефисами отделяет заголовок таблицы от данных; две отдельные строки с 1. и 2. нужны для нумерованного списка.

Текст помещён между обратными кавычками JavaScript, поэтому переносы остаются настоящими переносами. Для другого текста, содержащего обратные кавычки или конструкции интерполяции JavaScript, такой способ вставки нужно адаптировать. Здесь используйте ровно приведённый пример.

Шаг 3. Проверьте вход

В Check Draft задайте Run Once for All Items, JavaScript и вставьте:

javascript
const items = $input.all();
if (items.length !== 1) throw new Error('one_draft_required');
const row = items[0].json;
if (row.draft_id !== 'md-demo-001') throw new Error('draft_id_invalid');
if (typeof row.markdown !== 'string' || !row.markdown.trim()) {
  throw new Error('markdown_text_required');
}
if (Buffer.byteLength(row.markdown, 'utf8') > 16384) throw new Error('markdown_too_large');
return [{ json: { draft_id: row.draft_id, markdown: row.markdown } }];

Проверка требует один item, заданный ID и непустую строку не длиннее 16384 байт UTF-8. Это лимит учебной схемы, а не ограничение формата Markdown. Служебные поля входа не переносятся дальше: сохраняются только draft_id и markdown.

В Settings всех узлов оставьте On Error: Stop Workflow. Continue On Fail, Always Output Data, Retry On Fail и Execute Once не включайте. Пустая строка или неверный ключ не исправятся повторным преобразованием.

Шаг 4. Настройте Markdown to HTML

Откройте Render HTML и задайте следующие значения:

Поле Значение На что обратить внимание
Mode Markdown to HTML Не HTML to Markdown
Markdown {{ $json.markdown }} Режим Expression
Destination Key html Новое поле рядом с markdown
Tables Support On В Options, для таблицы примера

В поле Markdown переключитесь в Expression и вставьте {{ $json.markdown }}. В экспортированном JSON выражение хранится с начальным знаком =; вручную вводить его как обычный текст не нужно. Если оставить Fixed и написать $json.markdown, узел преобразует буквальную строку вместо черновика.

Destination Key оставьте html. Не указывайте markdown: так результат перезапишет исходный текст. В этой инструкции также не используется вложенный путь result.html — проверка ждёт поле html на верхнем уровне.

Шаг 5. Задайте заголовки и формат выхода

В Options включите Tables Support, задайте Header Level Start: 2 и No Header ID: On. Complete HTML Document оставьте Off. GitHub Code Blocks — On, Simple Line Breaks — Off; остальные параметры не меняйте.

При Header Level Start 2 исходный # превращается в H2, а ## — в H3. Это удобно для фрагмента внутри страницы, где основной H1 уже есть. No Header ID убирает автоматически созданные ID заголовков. Мы получаем фрагмент, не полный документ с html, head и body.

Выполните Render HTML. Исходные draft_id и markdown должны сохраниться, рядом появится строка html. В проверенной версии результат такой:

html
<h2>Учебная сводка</h2>
<p><strong>Статус:</strong> черновик.</p>
<h3>Следующие шаги</h3>
<ol>
<li>Проверить цифры.</li>
<li>Согласовать текст.</li>
</ol>
<h3>Проверка пакета</h3>
<table>
<thead>
<tr>
<th>Этап</th>
<th>Статус</th>
</tr>
</thead>
<tbody>
<tr>
<td>Проверка</td>
<td>Готово</td>
</tr>
<tr>
<td>Отправка</td>
<td>Не выполнена</td>
</tr>
</tbody>
</table>
<p><a href="https://ezgpt.ru/glossary">Термины</a></p>

Не вставляйте фрагмент в боевую страницу для проверки. Пока достаточно посмотреть его как строку в JSON. Наличие тегов в Output не означает, что браузер уже отрисовал документ или что текст можно публиковать.

Шаг 6. Проверьте поле HTML и сохранность исходника

В Check HTML выберите Run Once for All Items, JavaScript и вставьте:

javascript
const items = $input.all();
if (items.length !== 1) throw new Error('one_html_item_required');
const row = items[0].json;
const original = $('Check Draft').first().json;
if (Object.keys(row).sort().join('|') !== 'draft_id|html|markdown') {
  throw new Error('html_fields_mismatch');
}
if (row.draft_id !== original.draft_id || row.markdown !== original.markdown) {
  throw new Error('draft_changed');
}
if (typeof row.html !== 'string' || !row.html.trim() || row.html === row.markdown) {
  throw new Error('html_text_required');
}
if (Buffer.byteLength(row.html, 'utf8') > 32768) throw new Error('html_too_large');
return items;

На выходе ожидаются ровно draft_id, markdown и html. Исходные ID и Markdown сравниваются с Check Draft, HTML должен быть непустым и не превышать 32768 байт. Проверка позволяет заметить неверный Destination Key, перезапись исходника и отсутствие результата.

Это ещё не разбор HTML. Мы не ищем теги регулярными выражениями и не объявляем строку безопасной только потому, что она не пустая. Структуру прочитает следующий штатный узел.

Шаг 7. Прочитайте структуру через HTML

В Inspect HTML выберите Operation: Extract HTML Content, Source Data: JSON, JSON Property: html. В Extraction Values добавьте семь строк:

Key CSS Selector Return Value
headings h2 Text
sections h3 Text
steps ol > li Text
strong strong Text
headers th Text
cells td Text
links a Attribute: href

Для каждой строки включите Return Array. Для links поле Attribute заполните значением href. В Options включите Trim Values и Clean Up Text. После выполнения должен быть один item с семью массивами.

В headings ожидается Учебная сводка, в sections — Следующие шаги и Проверка пакета. Массив steps должен содержать два отдельных текста, а не одну склеенную строку. В headers будут Этап и Статус, в cells — четыре значения двух строк таблицы.

Inspect HTML читает переданную строку; он не открывает страницу и не переходит по ссылке Термины. В links попадёт только значение href. Return Array нужен даже для одного H2: следующий Code сравнивает именно массивы.

Шаг 8. Соберите проверенный предпросмотр

В Draft Preview задайте Run Once for All Items, JavaScript и вставьте:

javascript
const items = $input.all();
if (items.length !== 1) throw new Error('one_inspection_required');
const row = items[0].json;
const expected = {
  headings: ['Учебная сводка'],
  sections: ['Следующие шаги', 'Проверка пакета'],
  steps: ['Проверить цифры.', 'Согласовать текст.'],
  strong: ['Статус:'],
  headers: ['Этап', 'Статус'],
  cells: ['Проверка', 'Готово', 'Отправка', 'Не выполнена'],
  links: ['https://ezgpt.ru/glossary'],
};
for (const [key, value] of Object.entries(expected)) {
  if (JSON.stringify(row[key]) !== JSON.stringify(value)) throw new Error(key + '_mismatch');
}
const rendered = $('Check HTML').first().json;
return [{ json: {
  draft_id: rendered.draft_id,
  status: 'needs_review',
  html: rendered.html,
  checks: { heading_h2: 1, sections_h3: 2, ordered_steps: 2, table_rows: 2, links: 1 },
  approved: false,
  action_performed: false,
} }];

Сравнение намеренно точное: проверяются тексты, порядок и число элементов данного учебного примера. Если вы поменяете содержание черновика, нужно осознанно обновить expected и повторить тесты. Этот Code не является универсальной проверкой любой статьи.

HTML берётся из Check HTML, потому что Inspect HTML вернул извлечённые поля вместо исходного объекта. needs_review и approved false — данные для ручной проверки, а не готовый механизм согласования. Узлов публикации и отправки в цепочке нет.

Шаг 9. Выполните всю цепочку

Нажмите Execute Workflow. Каждый узел должен выдать один item. Итоговый JSON:

json
{
  "draft_id": "md-demo-001",
  "status": "needs_review",
  "html": "<h2>Учебная сводка</h2>\n<p><strong>Статус:</strong> черновик.</p>\n<h3>Следующие шаги</h3>\n<ol>\n<li>Проверить цифры.</li>\n<li>Согласовать текст.</li>\n</ol>\n<h3>Проверка пакета</h3>\n<table>\n<thead>\n<tr>\n<th>Этап</th>\n<th>Статус</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Проверка</td>\n<td>Готово</td>\n</tr>\n<tr>\n<td>Отправка</td>\n<td>Не выполнена</td>\n</tr>\n</tbody>\n</table>\n<p><a href=\"https://ezgpt.ru/glossary\">Термины</a></p>",
  "checks": {
    "heading_h2": 1,
    "sections_h3": 2,
    "ordered_steps": 2,
    "table_rows": 2,
    "links": 1
  },
  "approved": false,
  "action_performed": false
}

Сверьте не только статус success, но и checks: один H2, два H3, два пункта списка, две строки данных таблицы и одна ссылка. В таблице две строки данных плюс заголовок: table_rows не включает строку с Этап и Статус.

В поле html находится строка с тегами, а не отдельный файл. Буквы кириллицы должны читаться нормально. Вложенный HTML в JSON экранируется обычными правилами JSON; это не означает потерю разметки.

Шаг 10. Проверьте отключённую таблицу и неверное поле

В Render HTML временно выключите Tables Support. После полного запуска ожидается headers_mismatch в Draft Preview: Markdown-таблица не превратилась в ожидаемые th. Верните Tables Support On.

Теперь смените Destination Key с html на data. Ожидается html_fields_mismatch в Check HTML. Верните html. Если выбрать result.html, проверка тоже отклонит результат: путь может быть допустим для узла, но не соответствует договору нашей схемы.

Для проверки Expression временно введите в Markdown обычную Fixed-строку $json.markdown. В конце ожидается headings_mismatch: нужного заголовка нет. Затем восстановите выражение {{ $json.markdown }} в режиме Expression.

Шаг 11. Проверьте уровень заголовка и пункты списка

Временно задайте Header Level Start: 1. Учебная сводка станет H1, а в headings HTML-узел найдёт другие элементы. Проверка должна завершиться с headings_mismatch. Верните 2.

В Sample Draft временно уберите строку 2. Согласовать текст. Полный запуск должен остановиться с steps_mismatch. Если заменить 1. и 2. на дефисы, получится ненумерованный список, и ожидается та же ошибка: селектор ol > li его не принимает. Верните два исходных пункта.

Это проверки конкретного требования к черновику, а не утверждение, что другие уровни заголовков или ненумерованные списки всегда неправильны. Они неправильны только для нашего ожидаемого результата.

Шаг 12. Восстановите и сохраните рабочую схему

Верните исходный текст, Markdown to HTML, Expression, поле html, Header Level Start 2 и Tables Support On. В Inspect HTML сохраните JSON Property html и все Return Array On. Сохраните workflow, выполните заново и сверьте результат с шагом 9.

Если дальше нужен HTML для CMS, сначала определите разрешённые теги, атрибуты, схемы и домены ссылок, затем добавьте подходящую серверную очистку и редакторское подтверждение. Не подключайте публикацию лишь потому, что текст преобразовался в HTML или workflow завершился без ошибок.

Если результат отличается

Ошибка Что проверить Ожидаемая настройка
markdown_text_required Тип и содержимое исходника Непустая строка markdown
html_fields_mismatch Куда записан результат Destination Key html
headings_mismatch Выражение и уровни заголовков Expression, Header Level Start 2
headers_mismatch Преобразование таблицы Tables Support On
steps_mismatch Нумерацию и число пунктов Два пункта внутри ol

Если HTML-узел сообщает об отсутствующем поле, проверьте JSON Property html. Если массив headings оказался строкой, включите Return Array. Не убирайте итоговое сравнение только ради статуса success: сначала найдите, чем реальный результат отличается от expected.

Граница между преобразованием и безопасностью

Markdown-узел в проверенном сценарии сохраняет встроенный HTML. В отдельном тесте обычный элемент aside прошёл через преобразование без удаления и не помешал сравнению ожидаемых полей. Значит, наша проверка видит нужные элементы, но не запрещает всё остальное. Это не санитайзер и не защита от XSS.

Для чужого текста нужны отдельные правила безопасного вывода. Не вставляйте непроверенный HTML в DOM, письмо или CMS и не превращайте ссылки из входа в автоматические HTTP-запросы. В этой инструкции браузерное исполнение HTML, проверка ссылок, атрибутов и вредоносных конструкций не проводились.

Извлечь поля из HTML с проверкой структуры

Остановить workflow при неверных данных

Прочитать RSS и подготовить данные для дайджеста

Что проверено редакцией

16 октября 2026 года workflow проверен на n8n 2.42.4 в отдельном Docker-экземпляре ручными запусками API. Успешно пройдена 31 проверка: основной пример, переносы и пробелы, лишнее входное поле, встроенный HTML, ID заголовков, некорректный вход, размеры, число items, Destination Key и Expression, уровни заголовков, таблицы, список, ссылка, выделение, поле HTML и Return Array. Сверены точный HTML основного примера, извлечённые массивы, исходник, статус предпросмотра и отсутствие успешного результата при ошибках. Использованы штатные Markdown 1 с Showdown и HTML 1.2, настройки сверены с документацией и кодом узлов. Редакторские клики, Cloud, браузерный рендер, безопасность HTML, CMS, публикация и большие документы не тестировались. Учебный контейнер остановлен, существующие workflows не менялись. Обложка — редакционная AI-иллюстрация.

Частые вопросы

Markdown в n8n сам напишет статью?

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

Почему таблица осталась строками с вертикальными чертами?

Проверьте Tables Support и строку-разделитель между заголовком и данными. В учебном примере таблица должна дать два th и четыре td.

Зачем Header Level Start 2?

Чтобы исходный # стал H2, если фрагмент будет частью страницы с собственным H1. Для другого места назначения уровни нужно согласовать отдельно.

Почему результат выглядит как строка с тегами?

Workflow возвращает HTML в поле JSON и не открывает его как веб-страницу. Браузерный предпросмотр, файл и публикация — отдельные действия, которых здесь нет.

Можно сразу вставить результат в сайт?

Не для чужого непроверенного текста. Преобразование Markdown не заменяет очистку HTML, правила ссылок, безопасный вывод и ручное подтверждение публикации.

Дальше по теме

Похожие материалы