Jupyter Notebook 图片插入、大小控制与对齐指南
2026/9/18 17:14:47 网站建设 项目流程

Jupyter Notebook 里插一张图,听起来是再基础不过的操作,![](path)一行就能解决。可真正把它放进一份要交付的分析报告、一门课的讲义、或者一份机器学习实验记录里时,问题就冒出来了:图片默认按原始分辨率铺开,一张手机截图能顶满整个屏幕;想让图居中,Markdown 原生语法压根不认;导出成 HTML 分享给同事,图片变成了一个碎掉的图标。jupyter notebook 插入图片并控制大小和对齐方式这个需求,拆开看是三件互相牵连的事——图怎么进来、进来之后多大、摆在哪。任何一个环节没考虑到,最后交付的东西都会显得不专业。

这篇内容面向所有用 Notebook 干实事的人:做数据分析的、跑实验记录的、写教学材料的、拿 Notebook 当个人知识库的,不管你是刚学会新建单元格的新手,还是已经能熟练用nbconvert批量导出的老手,下面这些手法都能直接用上。我会从 Jupyter 的渲染管线讲起,说清楚为什么原生 Markdown 做不到尺寸和对齐控制,然后给出 HTML 标签、代码显示、base64 内嵌这几条路线,配上尺寸换算过程、对齐的四种写法,最后把导出兼容性和一堆踩过的坑整理成速查表。目标只有一个:让你以后往 Notebook 里放图,不用再靠试。

1. 先搞清楚 Notebook 里的图片到底由谁渲染

很多人卡在图片样式上,根源是没分清单元格的类型差异。Markdown 单元格和 Code 单元格走的是两条完全不同的渲染链路,能用的语法、能生效的样式都不一样。把这条链路理清楚,后面所有操作都是顺水推舟。

1.1 Markdown 单元格与代码单元格的渲染差异

Markdown 单元格的内容会先被解析成 HTML,再交给前端的渲染层显示。这个转换过程支持标准 Markdown 语法,比如![alt](src),同时也允许你直接写裸 HTML——这一点非常关键,意味着<img><div><table>这些标签在 Markdown 单元格里是合法且会被渲染的。但注意,Markdown 解析器对 HTML 的处理是"透传":它不会帮你做任何样式增强,你写多少属性,浏览器就按多少属性渲染。

Code 单元格则不同。它执行 Python,输出结果由 IPython 的输出格式化器接管。文本走text/plain,而IPython.display.Image这类对象会输出富媒体表示(rich representation),前端根据 MIME 类型选择渲染方式。换句话说,代码单元里图片的尺寸是你在 Python 对象里指定的,不是在 HTML 里指定的。

这个区别决定了两件事:想精细控制样式(边框、间距、对齐),走 Markdown 单元写 HTML 更直接;想让图片由数据动态生成、批量处理、随计算结果变化,走代码单元更合适。现实中我通常是混着用——示意图、流程图这种静态资源放 Markdown,模型输出、可视化结果这种动态内容走代码。

1.2 路径解析:相对路径到底相对谁

这是新手最容易翻车的地方。Jupyter Notebook 的相对路径,基准是Notebook 服务启动时的工作目录,或者更准确地说,是当前 kernel 的工作目录,一般等于你启动jupyter notebook命令时所在的目录,而不是.ipynb文件所在的目录。

比如你在~/work下执行启动命令,然后打开~/work/project/report.ipynb,此时相对路径images/a.png会被解析成~/work/images/a.png,而不是~/work/project/images/a.png。这就是为什么很多人明明把图和 notebook 放在同一个文件夹,图片却加载不出来。

稳妥的做法有三种,我按推荐程度排:

  • 用绝对路径(临时验证最快,但不适合分享):/Users/you/work/project/images/a.png,自己能跑,别人拿到就废。
  • 启动时切到 notebook 所在目录cd ~/work/project && jupyter notebook,之后相对路径就和 notebook 同级了,这是我最常用的方式。
  • 代码里动态定位:在 notebook 开头跑一段设置工作目录的代码,保证路径基准稳定。
