JupyterでToC&HTMLがうまく機能しない
Jupyterで作成したドキュメントを、HTMLで出力して他の人と共有することで、いちいちPowerPointなどスライドにせずとも簡単に作成できます。
特にExtentionであるTable of Contents(ToC)によって、Markdownによる目次が追加でき、より見やすいファイルを作ることができます。
セルからPythonコードによってMarkdownを出力しない限りは。
例えば、2つのMarkdownセルに以下のように記述したファイルをHTMLに出力します。
# First Cell ## First-sub Cell
そしてコマンド jupyter nbconvert --to html_toc hoge.ipynb
でHTMLにします。
すると、以下の画像のようなHTMLファイルが作られます。
左側のサイドバーに目次が作られ、クリックするとその場所に飛ぶことができます。便利です。
ちなみに、大量の画像を(サブディレクトリに格納することなく)1つのファイルにまとめるには --ExtractOutputPreprocessor.enabled=False
オプションをつけます。
今まで散々探していたのに、今日初めて知りました。
で、ここまでは良いのですが、次にPythonコードでもMarkdownを出力したくなったとします。
Jupyter上ではナンバリングが正しくされ、Table of Contents上でも正しく表示されます。
しかし、HTMLにconvertすると
1.1にあるべきのFirst-sub Cellが飛ばされ、左のサイドバーにも表示されません。
これは非常に不便で、for文等で機械的にMarkdownを出力する時に困ります。
こんな感じのissueも立っていますが解決されていません。
で、どうしたもんかなと先ほどのHTMLファイルとにらめっこしていると、First CellにはあってFirst-sub Cellにはないclassタグを見つけました。
それが <div class="text_cell_render border-box-sizing rendered_html">
です。
toc2.js(ToCのスクリプトファイル、インストール後はsite-packages>jupyter_contrib_nbextensions>nbextentions>toc2>toc2.js的なところにあった)を見ると、
all_headers = $('.text_cell_render').find('[id]:header:not(:has(.tocSkip))'); ~中略~ //loop over all headers all_headers.each(function(i, h) { // remove pre-existing number $(h).children('.toc-item-num').remove(); var level = parseInt(h.tagName.slice(1), 10) - min_lvl + 1; // skip below threshold, or h1 ruled out by cfg.skip_h1_title if (level < 1 || level > cfg.threshold) { return; } h = $(h); // numbered heading labels var num_str = incr_lbl(lbl_ary, level - 1).join('.'); if (cfg.number_sections) { $('<span>') .text(num_str + '\u00a0\u00a0') .addClass('toc-item-num') .prependTo(h); } ~中略~ // Create toc entry, append <li> tag to the current <ol>. ul.append( $('<li>').append( $('<span>').append( make_link(h, toc_mod_id)))); });
のようになっているので、どうもtext_cell_render
が怪しいのではないかと。
ということで、先ほどMarkdownを出力していたセルを
from IPython.display import HTML, Markdown display(HTML(f'<div class="text_cell_render"><h2 id="First-sub-Cell">First-sub Cell<a class="anchor-link" href="#First-sub-Cell">¶</a></h2></div>'))
に変えてみます。半角スペースはidやhref上ではハイフンに置き換えます。
そして、先ほどと同様にconvertすると
このように、正しく(こちらの想定通りに)表示することができるようになりました。
正しい修正方法ではない気しかしないですが、とりあえず使えるようになりました。