Convertir un notebook Jupyter en Markdown — gratuit et privé

Transformez un notebook .ipynb en Markdown propre directement dans votre navigateur. Conserve le texte et le code, retire les images en base64 et les métadonnées d'exécution qui alourdissent le JSON brut.

Notebook Jupyter en Markdown : garder le récit, jeter l'emballage

Un notebook, ce sont deux documents sous un seul nom de fichier. Il y a ce que vous avez écrit — titres, explications, code, le résultat imprimé qui faisait la démonstration — et il y a l'enveloppe JSON dans laquelle le format le range. Ouvrez un .ipynb dans un éditeur de texte et vous voyez l'enveloppe : chaque ligne de prose isolée dans sa propre chaîne, "execution_count": 7 sur chaque cellule, des objets "metadata": {} vides partout, et quelque part au milieu un quart de mégaoctet de base64 qui s'affiche sous la forme d'un petit nuage de points.

Convertir en Markdown jette l'enveloppe et garde le document. Vos cellules markdown étaient déjà du Markdown : elles passent telles quelles. Les cellules de code deviennent des blocs délimités étiquetés avec le langage du noyau, la forme que comprennent déjà tous les modèles, tous les README et tous les générateurs de sites statiques. Déposez un .ipynb ci-dessus et la conversion se fait dans cet onglet : le fichier n'est jamais envoyé.

Où se trouve vraiment le poids

On imagine qu'un gros notebook est gros à cause du code. Ce n'est presque jamais le cas. Le poids se loge à quatre endroits, par ordre décroissant :

  • Les images de sortie en base64. Chaque plt.show() écrit le PNG rendu dans le fichier, en base64. Une figure pèse couramment 200 à 400 Ko de caractères. Un notebook avec une douzaine de graphiques est surtout constitué de graphiques. Rien de tout cela ne signifie quoi que ce soit pour qui lit le texte.
  • Les représentations HTML des dataframes. Pandas émet à la fois text/plain et text/html pour le même résultat. La version HTML transporte des styles en ligne et un <table> avec une balise par cellule : elle peut peser vingt fois la version en texte brut tout en disant exactement la même chose.
  • Les images successives des barres de progression. Une boucle tqdm écrit une nouvelle ligne à chaque rafraîchissement, séparée par des retours chariot. Le fichier finit avec des centaines de copies d'une barre qui ne s'est affichée qu'une fois.
  • Les métadonnées par cellule. Négligeables une à une, pas collectivement : identifiants de cellule, compteurs d'exécution, indicateurs de repli et objets de métadonnées vides, sur quelques centaines de cellules, cela finit par compter.

La conversion ci-dessus traite chacun de ces cas. Les charges utiles multimédias sont comptées puis remplacées par une seule ligne de repère, pour que vous voyiez encore où se trouvait une figure. Quand un résultat propose à la fois du texte brut et du HTML, c'est le texte brut qui gagne. Les séquences de retour chariot se réduisent à l'état final de la ligne. Les métadonnées disparaissent complètement. L'outil rapporte ce qu'il a retiré : l'économie est visible plutôt qu'affirmée.

Pourquoi les blocs délimités comptent plus qu'il n'y paraît

Ce que Markdown apporte de plus utile à un notebook, c'est de marquer explicitement la frontière entre prose et code. Dans le JSON brut, cette frontière n'existe que sous la forme d'un champ "cell_type" situé plusieurs lignes au-dessus du contenu. Aplati sans précaution, une explication et le code qu'elle décrit se rejoignent, et il faut deviner lequel est lequel à la seule syntaxe — que l'on soit humain ou modèle.

Un bloc délimité étiqueté avec le langage supprime cette devinette. Il conserve aussi le langage du noyau : le convertisseur lit language_info dans les métadonnées du notebook, avec repli sur le kernelspec, si bien qu'un notebook R ou Julia est étiqueté R ou Julia plutôt que discrètement rebaptisé Python. Les sorties reçoivent leur propre bloc non étiqueté sous la cellule qui les a produites, précédé d'une simple ligne Output:, ce qui garde le lien de cause à effet visible sans inventer une syntaxe que Markdown ne possède pas.

Les traces d'erreur méritent d'être gardées

On est tenté de supprimer les erreurs en même temps que le reste. C'est en général le mauvais choix. Si vous confiez un notebook à un modèle en lui demandant pourquoi une cellule échoue, la trace d'erreur est la question tout entière. Ce qui rend les traces désagréables à l'état brut n'est pas leur contenu mais les codes de couleur ANSI dont IPython les enrobe — des séquences d'échappement qui s'affichent en couleur dans un terminal et en charabia ESC[0;31m partout ailleurs.

Ces séquences sont retirées et le texte de la trace conservé. Les plus longues sont raccourcies par le milieu plutôt que par la fin, car dans une pile profonde les appels utiles sont les premiers et les derniers, et les deux cents lignes d'internes de bibliothèque qui les séparent sont précisément celles que vous auriez sautées.

Les notebooks dans un export de dépôt

La même conversion tourne à l'intérieur des outils GitHub, GitLab et dossier local de ce site. Sélectionnez un .ipynb dans un dépôt et il arrive dans la sortie sous forme de cellules lisibles plutôt que d'un mur de JSON. Cela compte plus qu'il n'y paraît : dans un dépôt de science des données, les notebooks sont souvent là où loge le raisonnement, et jusqu'ici c'étaient les fichiers qu'il fallait décocher pour garder une sortie utilisable.

Dans ce contexte, la limite de sortie est plus serrée que sur cette page, car un export de dépôt sert de contexte à un modèle et les sorties de cellule en sont la partie la moins rentable par token. Une note dans le texte converti indique combien a été retiré : rien ne disparaît en silence.

Les vieux notebooks fonctionnent toujours

La version 4 du format range les cellules dans un tableau cells de premier niveau. La version 3, encore éparpillée dans les dépôts publics du début des années 2010, les imbrique un niveau plus bas dans worksheets et nomme le champ source input plutôt que source. Les deux formes sont lues. Le type de sortie pyout de la version 3, là où la version 4 dit execute_result, l'est aussi.

Deux choses ne sont volontairement pas tentées. Les widgets de notebook — curseurs et graphiques interactifs adossés à ipywidgets — rangent leur état dans un bloc de métadonnées séparé et ne donnent rien d'utile en texte ; le repère vous signale qu'il y en avait un. Et l'ordre d'exécution des cellules est conservé tel qu'il apparaît dans le fichier, sans tri par compteur d'exécution : l'ordre dans lequel on lit un notebook est celui dans lequel il est écrit.

Rien n'est envoyé

Les notebooks transportent des choses qu'on oublie : une clé d'API collée dans une cellule pendant un débogage, une chaîne de connexion à une base, une tranche de données de production imprimée pour vérifier une jointure. La conversion tourne en JavaScript dans cet onglet. Il n'y a pas d'étape d'envoi, pas de copie sur un serveur et aucune suppression à demander ensuite. Fermez l'onglet et c'est fini.

Si vous préférez du texte brut au Markdown — sans blocs, sans structure, juste la prose et le code — le convertisseur de notebook en texte produit cela, et le fichier que vous avez sélectionné vous suit.