import os # 把工作目录切到 notebook 文件所在目录,路径基准就固定了 notebook_dir = os.path.dirname(os.path.abspath("__file__")) if "__file__" in globals() else os.getcwd() os.chdir(notebook_dir) print("当前工作目录:", os.getcwd())

注意:如果先用 Markdown 单元写了图片、后来才切换工作目录,需要重新执行该 Markdown 单元(双击进去按 Shift+Enter)才会刷新渲染。

2. 原生 Markdown 图片语法能走多远

先把基线摸清楚。知道了原生语法的能力边界,才能判断什么时候必须上 HTML。

2.1 基础语法与实际表现

标准写法就这一种:

![替代文字](images/result.png)

渲染出来是一个<img>src指向路径,alt是替代文字。它的显示宽度默认等于图片的固有像素宽度,但在容器宽度不够时会被压缩到容器宽度,高度按比例自适应。行为上等价于 CSS 里的max-width: 100%; height: auto;

有一个不太常见的扩展写法,用标题位传尺寸,部分 Markdown 渲染器支持:

![alt](images/result.png "title")

这里的引号内容会被解析成title属性,鼠标悬停时显示提示文字,跟尺寸无关。Jupyter 默认的 Markdown 解析器不认{width=300}这类属性扩展语法(那是某些静态站点生成器的方言),你写了它只会当成普通文字显示出来。这一点我实测过很多次,别抱侥幸。

2.2 为什么纯 Markdown 控制不了尺寸和对齐

Markdown 的设计哲学是"内容与表现分离"。它只描述"这里有一张图",不描述"这张图多宽、摆哪"。尺寸和对齐属于表现层,归 CSS 管;Markdown 语法里没有对应字段,所以无论你怎么折腾方括号圆括号的组合,都表达不出width="400"或者居中的意思。

对齐更是如此。<img>是行内元素(inline element),它的水平位置由父容器的文本对齐方式决定。Markdown 段落默认左对齐,所以你插的图默认贴左。想改,就得引入能承载text-align的块级容器——而 Markdown 语法本身没有。

所以结论很干脆:要控制尺寸或对齐,就必须用 HTML 标签或代码方式。原生语法只适合"能把图显示出来就行"的场景。

3. 用 HTML 标签精准控制图片尺寸

好在 Markdown 单元格允许裸 HTML,这扇门一开,尺寸控制就完全自由了。下面三种写法各有适用场景,我按从粗到细的顺序说。

3.1 img 标签的三种尺寸写法与取舍

写法一:width/height 属性(像素)

<img src="images/result.png" width="480">

这是最省事的写法,width只写一个,高度会自动等比缩放。缺点是像素值写死,换到窄屏(比如手机上看导出的 HTML)还是可能溢出。

写法二:内联 style(百分比)

<img src="images/result.png" style="width:60%; height:auto;">

百分比相对父容器宽度计算,天然适配不同屏幕。我一般做报告都用这个,容器是 notebook 的输出区,宽度随窗口变,图也跟着变,不会撑破。

写法三:max-width 限制上限

<img src="images/result.png" style="max-width:600px; width:100%; height:auto;">

这个组合兼顾了两头:容器宽的时候最多 600 像素,不至于太大;容器窄的时候自动缩到 100%,不溢出。这是我个人最推荐的默认模板,写一次存成代码片段,以后到处粘贴。

三种写法的对比如下:

写法尺寸基准溢出风险适用场景
width="480"固定像素窄屏会溢出快速查看、内部草稿
style="width:60%"父容器百分比响应式报告、网页分享
max-width:600px; width:100%两者结合通用默认、正式交付

3.2 尺寸怎么算:从原图分辨率倒推显示宽度

尺寸不是拍脑袋填的,尤其当你要保证多张图视觉统一时。核心公式只有一个:

显示高度 = 原图高度 × (显示宽度 ÷ 原图宽度)

举例,一张原图 1920×1080 的截图,你想放在宽度 800 的内容区里,希望它占一半宽度:

  • 目标显示宽度 = 800 × 0.5 = 400 像素
  • 显示高度 = 1080 × (400 ÷ 1920) = 225 像素

所以写style="width:400px;"就够了,高度不用管。再比如 4 张图要并排,内容区宽 800,每张占据 1/4 还留点间隙,那么单张宽度控制在 180 上下比较舒服:style="width:180px;"

如果你的图原始宽度小于目标显示宽度,那就别放大了——放大只会让图变糊。这时候应该改用max-width,让它在小图时保持原样:style="max-width:400px; width:100%;"

实操心得:我习惯在 Notebook 里开一个隐藏的单元格专门记"内容区宽度",不同分辨率显示器上量一次记一次。写尺寸参数时直接照抄,省得每张图都去试。

3.3 让图片不撑破布局的三个细节

第一,永远给 height 留 auto。只要你写了width又手动写了固定height,变形就来了。除非你明确知道要裁剪成特定宽高比,否则高度交给浏览器算。

第二,注意高分屏(Retina)的观感。同样 400 像素宽的显示区域,在 2 倍屏上实际需要 800 像素的图源才够清晰。所以图源别压缩得太狠,宁可原图大一点、显示时缩小,也不要原图就小、显示时放大。

第三,竖版长图和横版图区别对待。竖版长图(比如手机截图、长流程图)不要用width:100%,那样高度会吓死人,整屏都放不下。竖图更该限制height

<img src="images/long.png" style="height:400px; width:auto;">

4. 对齐方式的四种落地手法

尺寸搞定,接着是位置。因为<img>是行内元素,单靠它自己没法"居中",必须靠外层容器或者 CSS 手段。下面四种方法我都长期用过,各有各的舒服场景。

4.1 div 包裹加内联样式:最通用的居中法

<div style="text-align:center;"> <img src="images/result.png" style="max-width:500px; width:100%; height:auto;"> </div>

原理很简单:<div>是块级元素,占满整行,text-align:center让它的行内内容(也就是 img)水平居中。这是我最常用的方式,兼容性最好,导出 HTML 也不出幺蛾子。

想右对齐就改成text-align:right,左对齐text-align:left。三行代码覆盖全部需求。

4.2 flex 布局:同时控水平和垂直

<div style="display:flex; justify-content:center; align-items:center;"> <img src="images/result.png" style="width:300px;"> </div>

justify-content管水平(center / flex-start / flex-end / space-between),align-items管垂直。需要并排多张图并均匀分布时,flex 比 text-align 更合适:

<div style="display:flex; justify-content:space-between;"> <img src="images/a.png" style="width:30%;"> <img src="images/b.png" style="width:30%;"> <img src="images/c.png" style="width:30%;"> </div>

三张图等距排一行,间距由space-between自动算,不用手写 margin。

4.3 表格法对齐:老派但稳得离谱

<table> <tr> <td align="center"> <img src="images/result.png" width="400"> </td> </tr> </table>

这写法在今天看来有点原始,但它有两个别人比不了的好处:在导出成 PDF 或某些邮件客户端渲染时,表格布局的兼容性远好于 flex;另外一张图配一行图注时,表格能天然做到图与文字一起居中:

<table> <tr> <td align="center"> <img src="images/result.png" width="400"><br> <em>图 1:模型收敛曲线</em> </td> </tr> </table>

<br>换行加斜体图注,整体被 td 的居中带着走。写实验报告时我用这个最多——图注必须跟图一起居中,否则看着别扭。

4.4 代码单元格里的对齐控制

代码单元里display(Image(...))出来的图,默认是块级居中(Jupyter 给输出区加了居中样式)。但如果你想自定义位置,可以借助 HTML 包装:

from IPython.display import Image, HTML, display img_html = '<img src="images/result.png" style="max-width:500px; height:auto;">' display(HTML(f'<div style="text-align:center;">{img_html}</div>'))

注意这里传给HTML的是字符串,src用相对路径同样受工作目录影响。这个套路的好处是可以把对齐方式参数化,配合循环批量出图:

def show_centered(path, width=500, caption=None): html = f'<div style="text-align:center;">' html += f'<img src="{path}" style="max-width:{width}px; width:100%; height:auto;">' if caption: html += f'<br><em>{caption}</em>' html += '</div>' display(HTML(html)) show_centered("images/result.png", width=460, caption="图 1:损失曲线")

一个函数把"尺寸 + 居中 + 图注"全包了,后面几十张图重复调用即可,样式绝对统一。这种小工具函数,是我最推荐沉淀到个人 snippet 库里的东西。

5. 代码路线:IPython.display 与 base64 内嵌

有些场景 HTML 标签解决不了,比如图是运行时生成的、需要批量循环、或者要保证导出后不丢图。这时候就得靠代码。

5.1 Image 对象的宽高参数

from IPython.display import Image, display display(Image(filename='images/result.png', width=480))

widthheight接受整数(像素)或者字符串(比如'60%',部分版本支持)。只给width时会等比缩放。这个方式适合对已经落地的图片文件做统一展示。

如果图片在内存里(比如 matplotlib 画完还没存盘),可以用retina=True提高清晰度:

display(Image(data=png_bytes, width=480, retina=True))

retina=True会让图片以两倍分辨率渲染、再缩回指定宽度,在 2 倍屏上看锐利很多。这个参数很少人知道,做精细报告时很值。

5.2 循环批量展示并统一尺寸

实验里经常要对比多组结果,一屏放 6 张图。手写 6 段 HTML 太累,循环更省事:

from IPython.display import HTML, display paths = [f"images/run{i}.png" for i in range(1, 7)] cells = [f'<img src="{p}" style="width:31%; margin:1%;">' for p in paths] grid = '<div style="display:flex; flex-wrap:wrap; justify-content:flex-start;">' + "".join(cells) + '</div>' display(HTML(grid))

flex-wrap:wrap保证放不下时自动换行,width:31%margin:1%刚好一行三张。这套三列网格布局我几乎在每个对比实验里都用,比一张张手动插效率高一个量级。

5.3 base64 内嵌:让图片跟着 notebook 走

前面所有方法都有一个共同前提——图片文件得在。一旦你把.ipynb单独发给别人,或者导出成自包含 HTML,路径就可能断掉。解决办法是把图片编码进文档本身:

import base64 from IPython.display import HTML, display def embed_image(path, width=480, align="center"): with open(path, "rb") as f: b64 = base64.b64encode(f.read()).decode("ascii") ext = path.rsplit(".", 1)[-1].lower() mime = {"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg", "gif": "image/gif"}.get(ext, "image/png") return ( f'<div style="text-align:{align};">' f'<img src="data:{mime};base64,{b64}" style="max-width:{width}px; width:100%; height:auto;">' f'</div>' ) display(HTML(embed_image("images/result.png", width=460)))

图片变成一长串 base64 字符串写进 notebook,好处是单文件自包含、随便怎么传都不丢图;代价是.ipynb体积迅速膨胀——一张 200KB 的 PNG 编码后大约 270KB 文本,几十张图就能把文件顶到十几 MB。所以我的原则是:草稿阶段用路径,交付阶段再批量嵌 base64。而且嵌入前最好先用工具把图压一遍,能省不少体积。

注意:Image(data=...)也可以直接接收 base64 字节,效果等价,但控制对齐不如 HTML 灵活,所以我更偏向 HTML 这条路。

6. 导出与兼容性:别等交报告时才发现图没了

Notebook 里的效果是一回事,导出后的效果是另一回事。这一步不提前测,交付时必然翻车。

6.1 导出 HTML 时图片的去向

jupyter nbconvert --to html report.ipynb默认不会把图片打包进去,它只在 HTML 里保留src路径。对方打开时,如果图片不在相对同一位置,就是一堆碎图标。

三个应对方案:

  • --embed-images:新版 nbconvert 支持把本地图片转成 data URI 内嵌,导出的 HTML 完全自包含。命令是jupyter nbconvert --to html --embed-images report.ipynb
  • 手动走 base64 路线(上一节的方法),优点是过程透明可控。
  • 图片和 HTML 一起打包成 zip 交付,顺手但不够优雅。

我通常用第一个,一条命令解决,实测下来最稳。

6.2 转 PDF / LaTeX 时的注意点

--to pdf走的是 LaTeX 渲染链。问题在于:LaTeX 不认识 flex 布局,你写的display:flex在转 PDF 时会失效或报错。这时候前面提到的表格法 (<table><td align="center">) 就成了救命稻草,它在 LaTeX 里会被转成对应的表格环境,居中效果保留得最好。

