Конвертация блокнота Jupyter в Markdown: оставить текст, выбросить обёртку
Блокнот — это два документа под одним именем файла. Есть то, что вы написали: заголовки, пояснения, код, напечатанный результат, ради которого всё и затевалось. И есть JSON-конверт, в котором формат всё это хранит. Откройте .ipynb в текстовом редакторе — и вы увидите конверт: каждая строка текста упакована в отдельный строковый литерал, "execution_count": 7 у каждой ячейки, пустые объекты "metadata": {} повсюду, а где-то в середине — четверть мегабайта base64, который отрисовывается в одну маленькую диаграмму рассеяния.
Конвертация в Markdown выбрасывает конверт и оставляет документ. Ваши markdown-ячейки и так были Markdown — они проходят насквозь без изменений. Ячейки с кодом становятся огороженными блоками с меткой языка ядра: это та форма, которую уже понимают любая модель, любой README и любой генератор статических сайтов. Перетащите .ipynb выше — конвертация произойдёт прямо в этой вкладке браузера, файл никуда не отправляется.
Где на самом деле лежит вес
Принято думать, что большой блокнот большой из-за кода. Почти никогда. Вес сидит в четырёх местах, по убыванию:
- Картинки вывода в base64. Каждый
plt.show()записывает отрисованный PNG прямо в файл в виде base64. Одна фигура — это обычно 200–400 КБ символов. Блокнот с дюжиной графиков состоит в основном из графиков. Читателю текста они не говорят ровным счётом ничего. - HTML-представления датафреймов. Pandas выдаёт для одного и того же результата и
text/plain, иtext/html. HTML-версия тащит инлайновые стили и<table>с тегом на каждую ячейку, поэтому она бывает в двадцать раз больше текстовой, говоря ровно то же самое. - Кадры прогресс-баров. Цикл с tqdm пишет новую строку на каждое обновление, разделяя их возвратами каретки. В файле оседают сотни копий полоски, которая на экране показывалась один раз.
- Метаданные каждой ячейки. По отдельности мелочь, вместе — нет: идентификаторы ячеек, счётчики выполнения, флаги свёрнутости и пустые объекты метаданных на несколько сотен ячеек складываются в заметный объём.
Конвертер выше разбирается с каждым из этих пунктов. Медиа-нагрузка подсчитывается и заменяется одной строкой-заглушкой, чтобы было видно, где стояла фигура. Если результат предлагает и текст, и HTML, побеждает текст. Последовательности с возвратом каретки схлопываются до финального состояния строки. Метаданные выбрасываются целиком. Инструмент сообщает, что именно он убрал, — экономия видна, а не декларируется.
Почему огороженные блоки важнее, чем кажется
Самое полезное, что Markdown делает для блокнота, — явно размечает границу между текстом и кодом. В сыром JSON эта граница существует только как поле "cell_type" несколькими строками выше содержимого. Если сплющить файл небрежно, объяснение и код, который оно описывает, сливаются, и читателю — человеку или модели — приходится догадываться, где что, по одному лишь синтаксису.
Ограждение с меткой языка снимает эту загадку. Заодно оно переносит дальше язык ядра: конвертер читает language_info из метаданных блокнота, а при его отсутствии — kernelspec, поэтому блокнот на R или Julia получает метку R или Julia, а не тихо записывается в Python. Вывод получает собственный блок без метки под породившей его ячейкой, с простой строкой Output: впереди: причинно-следственная связь остаётся видимой, и при этом не выдумывается синтаксис, которого в Markdown нет.
Трейсбеки стоит оставлять
Есть соблазн вычистить вывод с ошибками вместе со всем остальным. Обычно это ошибка. Если вы отдаёте блокнот модели и спрашиваете, почему падает ячейка, трейсбек — это и есть весь вопрос. Неприятным в сыром виде его делает не содержимое, а ANSI-коды цвета, в которые IPython его заворачивает: escape-последовательности, которые в терминале выглядят как цвет, а везде ещё — как мусор вида ESC[0;31m.
Эти последовательности убираются, а текст трейсбека остаётся. Длинные сокращаются с середины, а не с конца: в глубоком стеке полезны первые и последние кадры, а двести строк внутренностей библиотек между ними — ровно то, что вы и так пролистали бы.
Блокноты внутри выгрузки репозитория
Та же конвертация работает внутри инструментов для GitHub, GitLab и локальной папки на этом сайте. Отметьте .ipynb в репозитории — и он попадёт в результат читаемыми ячейками, а не стеной JSON. Это важнее, чем звучит: в репозитории по анализу данных именно в блокнотах часто живут рассуждения, а до сих пор это были файлы, которые приходилось снимать с галочки, чтобы вывод оставался пригодным.
В этом контексте лимит на вывод жёстче, чем на этой странице: выгрузка репозитория — это контекст для модели, а вывод ячеек в пересчёте на токен — самая малоценная его часть. В конвертированном тексте остаётся пометка о том, сколько было убрано, так что ничего не пропадает молча.
Старые блокноты тоже открываются
Четвёртая версия формата держит ячейки в массиве cells верхнего уровня. Третья, которая до сих пор разбросана по публичным репозиториям начала 2010-х, прячет их на уровень глубже внутри worksheets и называет поле с исходником input, а не source. Читаются обе формы. И тип вывода pyout из третьей версии — там, где в четвёртой стоит execute_result, — тоже.
Двух вещей здесь сознательно не делают. Виджеты блокнота — интерактивные ползунки и графики на ipywidgets — хранят состояние в отдельном блоке метаданных и в текст не превращаются ни во что осмысленное; заглушка просто сообщает, что виджет здесь был. И порядок выполнения ячеек сохраняется таким, каким он записан в файле, без сортировки по счётчику выполнения: блокнот читают в том порядке, в котором он написан.
Ничего не загружается на сервер
В блокнотах оседает то, о чём потом забывают: API-ключ, вставленный в ячейку во время отладки, строка подключения к базе, кусок боевых данных, распечатанный ради проверки джойна. Конвертация выполняется на JavaScript в этой вкладке. Здесь нет шага загрузки, нет копии на сервере и нечего потом просить удалить. Закройте вкладку — и всё исчезло.
Если вам нужен простой текст вместо Markdown — без ограждений, без структуры, только текст и код, — это делает конвертер блокнотов в текст, и выбранный файл переедет туда вместе с вами.