How to blog with Jupyter (IPython) Notebook and Nikola
Last updated on 2015-11-16
I have thought about starting a blog for a few years now and when I recently found some amazing tools which can be used in a data science blog I got so excited that I finally had to start my blog. I have used Python and the NumPy/SciPy stack to analyze and model data for a few years now. I'm also passionate about open data and open science, so I've been excited about how Jupyter Notebooks (a.k.a. IPython Notebooks) can be used to share a mixture of reproducible code and commentation. And with Nikola, it is possible to set up a whole blogging system based on posts written as Notebooks making it possible for anyone to easily try the analysis shown in the post. I got some inspiration from Damian Avila's blog. He has written instructions on blogging with Nikola and IPython. However, the instructions are outdated so I thought I'd start my blog by giving my own tips on setting up Nikola.
Set up the environment
First, install NumPy, SciPy, Matplotlib, IPython and Jupyter (if available) by using the package manager of your system (or use, for instance, Anaconda). I recommend you use Virtualenv and Virtualenvwrapper for setting up a virtual environment for the blog. After installing the packages, run the following command and put it to your shell startup file:
source virtualenvwrapper.sh
Create (and activate) a virtual environment:
mkdir -p /path/to/blog mkvirtualenv --system-site-packages -a /path/to/blog blog
Install Nikola to your virtual environment with some useful extra packages:
pip install nikola[extras]
You can leave the virtual environment with deactivate
and activate it with
workon blog
.
Create the site
Assuming that you are in the directory of your blog project, initialize Nikola:
nikola init .
Edit conf.py and modify POSTS
and PAGES
as follows:
POSTS = ( ("posts/*.ipynb", "blog", "post.tmpl"), ("posts/*.md", "blog", "post.tmpl"), ("posts/*.rst", "blog", "post.tmpl"), ("posts/*.txt", "blog", "post.tmpl"), ) PAGES = ( ("pages/*.md", "", "story.tmpl"), ("pages/*.ipynb", "", "story.tmpl"), ("pages/*.rst", "", "story.tmpl"), ("pages/*.txt", "", "story.tmpl"), )
The first format entries are used as defaults, so with these settings blog posts
would be Jupyter Notebooks and pages would be Markdown files by default if no
format is specified explicitly. Also, the blog posts would be located in
/blog/
and pages (e.g., /about/
) in the root.
Initialize git
I recommend you use git and GitHub because it makes publishing and sharing your blog posts and notebooks easy. The repository can be initialized as:
git init git add * git commit -am "Initial commit"
Push your code to a GitHub repository:
git remote add origin https://github.com/USERNAME/PROJECTNAME.git git push origin -u
Create content
Create your first blog post:
nikola new_post -f ipynb
Create your first page (for instance, "About"):
nikola new_page -f markdown
It isn't necessary to specify -f format
if you want to use the default
format. In order to edit the blog post, launch Jupyter Notebook:
jupyter notebook posts
Note that you may need to use ipython
instead of jupyter
if you have an old
version installed. In order to see your site, use Nikola to automatically
refresh your site at http://127.0.0.1:8000/:
nikola auto
Both of these processes will block your terminal until you stop them. Thus, I recommend opening separate dedicated terminals for the processes so they can run without blocking your working terminal.
Deploy to GitHub pages
Because the compiled website is static, there are lots of options where you can
serve your website. GitHub is a convenient place to deploy, so here are basic
instructions for that, but you could use any other location and define your own
DEPLOY_COMMANDS
(in conf.py
) to be used when running nikola deploy
. For
GitHub pages, you can use nikola github_deploy
after defining a few
parameters:
GITHUB_SOURCE_BRANCH = 'master' GITHUB_DEPLOY_BRANCH = 'gh-pages' GITHUB_REMOTE_NAME = 'origin'
If you are using a custom domain name, add CNAME
to files
directory:
echo mycustomdomain.com > files/CNAME
Deploy the static site to GitHub:
nikola github_deploy
Now your blog should be up and running.
Fine-tune the settings
You can already start blogging. The rest of this post will just provide some ideas on how to improve the settings.
Choose a theme
You can easily customize the look of your blog by creating a new theme. Bootswatch offers free themes which can be easily used in your Nikola project. After choosing the Bootswatch theme, create your custom theme:
nikola bootswatch_theme -n YOUR_THEME_NAME -s BOOTSWATCH_THEME_NAME -p bootstrap3-jinja
I recommend using bootstrap3-jinja
theme as the parent theme but you can
choose any other Nikola theme. But don't use
ipython
theme, that's not supported (thanks to Chris Warrick for pointing this
out). Now modify your conf.py
to use the new custom theme:
THEME = "YOUR_THEME_NAME"
Add social media links
If you want to use social media icons from Font
Awesome, add the following
definition to conf.py
:
EXTRA_HEAD_DATA = '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css">'
Then you can add some social media icons and links, for instance, to the footer
by adding the following lines to conf.py
:
CONTENT_FOOTER = ''' <div class="text-center"> <p> <span class="fa-stack fa-2x"> <a href="/rss.xml"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-rss fa-inverse fa-stack-1x"></i> </a> </span> <span class="fa-stack fa-2x"> <a href="https://twitter.com/jluttine"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-twitter fa-inverse fa-stack-1x"></i> </a> </span> <span class="fa-stack fa-2x"> <a href="https://github.com/jluttine"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-github fa-inverse fa-stack-1x"></i> </a> </span> <span class="fa-stack fa-2x"> <a href="https://www.linkedin.com/in/jluttine"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-linkedin fa-inverse fa-stack-1x"></i> </a> </span> <span class="fa-stack fa-2x"> <a href="mailto:{email}"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-envelope fa-inverse fa-stack-1x"></i> </a> </span> </p> <p> Contents © {date} <a href="mailto:{email}">{author}</a> — {license} — Powered by <a href="https://getnikola.com" rel="nofollow">Nikola</a> </p> </div> '''
Just remember to change the usernames in each of the social media links.
Write math
You can write math in your blog either inline as \\( X \sim N(\mu, \sigma) \\)
which looks like \( X \sim N(\mu, \sigma) \) or in display mode as
\\[ N(x|\mu, \sigma) = \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{(x-\mu)^2}{2\sigma^2}} \\]
which looks like
\[ N(x|\mu, \sigma) = \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{(x-\mu)^2}{2\sigma^2}} \]
By default, MathJax is used but there is also support for a very promising fast math renderer called KaTeX. In order to use KaTeX, use the following settings:
EXTRA_HEAD_DATA = """ ... (possibly other links here too, for instance, Font Awesome) ... <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css"> """ USE_KATEX = True
If math doesn't render correctly (or at all) with KaTeX, you might be using some
LaTeX commands that KaTeX doesn't (yet) support. Also, note that you need to
add mathjax
tag to your post to get the math rendered - even if you're using
KaTeX.
Add a license
You can also add a license for your content:
LICENSE = """ <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"> <img alt="Creative Commons License BY-SA" style="border-width:0; margin-bottom:12px;" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png"> </a> """
Remove .html suffix from archive.html
This is just a small annoyance, but by default, the archive is located in
/archive.html
. If you want it to be in /archive/
, add the following lines
to your conf.py
:
ARCHIVE_PATH = "archive" ARCHIVE_FILENAME = "index.html"
Remember to also fix the navigation links:
NAVIGATION_LINKS = { DEFAULT_LANG: ( ("/archive/", "Archive"), ("/categories/", "Tags"), ("/rss.xml", "RSS feed"), ("/about/", "About"), ), }
Miscellaneous settings
Here are some other configuration suggestions for you to consider. If you want the archive to show all your blog posts as a single list instead of a year and month based hierarchy, use the following setting:
CREATE_SINGLE_ARCHIVE = True
To show only short teasers instead of full posts on index pages:
INDEX_TEASERS = True
Remember to mark the teasers in your blog posts with <!-- TEASER_END -->
in
Markdown.
Conclusion
That's it, I hope this was useful. Of course, there are lots of configuration
options you can modify, so I recommend you read the brief Nikola
handbook to get an overview. You can also
take a look at my conf.py
in
GitHub. And if you have ideas
on how to improve the setup for a blog, please comment below.
And yes, I know, this blog post isn't a notebook. But the next one will be and in that post I'll share one amazing tool that I just discovered a few days ago related to sharing notebooks.
Comments
Comments powered by Disqus