尺寸上,百分比宽度在 LaTeX 里也经常被忽略。要转 PDF 的文档,建议尺寸统一写成像素固定值,对齐统一用<table>,把兼容性拉满。这个经验是我被 PDF 里跑偏的图坑过好几次之后才总结出来的。

输出目标尺寸写法对齐写法备注
Notebook 预览百分比 / 像素都行div / flex最自由
导出 HTMLmax-width + 百分比div / flex--embed-images
转 PDF / LaTeX固定像素table + td align避免 flex

7. 常见问题与排查技巧实录

最后这部分是我这些年攒下来的问题清单,几乎覆盖了所有"图不听话"的情况。

7.1 图片不显示的完整排查表

按从快到慢的顺序挨个排:

现象最可能原因解决动作
显示碎图标路径不对打印os.getcwd()核对基准目录
显示 alt 文字文件名拼错/大小写不符注意 Linux 下大小写敏感
空白无任何提示HTML 标签写错未闭合检查<img><div>配对
时好时坏工作目录被前面代码改了把切目录代码放最前面
导出后不显示图片没跟着打包--embed-images或 base64

路径问题占了我遇到的所有"图不显示"里的八成以上。一个好习惯:在 notebook 开头固定输出一次当前工作目录,出问题时一眼就能对照。

7.2 尺寸和对齐不生效的几种情况

尺寸写了没变化:多半是同时写了widthheight两个固定值,或者 CSS 优先级被上层样式覆盖。去掉多余属性,只留一个max-width试。

居中没效果:检查<img>是不是被<p>单行包裹了。Markdown 会把独立成行的 HTML 块包进段落,而段落的text-align可能覆盖你的设置。把<div><img>写在同一逻辑块里,中间别留空行。

图片被拉变形:手动指定了固定高度却没管宽度比例。规规矩矩写width,让高度 auto。

竖图占满整屏:没限制高度。竖版图用height:400px; width:auto;而不是宽度百分比。

7.3 几条我踩出来的私房经验

经验一:把常用模板存成 snippet。我在 snippets 里存了三个模板——横图响应式、竖图限高、图配图注居中。写报告时直接调出粘贴,不用每次从零敲属性。这个习惯至少省了我一半的排版时间。

经验二:图源统一命名加序号。fig01_xxx.pngfig02_xxx.png这种,好处是循环批量插入时排序稳定,路径也容易记。别用带空格和中文的文件名,URL 编码后容易出问题。

经验三:大图先压缩再插入。一张 5MB 的原始截图放进 notebook,翻页会明显卡顿。用图像工具压到 200KB 以内、分辨率控制在 1600 像素宽,肉眼看几乎无损,滚动丝滑得多。

经验四:对齐方式一次定全局。一份文档里图的对齐要统一,要么全居中,要么全左对齐配图注。混着来会显得很随意。我通常报告类全居中,技术笔记类全左对齐配图注。

经验五:交付前一定走一遍完整导出。我自己就吃过亏:Notebook 里看着完美,导 PDF 后图飘了、图注错位。现在我的流程固定是——写完先导一遍 HTML 和 PDF,两个都检查过再交。

关于"jupyter notebook 怎么生成 markdown 目录语法"这个被问得很多的关联问题,顺带说一句:HTML 锚点在这里也有用武之地。你可以在图片外面套一个带id的容器<div id="fig1" align="center">...</div>,然后在文档开头用[跳到图1](#fig1)建立内部链接,长文档里用它做图片索引跳转相当方便,和 markdown 目录是同一套锚点机制。

至于那些"jupyter notebook 打不开""单元格执行没反应""无法运行"之类的环境问题,和图片排版是两条线,通常跟内核状态、依赖冲突、启动目录有关,等哪天专门写一篇环境排查的。图片这块,把路径、尺寸、对齐这三件套吃透,再算上导出兼容性,基本就能覆盖日常全部需求了。我个人的体会是,别小看这些排版细节——同样一份分析结果,图整齐、尺寸统一、位置规整的那份,看起来就是更可信。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询