Kurs
In der Data Science musst du häufig Berichte über deine Arbeit erstellen, um Ergebnisse für Entscheider oder andere nicht-technische Stakeholder aufzubereiten. Dein Jupyter Notebook in ein stabiles PDF- oder HTML-Dokument zu konvertieren, ist für Kolleginnen und Kollegen ohne Python oder installiertes Jupyter deutlich einfacher nutzbar. Python verwendet dafür die Bibliothek nbconvert und die Template-Engine Jinja2. Templates legen fest, wie ein Dokument im Web oder in einem anderen Ausgabeformat dargestellt wird. Zu verstehen, wie sich Templates anpassen lassen, hilft dir, deine Notebooks als ansprechende Reports aufzubereiten.
Jupyter Notebooks ermöglichen es dir, Python-Code direkt im Browser auszuführen. Wenn du mehr über Python lernen möchtest, wirf einen Blick auf unseren kostenlosen Kurs Intro to Python for Data Science.
In diesem Tutorial behandelst du unter anderem:
- Templates und den
Jinja2-Templatedesigner. - Rendering von Templates und Vererbung.
- Wie du
nbconvertzum Export deiner Notebooks nutzt. - Syntax und Struktur, um die Jupyter-Standardtemplates für den HTML-Export zu erweitern.
- Unterschiede beim Export in LaTeX- und PDF-Formate.
What are Templates?
Aus dem Python-Wiki: "Templating, insbesondere Web-Templating, ist eine Möglichkeit, Daten in unterschiedlichen Formen darzustellen ... Häufig bestehen Templating-Lösungen aus einem Dokument (dem Template) und Daten. Templates ähneln meist der finalen Ausgabe, enthalten aber Platzhalter statt echter Daten"
Das Jupyter Notebook lässt sich bequem über File -> Download As (in JupyterLab: Export Notebook As) exportieren. Diese Option nutzt die Standardtemplates aus der Jupyter-Umgebung. Jinja2 ist eine mächtige Templating-Sprache in Python, um Blöcke und Typografie zu definieren. Templates besitzen Abschnitte, sogenannte Tags, die steuern, wie Eingabedaten gerendert werden. Beim Rendern ersetzen die Daten die Variablen bzw. Ausdrücke.
Die Trennzeichen für die unterschiedlichen Template-Bestandteile sind:
{% ... %}für Statements{{ ... }}für Ausdrücke, die in die Ausgabe geschrieben werden{# ... #}für Kommentare, die nicht in der Ausgabe erscheinen
Hier ein kurzes Beispiel für ein Jinja-Template (aus der Jinja2-Dokumentation):
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage </title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid">you </a>.
{% endblock %}
</div>
</body>
</html>
Du siehst, dass ein Großteil wie ein normales HTML-Dokument aussieht. Jinja benötigt nur wenige zusätzliche Zeilen, um den Code als Template zu interpretieren. Der Block {% block head %} definiert den Kopfbereich des HTML-Dokuments und wie dieses Template sein Styling erweitert. Der Abschnitt {% block title %} bestimmt, wo der übergebene Titel erscheint. Und {% endblock %} schließt jeweils den zugehörigen Template-Block.
Jetzt bist du bereit, mit Jinja eigene, individuelle Templates zu erstellen!
Einführung in Jinja
Jinja ist eine Template-Engine, mit der du festlegst, wie deine Dokumente dargestellt werden. In diesem Tutorial konzentrierst du dich darauf, dein Jupyter Notebook mithilfe von Jinja-Templates zu exportieren.
Importiere zuerst aus Jinja2 die Klasse Template:
from jinja2 import Template
Grundlagen des Renderns mit Templates
Daten in Jinja-Templates zu rendern, ist unkompliziert. Mit Klammern und Variablennamen gibst du Daten im Template aus:
myTemplate = Template("My template is {{ something }}!")
myTemplate.render(something="awesome")
'My template is awesome!'
Der Aufruf .render(input) zeigt das Template in deinem Notebook an, wobei dein Input die generische Variable {{ something }} ersetzt. Das ist ein Beispiel für die Nutzung von expression in Jinja.
Noch dynamischer wird es mit statements in Python, die Jinja interpretiert.
myFancyTemplate = Template("Let's count to 10: {% for i in range(11) %}{{i}} " "{% endfor %}")
myFancyTemplate.render()
"Let's count to 10: 0 1 2 3 4 5 6 7 8 9 10 "
Beachte: Du definierst eine for-Schleife, und das Template rendert die Zahlen von 0 bis 10 mit Leerzeichen dazwischen. Die statement-Syntax {% expression %} gibt dir viel Flexibilität für eigene Templates.
Template-Vererbung
Um die Standard-Exporte von Jupyter zu erweitern, nutzt du Template-Vererbung. Vererbung bedeutet in der Programmierung, dass ein Kindobjekt Funktionalitäten des Elternobjekts übernimmt und erweitern kann. Ein einfaches Beispiel: Ein Objekt Dog kann bellen, fressen und laufen. Durch Vererbung erstellst du ein Objekt Dalmatian, das all das erbt und z. B. hat Flecken ergänzt. Fürs Templating ist Vererbung besonders nützlich, weil oft nur kleine Anpassungen nötig sind. Wie in anderen Sprachen nutzt Jinja Schlüsselwörter wie extends und super, um auf Definitionen des Elternteils zuzugreifen.
Hier nochmal das Eltern-Template von oben, base.html:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage </title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid">you </a>.
{% endblock %}
</div>
</body>
</html>
Ein Kind-Template könnte so aussehen (aus der Jinja2-Dokumentation):
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
Beachte den Start mit {% extends "base.html" %}. Diese Deklaration sagt der Jinja-Engine, dass dieses Dokument HTML ist und von base.html erbt. So kannst du für die Startseite eigene Eigenschaften festlegen (z. B. eine bestimmte CSS-Farbe). Im head-Block erbt das Kind-Template mit super() zusätzlich die Styles des Eltern-Templates.
nbconvert für den Export von Jupyter Notebooks verwenden
Jetzt, da du Syntax und Vererbung von Templates kennst, lernst du, wie du dein Jupyter Notebook mit nbconvert exportierst und mit Templates anpasst.
Importiere zunächst nbconvert:
import nbconvert
nbconvert erzeugt Ausgaben in verschiedenen Formaten. Dieses Tutorial konzentriert sich auf HTML und LaTeX/PDF. Um die Ausgabe direkt im Notebook anzuzeigen, nutze die IPython-Display-Funktion und --stdout beim Aufruf von nbconvert.
example = !jupyter nbconvert --to html 'Example.ipynb' --stdout
from IPython.display import HTML, display
display(HTML('\n'.join(example)))
[NbConvertApp] Converting notebook Example.ipynb to html

Dieser Ausschnitt eines Notebooks sieht dem ursprünglichen .ipynb sehr ähnlich. Die Ausgabe erscheint als eigenständiges Dokument mit Überschriften und Text wie im Notebook. Codezellen werden in grauen Kästen dargestellt (bekannt als "Notebook-Stil"). Ein sichtbarer Unterschied: Das Jupyter-Standard-HTML-Template zeigt keine "Out:"-Prompts wie im aktiven Notebook.
nbconvert mit einem eigenen Child-Template nutzen
In der Praxis willst du meist die Standard-Exporttemplates von Jupyter erweitern und kleine Designanpassungen vornehmen.
Zum Beispiel entfernt dieses einfache Template Markdown-Zellen aus der Ausgabe (rmMkdwn.tpl):
{% extends 'basic.tpl'%}
{% block markdowncell -%}
{% endblock markdowncell %}
Ein Template in nbconvert anzugeben, erfolgt über die Option --template=:
example = !jupyter nbconvert --to html 'Example.ipynb' --template='rmMkdwn.tpl' --stdout
display(HTML('\n'.join(example)))
[NbConvertApp] Converting notebook Example.ipynb to html
Selbst ein sehr einfaches Template wie rmMkdwn.tpl kann deine Ausgabe stark individualisieren.
Hier ein komplexeres Template, das Zellen rot einrahmt (boxRed.tpl):
{% extends 'full.tpl'%}
{% block any_cell %}
<div style="border:thin solid red">
{{ super() }}
</div>
{% endblock any_cell %}
example = !jupyter nbconvert --to html 'Example.ipynb' --template='boxRed.tpl' --stdout
display(HTML('\n'.join(example)))
[NbConvertApp] Converting notebook Example.ipynb to html 
Wie du siehst, ändern sich hier lediglich rote Rahmen um jede Zelle. Mit super() bleibt das individuelle Styling jeder Zelle aus dem Eltern-Template full.tpl erhalten.
Unterschiede bei Templates für LaTeX- und PDF-Export
Da { } und % in LaTeX Sonderzeichen sind, musst du dort (( )) und * verwenden. Die Standard-LaTeX-Templates heißen base.tplx, article.tplx und report.tplx und entsprechen den LaTeX-Dokumentklassen.
So entfernst du Markdown-Zellen mit einem LaTeX-Child-Template:
((* extends 'article.tplx' *))
((* block markdowncell -*))
((* endblock markdowncell *))
nbconvert kann LaTeX- oder PDF-Ausgaben erzeugen – die PDF-Datei wird aus dem LaTeX-Template kompiliert. Zur Veranschaulichung siehst du hier den PDF-Export mit rmMkdwn.tplx.
!jupyter nbconvert --to pdf 'Example.ipynb' --template='rmMkdwn.tplx'
from IPython.display import IFrame
IFrame('Example.pdf', width=800, height=500)
[NbConvertApp] Converting notebook Example.ipynb to pdf
[NbConvertApp] Support files will be in Example_files/
[NbConvertApp] Making directory Example_files
[NbConvertApp] Writing 16047 bytes to notebook.tex
[NbConvertApp] Building PDF
[NbConvertApp] Running xelatex 3 times: ['xelatex', 'notebook.tex']
[NbConvertApp] Running bibtex 1 time: ['bibtex', 'notebook']
[NbConvertApp] WARNING | bibtex had problems, most likely because there were no citations
[NbConvertApp] PDF successfully created
[NbConvertApp] Writing 16102 bytes to Example.pdf

Beachte: Diese Ausgabe unterscheidet sich deutlich von der HTML-Version. LaTeX übernimmt die erste Markdown-Zelle als Artikeltitel, daher wird sie weiterhin angezeigt. Das Template hat den Untertitel korrekt entfernt (diese Markdown-Zelle wurde also als echte Markdown-Zelle gewertet). Die Dokumentklasse ergänzt das heutige Datum – das stand nicht im ursprünglichen Notebook. Schriftbild und Abstände weichen von HTML ab. Außerdem nutzt die Ausgabe standardmäßig die klassische IPython-Darstellung statt des "Notebook-Stils".
Wenn du eine in Jupyter vorhandene LaTeX-Dokumentklasse erweiterst, achte darauf, welches Format sie mitbringt.
Fazit
Merke dir diese Kernpunkte zu Templates, Jinja2 und nbconvert:
Templates legen fest, wie ein Dokument im Web oder in anderen Formaten dargestellt wird. Jupyter Notebooks nutzen Jinja-Templates für verschiedene Exportformate. Dank Vererbung kannst du Parent- und Child-Templates für ähnliche Seiten mit leicht unterschiedlichem Layout definieren.
Jinja-Templates besitzen statements, expressions und optional comments. Statements ({% statement %}) definieren die Struktur. Expressions ({{ expression }}) füllen das Template mit deinen Daten. Comments ({# comment #}) erscheinen nicht in der Ausgabe.
nbconvert ist eine Python-Bibliothek, mit der du dein Jupyter Notebook in andere Formate wie HTML, LaTeX und PDF konvertierst. nbconvert nutzt Jinja-Templates, um festzulegen, wie Notebooks in diesen Formaten dargestellt werden. Du kannst eigene Templates definieren oder per Vererbung die Standardtemplates erweitern.
Jetzt kannst du loslegen, eigene Templates bauen und wunderschöne Jupyter-Reports exportieren!
Referenzen: