How to embed HTML into IPython output?
PythonHtmlIpythonJupyter NotebookPython Problem Overview
Is it possible to embed rendered HTML output into IPython output?
One way is to use
from IPython.core.display import HTML
HTML('<a href="http://example.com">link</a>')
or (IPython multiline cell alias)
%%html
<a href="http://example.com">link</a>
Which return a formatted link, but
- This link doesn't open a browser with the webpage itself from the console. IPython notebooks support honest rendering, though.
- I'm unaware of how to render
HTML()
object within, say, a list orpandas
printed table. You can dodf.to_html()
, but without making links inside cells. - This output isn't interactive in the PyCharm Python console (because it's not QT).
How can I overcome these shortcomings and make IPython output a bit more interactive?
Python Solutions
Solution 1 - Python
This seems to work for me:
from IPython.core.display import display, HTML
display(HTML('<h1>Hello, world!</h1>'))
The trick is to wrap it in display
as well.
Edit:
from IPython.display import display, HTML
In order to avoid:
> DeprecationWarning: Importing display from IPython.core.display is > deprecated since IPython 7.14, please import from IPython display
Solution 2 - Python
Some time ago Jupyter Notebooks started stripping JavaScript from HTML content [#3118]. Here are two solutions:
Serving Local HTML
If you want to embed an HTML page with JavaScript on your page now, the easiest thing to do is to save your HTML file to the directory with your notebook and then load the HTML as follows:
from IPython.display import IFrame
IFrame(src='./nice.html', width=700, height=600)
Serving Remote HTML
If you prefer a hosted solution, you can upload your HTML page to an Amazon Web Services "bucket" in S3, change the settings on that bucket so as to make the bucket host a static website, then use an Iframe component in your notebook:
from IPython.display import IFrame
IFrame(src='https://s3.amazonaws.com/duhaime/blog/visualizations/isolation-forests.html', width=700, height=600)
This will render your HTML content and JavaScript in an iframe, just like you can on any other web page:
<iframe src='https://s3.amazonaws.com/duhaime/blog/visualizations/isolation-forests.html', width=700, height=600></iframe>
Solution 3 - Python
Related: While constructing a class, def _repr_html_(self): ...
can be used to create a custom HTML representation of its instances:
class Foo:
def _repr_html_(self):
return "Hello <b>World</b>!"
o = Foo()
o
will render as:
> Hello World!
For more info refer to IPython's docs.
An advanced example:
from html import escape # Python 3 only :-)
class Todo:
def __init__(self):
self.items = []
def add(self, text, completed):
self.items.append({'text': text, 'completed': completed})
def _repr_html_(self):
return "<ol>{}</ol>".format("".join("<li>{} {}</li>".format(
"☑" if item['completed'] else "☐",
escape(item['text'])
) for item in self.items))
my_todo = Todo()
my_todo.add("Buy milk", False)
my_todo.add("Do homework", False)
my_todo.add("Play video games", True)
my_todo
Will render:
>
-
>
- ☐ Buy milk >
- ☐ Do homework >
- ☑ Play video games >
Solution 4 - Python
Expanding on @Harmon above, looks like you can combine the display
and print
statements together ... if you need. Or, maybe it's easier to just format your entire HTML as one string and then use display. Either way, nice feature.
display(HTML('<h1>Hello, world!</h1>'))
print("Here's a link:")
display(HTML("<a href='http://www.google.com' target='_blank'>www.google.com</a>"))
print("some more printed text ...")
display(HTML('<p>Paragraph text here ...</p>'))
Outputs something like this:
Hello, world!
Here's a link:
some more printed text ...
Paragraph text here ...
Solution 5 - Python
First, the code:
from random import choices
def random_name(length=6):
return "".join(choices("abcdefghijklmnopqrstuvwxyz", k=length))
# ---
from IPython.display import IFrame, display, HTML
import tempfile
from os import unlink
def display_html_to_frame(html, width=600, height=600):
name = f"temp_{random_name()}.html"
with open(name, "w") as f:
print(html, file=f)
display(IFrame(name, width, height), metadata=dict(isolated=True))
# unlink(name)
def display_html_inline(html):
display(HTML(html, metadata=dict(isolated=True)))
h="<html><b>Hello</b></html>"
display_html_to_iframe(h)
display_html_inline(h)
Some quick notes:
- You can generally just use inline HTML for simple items. If you are rendering a framework, like a large JavaScript visualization framework, you may need to use an IFrame. Its hard enough for Jupyter to run in a browser without random HTML embedded.
- The strange parameter,
metadata=dict(isolated=True)
does not isolate the result in an IFrame, as older documentation suggests. It appears to preventclear-fix
from resetting everything. The flag is no longer documented: I just found using it allowed certaindisplay: grid
styles to correctly render. - This
IFrame
solution writes to a temporary file. You could use a data uri as described here but it makes debugging your output difficult. The JupyterIFrame
function does not take adata
orsrcdoc
attribute. - The
tempfile
module creations are not sharable to another process, hence therandom_name()
. - If you use the HTML class with an IFrame in it, you get a warning. This may be only once per session.
- You can use
HTML('Hello, <b>world</b>')
at top level of cell and its return value will render. Within a function, usedisplay(HTML(...))
as is done above. This also allows you to mixdisplay
andprint
calls freely. - Oddly, IFrames are indented slightly more than inline HTML.
Solution 6 - Python
to do this in a loop, you can do:
display(HTML("".join([f"<a href='{url}'>{url}</a></br>" for url in urls])))
This essentially creates the html text in a loop, and then uses the display(HTML()) construct to display the whole string as HTML