RSS — это лента записей с заголовками, датами и ссылками. n8n может прочитать её без копирования новостей вручную. Но прочитать ленту ещё не значит получить готовый дайджест: записи бывают без даты, с повторными ID или в неожиданном порядке. Соберём проверяемый предпросмотр, который останавливается на таких ошибках.
Результат инструкции
Получим три записи из собственной учебной RSS-ленты ezGPT. Штатный RSS Read превратит их в items, Code проверит обязательные поля, Sort поставит более новые записи первыми. Последний Code соберёт один JSON: status needs_review, count 3, массив entries, approved false и action_performed false.
Это подготовка данных для ручной редакторской проверки, не готовая новостная статья и не AI-выжимка. Модель, API-ключ и платная подписка не нужны. Записи вымышлены, даты зафиксированы для воспроизводимости. Адреса example.invalid внутри записей — намеренные учебные заглушки: открывать их бессмысленно. RSS Read загружает XML ленты, но не переходит по ссылкам каждой новости.
Что понадобится
- Работающий n8n и доступ к созданию отдельного workflow. Проверено на n8n 2.42.4, RSS Read версии 1.2 и Sort версии 1.
- Доступ из n8n к HTTPS-файлу учебной ленты. Сервер, на котором работает n8n, должен сам видеть ezgpt.ru; открытая страница в вашем браузере этого не доказывает.
- Новый учебный workflow. Рабочие рассылки, каналы и сайт к нему подключать не нужно.
Установить n8n локально через Docker
Шаг 1. Импортируйте готовую схему
Скачать n8n-rss-digest-preview.json
Создайте новый workflow. В меню редактора выберите Import from File и загрузите JSON. Файл не содержит credentials и ID чужого workflow, схема неактивна. После импорта должны появиться пять соединённых узлов:
Manual Trigger → Read Demo Feed → Check Feed → Sort News → Digest Preview
Read Demo Feed — штатный RSS Read. Check Feed и Digest Preview — Code. Sort News — Sort. Если собираете вручную, сохраните эти имена: итоговая проверка обращается к Check Feed по имени. Не добавляйте Schedule Trigger, отправку писем и публикацию, пока проверяете прототип.
Шаг 2. Проверьте учебный XML
Открыть учебную RSS-ленту ezGPT
В XML есть rss version 2.0, channel и три item. У каждой записи заданы guid, title, link, pubDate и description. В guid стоит isPermaLink false: это строковый идентификатор demo-001, а не адрес страницы. Вторая запись в файле новее первой — это специально, чтобы проверить сортировку.
<item>
<guid isPermaLink="false">demo-002</guid>
<title>Учебная новость: проверка RSS</title>
<link>https://example.invalid/rss-demo-002</link>
<pubDate>Fri, 16 Oct 2026 08:00:00 GMT</pubDate>
<description>Проверяем заголовок и дату.</description>
</item>
Если браузер показывает XML как дерево или предлагает скачать файл, это нормально. Если открывается страница входа, HTML ошибки или пустой файл, сначала решите проблему с доступом. Сам пример работает с публичным XML, без авторизации.
Шаг 3. Настройте RSS Read
Откройте Read Demo Feed. В URL вставьте https://ezgpt.ru/storage/article-files/ezgpt-rss-demo.xml. Это адрес самой ленты, не главная страница сайта. В Options оставьте Ignore SSL Issues выключенным. Custom Fields не заполняйте: используем стандартные поля.
В Settings включите Always Output Data только у Read Demo Feed. Пустая RSS-лента иначе может дать ноль items, и следующий Code вообще не запустится. С включённой настройкой до проверки дойдёт пустой объект, который превратим в явную ошибку feed_empty.
Для всех узлов оставьте On Error: Stop Workflow. Continue On Fail, Retry On Fail и Execute Once не включайте. Проблемы сертификата не нужно скрывать отключением проверки TLS. В этом примере не тестируется обход сетевых ограничений или доступ к защищённым лентам.
Шаг 4. Выполните чтение и проверьте поля
Запустите workflow от Manual Trigger и посмотрите Output узла Read Demo Feed. Ожидаются три items, а не один item с массивом news. Первый item должен содержать такие данные:
{
"title": "Учебная новость: проверка RSS",
"link": "https://example.invalid/rss-demo-002",
"pubDate": "Fri, 16 Oct 2026 08:00:00 GMT",
"content": "Проверяем заголовок и дату.",
"contentSnippet": "Проверяем заголовок и дату.",
"guid": "demo-002",
"isoDate": "2026-10-16T08:00:00.000Z"
}
Штатный парсер добавил isoDate с UTC-временем и contentSnippet с текстовым описанием. pubDate остался исходной строкой. В следующем узле используем isoDate, чтобы не сортировать английские названия дней недели. contentSnippet — описание из ленты после удаления разметки, не полный текст статьи и не пересказ от модели.
| Поле RSS Read | Во что преобразуем | Правило примера |
|---|---|---|
| guid | id | Непустая строка, без повторов |
| title | title | Непустой текст, до 200 символов |
| link | link | Учебный адрес example.invalid |
| isoDate | published_at | UTC-строка с датой и временем |
Дополнительно contentSnippet переносится в excerpt: непустая строка не длиннее 1000 символов. Остальные поля парсера в предпросмотр не переносим. Для реальной ленты отсутствие описания может быть допустимым, но здесь договор намеренно строгий.
Шаг 5. Добавьте проверку записей
В Check Feed выберите Mode: Run Once for All Items, Language: JavaScript. Вставьте весь код:
const items = $input.all();
if (!items.length || (items.length === 1 && !Object.keys(items[0].json).length)) {
throw new Error('feed_empty');
}
if (items.length > 20) throw new Error('feed_too_many_items');
const ids = new Set();
const links = new Set();
return items.map(({ json: row }) => {
const text = key => {
const value = row[key];
if (typeof value !== 'string' || !value.trim()) throw new Error(key + '_required');
return value.trim();
};
const id = text('guid');
const title = text('title');
const link = text('link');
const date = text('isoDate');
const excerpt = text('contentSnippet');
if (title.length > 200 || excerpt.length > 1000) throw new Error('feed_text_too_long');
if (!link.startsWith('https://example.invalid/') || !/^rss-demo-\d{3}$/.test(link.slice('https://example.invalid/'.length))) {
throw new Error('feed_link_not_allowed');
}
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(date) || !Number.isFinite(Date.parse(date))) {
throw new Error('feed_date_invalid');
}
if (ids.has(id) || links.has(link)) throw new Error('feed_duplicate');
ids.add(id);
links.add(link);
return { json: { id, title, link, published_at: date, excerpt } };
});
Проверка принимает от 1 до 20 записей. Двадцать — наше учебное ограничение после чтения, а не предел RSS Read и не защита загрузки от большого XML. Заголовки и описания очищаются от внешних пробелов. Повтор ID или ссылки завершает запуск с feed_duplicate; записи не склеиваются и не удаляются молча.
Разрешён только точный учебный префикс https://example.invalid/ и путь rss-demo- с тремя цифрами. В реальной задаче замените правило согласованным списком доменов источников, а не убирайте проверку целиком. Этот Code не переходит по ссылкам.
Даты берутся из isoDate, уже созданного парсером. Код проверяет форму UTC-строки и возможность прочитать её, но не доказывает правильность исходной даты издателя. Некорректную календарную дату некоторые парсеры могут нормализовать. Для реальных новостей отдельно проверяют исходную дату, часовой пояс и актуальность.
Шаг 6. Отсортируйте новости
В Sort News выберите Type: Simple. Добавьте два поля в таком порядке: published_at — Descending, затем id — Ascending. Остальные настройки оставьте исходными.
Сначала сравниваются даты: более поздняя идёт выше. При одинаковой дате сравнивается ID, чтобы результат не зависел от исходного порядка XML. Здесь даты приведены парсером к одной UTC-форме. Не подставляйте вместо published_at исходную строку pubDate или даты с разными смещениями без нормализации.
| Позиция | ID | published_at |
|---|---|---|
| 1 | demo-001 | 2026-10-16T09:00:00.000Z |
| 2 | demo-002 | 2026-10-16T08:00:00.000Z |
| 3 | demo-003 | 2026-10-15T15:00:00.000Z |
Выполните узел и сверьте порядок с таблицей. Это отбор по времени публикации внутри файла, не фильтр «за последние сутки». Три записи останутся в результате и завтра: текущая дата в схеме не используется.
Шаг 7. Соберите предпросмотр
В Digest Preview выберите Run Once for All Items, JavaScript и вставьте:
const source = $('Check Feed').all().map(item => item.json);
const entries = $input.all().map(item => item.json);
const expected = [...source].sort((a, b) => b.published_at.localeCompare(a.published_at) || a.id.localeCompare(b.id));
if (JSON.stringify(entries) !== JSON.stringify(expected)) throw new Error('feed_order_mismatch');
return [{ json: {
status: 'needs_review',
count: entries.length,
entries,
approved: false,
action_performed: false,
} }];
Последний узел заново вычисляет ожидаемый порядок по проверенным записям и сравнивает его с выходом Sort. Так ошибку Descending/Ascending не получится скрыть обычным успешным статусом. В результат переносится весь проверенный список, сокращения до топ-3 здесь нет.
needs_review и approved false — поля данных для человека, а не действующий механизм согласования. В этой схеме физически нет узла отправки или публикации. Перед добавлением таких действий нужны отдельная проверка прав, подтверждение редактора и журнал результата.
Шаг 8. Запустите всю цепочку
Нажмите Execute Workflow. После RSS Read, Check Feed и Sort News должно быть по три items. Digest Preview возвращает один item с таким JSON:
{
"status": "needs_review",
"count": 3,
"entries": [
{
"id": "demo-001",
"title": "Учебная новость: текст & данные",
"link": "https://example.invalid/rss-demo-001",
"published_at": "2026-10-16T09:00:00.000Z",
"excerpt": "Это вымышленная запись, не новость редакции."
},
{
"id": "demo-002",
"title": "Учебная новость: проверка RSS",
"link": "https://example.invalid/rss-demo-002",
"published_at": "2026-10-16T08:00:00.000Z",
"excerpt": "Проверяем заголовок и дату."
},
{
"id": "demo-003",
"title": "Учебная новость: черновик дайджеста",
"link": "https://example.invalid/rss-demo-003",
"published_at": "2026-10-15T15:00:00.000Z",
"excerpt": "Готовим данные без отправки и публикации."
}
],
"approved": false,
"action_performed": false
}
Проверьте count 3, порядок demo-001 → demo-002 → demo-003, слово «данные» и амперсанд в первом заголовке. approved и action_performed должны быть логическими false, не строками. Ссылки сохранились как данные; workflow не проверяет их доступность и не получает полные статьи.
Шаг 9. Добавьте временный узел для теста
Чтобы проверить ошибки, не меняя публичный XML, добавьте Code с именем Test Feed между Read Demo Feed и Check Feed. Соедините цепочку через него, выберите Run Once for All Items и JavaScript. Вставьте:
const scenario = 'empty';
const rows = $input.all().map(item => ({ ...item.json }));
if (scenario === 'empty') return [{ json: {} }];
if (scenario === 'single') return [{ json: rows[0] }];
if (scenario === 'title') rows[0].title = '';
if (scenario === 'duplicate') rows[1].guid = rows[0].guid;
return rows.map(json => ({ json }));
Test Feed нужен только для диагностики. Он изменяет копию items текущего выполнения, не XML на сайте. В готовом скачиваемом workflow этого узла нет. В строке scenario переключайте только указанные значения и каждый раз запускайте всю цепочку от начала.
Шаг 10. Проверьте пустую ленту и одну запись
Оставьте scenario empty. Ожидается feed_empty в Check Feed и отсутствие успешного Digest Preview. Это имитация пустого объекта, который Always Output Data отдаёт после действительно пустой ленты; она не проверяет сетевую доступность источника.
Замените empty на single. После запуска ожидаются count 1 и только demo-002: это первая запись исходного XML, а не самая новая из всех трёх. Сначала Test Feed оставил одну запись, и только затем Sort отсортировал оставшееся. Не путайте такое ограничение до сортировки с выбором самой новой новости.
Шаг 11. Проверьте пропуск, повтор и неправильный порядок
В Test Feed задайте scenario title. Ожидается title_required. Затем задайте duplicate: ожидается feed_duplicate. В обоих случаях успешного итогового предпросмотра не должно быть. Если получили JSON, проверьте, действительно ли Test Feed стоит перед Check Feed.
Теперь удалите Test Feed и напрямую соедините Read Demo Feed с Check Feed. В Sort News временно смените published_at с Descending на Ascending. Digest Preview должен остановиться с feed_order_mismatch. Верните Descending и убедитесь, что схема снова даёт результат из шага 8.
Шаг 12. Сохраните рабочий вариант
Проверьте, что осталось пять исходных узлов, URL снова указывает на учебный XML, Always Output Data включён только у RSS Read, Sort настроен по двум полям, а ошибок продолжения нет. Сохраните и выполните workflow ещё раз.
Повторный запуск вернёт те же три ID. RSS Read здесь читает файл целиком и не помнит, какие новости вы уже видели. Для реального ежедневного дайджеста понадобится отдельное хранилище обработанных ID и статусов. Добавлять расписание до такого контроля рано: оно повторит чтение, но само не решит задачу повторов.
Если результат отличается
| Симптом | Причина | Что проверить |
|---|---|---|
| feed_empty | Лента пуста или пришёл пустой объект | Output RSS Read и Always Output Data |
| title_required | Нет непустого заголовка | title текущей записи |
| isoDate_required | Парсер не получил пригодную дату | pubDate исходного XML |
| feed_duplicate | Повтор ID или ссылки в пакете | guid и link всех записей |
| feed_order_mismatch | Неверный порядок после Sort | published_at Descending, id Ascending |
Сетевая ошибка, ответ HTTP 503 или сломанный XML останавливаются ещё в Read Demo Feed. Проверяйте сообщение именно этого узла. Включение Ignore SSL Issues не исправит неверный адрес, HTML вместо RSS или ошибку сервера. On Error: Continue может превратить проблему чтения в обычный объект error — здесь этот режим не используется.
Что не делает этот прототип
Схема не проверяет факты новостей, не оценивает источник, не извлекает полную статью и не формирует связный текст дайджеста. Описание из RSS может быть коротким или устаревшим. Наличие даты и ссылки не делает сообщение достоверным. Перед публикацией нужны первоисточники и редакторская проверка.
Внешний RSS — недоверенный ввод. Не передавайте описание в AI как системную инструкцию, не исполняйте код из ленты и не вставляйте исходный HTML в страницу без безопасного вывода. Авторизация, Atom, нестандартные поля, большие ленты, тайм-ауты, расписание и хранение истории требуют отдельной проверки.
Разобрать XML в JSON с проверкой структуры
Извлечь поля из HTML без перемешивания карточек
Остановить workflow при неверных данных
Что проверено редакцией
16 октября 2026 года схема проверена на n8n 2.42.4 в отдельном Docker-экземпляре ручными запусками API. Все 29 тестов прошли: основной публичный XML, одна запись, обратный порядок, HTML в описании, равные даты, пустая и слишком длинная лента, отсутствие/пустота заголовка, ID, ссылки, даты и описания, HTTP и чужой домен в ссылке, повторы ID и ссылки, длинные тексты, сломанный XML, HTML вместо RSS, HTTP 503, неправильная сортировка, пустая лента без Always Output Data и четыре режима Test Feed. Основной тест использует точный скачиваемый workflow и опубликованный учебный XML. Остальные варианты — собственный локальный HTTP-источник и диагностический Code. Сверены порядок, поля, UTF-8, амперсанд, статус предпросмотра и отсутствие успешного результата при ошибках. Отдельно подтверждено: без Always Output Data пустая лента заканчивается без запуска проверочного Code. Настройки RSS Read 1.2 сверены с официальной документацией и кодом узла. Редакторские клики, Cloud, реальные новостные издания, Atom, авторизация, история между запусками, большие объёмы и полный аудит безопасности не тестировались. Учебный контейнер остановлен, существующие workflows не менялись. Обложка — редакционная AI-иллюстрация.
Частые вопросы
RSS Read сам напишет дайджест?
Нет. Он читает записи ленты. В примере получается проверенный JSON для редакторского предпросмотра, без AI-пересказа, проверки фактов и публикации.
Зачем Always Output Data у RSS Read?
При пустой ленте следующий узел иначе может не запуститься. С настройкой он получает пустой объект, который Check Feed отклоняет с feed_empty. Она не превращает сетевую ошибку в успешное чтение.
Почему нельзя открыть ссылки example.invalid?
Это намеренные заглушки вымышленной ленты. Рабочий адрес в примере — URL самого XML на ezgpt.ru. Workflow не переходит по ссылкам записей.
Повторный запуск не добавит те же новости?
В этом прототипе повторятся те же записи: постоянного хранилища истории нет. Для реальной сборки сохраняйте ID, источник и статус обработки отдельно; проверка дублей здесь работает только внутри одного пакета.
Можно сразу заменить URL своей лентой?
Сначала проверьте её поля и договор данных. В Code задан учебный домен example.invalid, обязательны guid, isoDate и contentSnippet. Для другого источника нужно осознанно настроить проверки и повторить тесты.