如何将HTML嵌入IPython输出?

248

是否可以将渲染后的HTML输出嵌入到IPython输出中?

一种方法是使用

from IPython.core.display import HTML
HTML('<a href="http://example.com">link</a>')

或者(IPython多行单元格别名)

%%html
<a href="http://example.com">link</a>

返回格式化链接,但是

  1. 这个链接不能从控制台打开网页本身。不过,IPython笔记本支持诚实的渲染。
  2. 我不知道如何在列表或pandas打印表格中呈现HTML()对象。你可以使用df.to_html(),但无法在单元格内创建链接。
  3. 这个输出在PyCharm Python控制台中不是交互式的(因为它不是QT)。

如何克服这些缺点并使IPython输出更具交互性?


2
这是你想做的吗?http://ipython.org/ipython-doc/dev/config/integrating.html - cel
@cel 它的输出格式与 HTML() 相同,但我仍然无法解决项目1和2。 - Anton Tarasenko
2
我不是专家,所以这可能是错误的,但我觉得将任意HTML代码注入到其他对象的表示中是行不通的。这会耦合对象的逻辑和表示,可能并不理想。但你肯定可以编写包装器对象,其中包含原始对象,并使用_repr_html_方法提供自定义的HTML表示。 - cel
实际上,我刚刚运行了你的代码,只要我移动到另一个单元格,它就可以工作了... - Goodword
添加JavaScript:https://dev59.com/questions/0mQn5IYBdhLWcg3wamjO#47414836 - Anton Tarasenko
6个回答

367

4
这个版本能执行JavaScript吗? - Joshua M. Moore
6
这是一个示例笔记本的链接,展示了显示效果的可能性:丰富输出 - Romain
2
显示部分允许我在笔记本中嵌入JavaScript。 - lamecicle
1
如果我需要使用Dash制作网站并且所有Python代码都在Jupyter的.ipynb文件中,那么这是一种可行的方法吗? - user8322222
这个“丰富输出”笔记本链接有效:https://github.com/ipython/ipython/blob/main/examples/IPython%20Kernel/Rich%20Output.ipynb - Itamar Katz
显示剩余4条评论

60

前段时间,Jupyter笔记本开始从HTML内容中剥离JavaScript [#3118]。下面是两个解决方案:

本地HTML服务

如果你现在想要在你的页面上嵌入带有JavaScript的HTML页面, 最简单的方法是将HTML文件保存到与你的笔记本相同的目录中,然后按以下步骤加载HTML:

from IPython.display import IFrame

IFrame(src='./nice.html', width=700, height=600)

提供远程 HTML 服务

如果您喜欢托管解决方案,您可以将 HTML 页面上传到 Amazon Web Services S3 中的“存储桶”,更改存储桶的设置以使其成为静态网站主机,并在笔记本中使用一个 Iframe 组件:

from IPython.display import IFrame

IFrame(src='https://s3.amazonaws.com/duhaime/blog/visualizations/isolation-forests.html', width=700, height=600)

这将会在一个 iframe 中呈现您的 HTML 内容和 JavaScript,就像您在其他任何网页上一样:

<iframe src='https://s3.amazonaws.com/duhaime/blog/visualizations/isolation-forests.html', width=700, height=600></iframe>


1
非常感谢,这正是我所寻找的。我使用它在我的静态博客站点中交互地渲染 plotly 图表。 - Okroshiashvili
1
太完美了!正是我所需要的——因为我想在Amazon SageMaker上的Jupyter Notebook上托管整个Web应用程序。谢谢! - Adi Levin
2
为了获得额外的分数,可以从单元格异步运行交互式Web服务器,并在其他单元格中的iFrame内与其创建的页面进行交互! - holdenweb
@holdenweb,你能给一个代码示例来说明如何实现吗?这听起来是个非常酷的想法。 - Rich Lysakowski PhD
1
简单的概念证明请参见 https://gist.github.com/holdenweb/fb8de56e33cdfaef9218673915cc7f1c - holdenweb
显示剩余2条评论

25
相关:在构建一个类时,可以使用def _repr_html_(self): ...来创建其实例的自定义HTML表示:
class Foo:
    def _repr_html_(self):
        return "Hello <b>World</b>!"

o = Foo()
o

将呈现为:

你好世界

有关更多信息,请参阅{{link1:IPython文档}}。

一个高级示例:

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

将呈现:

  1. ☐ 购买牛奶
  2. ☐ 做作业
  3. ☑ 玩视频游戏

9

在@Harmon的基础上拓展,看起来你可以将displayprint语句合并在一起...如果需要的话。或者,也许更容易的方法是将整个HTML格式化为一个字符串,然后使用display。无论哪种方式,这都是一个不错的功能。

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>'))

输出的结果如下:


你好,世界!

这是一个链接:

www.google.com

一些其他的文本内容...

这里是段落文本...



4

首先,这是代码:

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)

一些快速说明:
  • 对于简单的项目,您通常可以只使用内联HTML。如果您正在呈现框架,例如大型JavaScript可视化框架,则可能需要使用IFrame。Jupyter在没有随机嵌入的HTML的情况下在浏览器中运行已经很困难了。
  • 奇怪的参数metadata=dict(isolated=True)并不会像旧文档所建议的那样将结果隔离在IFrame中。它似乎防止clear-fix重置所有内容。该标志已不再记录:我发现使用它可以使某些display:grid样式正确呈现。
  • 这个IFrame解决方案写入一个临时文件。您可以像此处描述的那样使用数据URI,但这会使调试输出变得困难。Jupyter IFrame函数不接受datasrcdoc属性。
  • tempfile模块创建的内容不能共享到另一个进程,因此需要random_name()
  • 如果您在其中使用IFrame的HTML类,则会收到警告。这可能仅在每个会话中发生一次。
  • 您可以在单元格的顶层使用HTML('Hello, <b>world</b>'),其返回值将呈现。在函数内部,使用display(HTML(...)),就像上面所做的那样。这也允许您自由混合displayprint调用。
  • 奇怪的是,IFrame的缩进略微多于内联HTML。

3
为了在循环中实现这一点,你可以这样做:
display(HTML("".join([f"<a href='{url}'>{url}</a></br>" for url in urls])))

这基本上是在循环中创建html文本,然后使用display(HTML())结构将整个字符串作为HTML显示出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接