Bokeh 图表导出完整指南:基于 Playwright 后端的 PNG 与 SVG 输出
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
Bokeh 提供了从 Python 侧将图表、布局与控件导出为 PNG 与 SVG 文件的能力,其核心是"无头浏览器渲染 + 截图 / DOM 提取"的导出流水线。本文以仓库中 export.rst 为骨架,系统讲解导出依赖的安装、后端选择、export_png/export_svg/export_svgs等函数的完整用法,并结合 src/bokeh/io/export.py、src/bokeh/io/browser.py 等源码揭示其底层实现原理。读完本文,你将能独立完成"把 Bokeh 图保存成高清 PNG、把矢量图导出为可编辑 SVG"的完整工作流,并能理解后端切换与参数调优的关键。
导出功能的额外依赖:无头浏览器后端
Bokeh 的导出功能并非纯 Python 实现——它需要把一个布局渲染进真实浏览器环境,再截取结果。因此使用导出函数前,必须先准备一个无头浏览器后端。当前 Bokeh 默认使用Playwright,同时为了兼容旧代码暂时保留了已弃用的Selenium后端(src/bokeh/io/export.py中通过ExportBackendType = Literal["selenium", "playwright"]定义后端类型)。
使用 Playwright(默认后端)
Playwright 是 Bokeh 的默认导出后端。安装可选导出依赖与 Chromium 无头 Shell 即可:
pip install "bokeh[export]" playwright install --only-shell chromium其中bokeh[export]这一 extra 依赖在仓库 pyproject.toml 中定义为playwright >=1.49。也可以单独安装playwright包,无需手动管理浏览器驱动二进制——Bokeh 导出只要求 Chromium 的 headless shell,这大大简化了环境搭建。
从源码看,Playwright 后端位于 src/bokeh/io/browser.py,其启动 Chromium 时使用了以下参数(见_PlaywrightState._ensure_browser):
--hide-scrollbars:隐藏滚动条,避免截图出现多余元素;--force-device-scale-factor={scale_factor}:按scale_factor控制设备像素比,实现高分辨率导出;--force-color-profile=srgb:强制 sRGB 色彩空间,保证颜色一致性。
若 Chromium headless shell 未安装,启动会抛出RuntimeError并提示运行playwright install --only-shell chromium。
使用 Selenium(已弃用)
Selenium后端自 4.0.0 起被弃用,并将在未来版本移除,新代码应使用 Playwright。Selenium 方案要求安装seleniumPython 包,并把浏览器驱动二进制放到 PATH 中:
- Firefox 使用
geckodriver; - Chrome / Chromium 使用
ChromeDriver。
文档给出了 conda 与 pip 两种安装路径:
conda 安装(推荐,便于保证驱动与浏览器版本匹配):
# Selenium + geckodriver(Firefox) conda install selenium geckodriver -c conda-forge # geckodriver 需要系统中有兼容的 Firefox,也可以从 conda-forge 安装 conda install firefox -c conda-forge # Selenium + ChromeDriver(Chrome) conda install selenium python-chromedriver-binary -c conda-forgepip 安装:
pip install selenium # 然后从 geckodriver 发布页下载 geckodriver 二进制并放入 PATH(Firefox 方案) pip install selenium chromedriver-binary # 并确保 chromedriver(Windows 下为 chromedriver.exe)位于 PATH(Chrome 方案)使用 ChromeDriver 时,需要保证 ChromeDriver 与系统中的 Chrome / Chromium 版本兼容。此外,Selenium 后端还有两个补充配置入口:create_firefox_webdriver与create_chromium_webdriver(见 src/bokeh/io/webdriver.py),前者依赖 PATH 中的firefox与geckodriver,后者会优先读取settings.chromedriver_path(),找不到时再在 PATH 中依次查找chromedriver、chromium.chromedriver、chromedriver-binary,仍找不到则报错并提示可用BOKEH_CHROMEDRIVER_PATH指定位置。
选择导出后端:环境变量、参数与全局设置
Bokeh 默认使用 Playwright。你可以通过以下三种方式指定后端,且优先级各不相同:
1. 环境变量——设置BOKEH_EXPORT_BACKEND,取值auto、playwright、selenium:
BOKEH_EXPORT_BACKEND=playwright python my_script.py该环境变量对应的全局设置在 src/bokeh/settings.py 中定义(PrioritizedSetting("export_backend", "BOKEH_EXPORT_BACKEND", default="playwright")),默认值即playwright。
2. 每次调用传参——直接把backend传给任意导出函数:
from bokeh.io import export_png export_png(plot, filename="plot.png", backend="playwright")3. 全局设置——修改settings.export_backend:
from bokeh.settings import settings settings.export_backend = "playwright"webdriver关键字参数:所有导出函数还接受webdriver参数,可以直接传入一个浏览器实例——既可以是 Selenium 的WebDriver,也可以是 Playwright 的Browser/BrowserContext。后端会根据传入实例的类型自动判断,并覆盖backend参数与export_backend设置。传入 SeleniumWebDriver会走已弃用的 Selenium 后端并触发弃用警告。
auto的语义:auto会优先尝试 Playwright;当 Playwright 未安装时,回退到已弃用的 Selenium 后端。
上述优先级在源码 src/bokeh/io/export.py 的_resolve_backend中有完整实现:
- 传入的是 Playwright
Browser/BrowserContext→ 始终用 Playwright 后端; - 传入的是其他(Selenium)driver → 始终用 Selenium 后端;
- 显式指定了
backend→ 使用该后端; - 否则回退到
BOKEH_EXPORT_BACKEND设置; - 设置为
auto时,先探测playwright是否可导入,其次探测selenium;两者都未安装则抛出RuntimeError,提示安装命令。
导出 PNG 图像
export_png函数可以把布局渲染为 RGBA 格式的 PNG 图片:它在内存中完成渲染并截图,输出图片尺寸与源布局一致。
基本用法与save、show类似:
from bokeh.io import export_png export_png(plot, filename="plot.png")仓库中提供了可直接运行的完整示例 examples/output/export/export_to_png.py,它用autompg_clean采样数据构造一个分组柱状图后调用export_png(p, filename="plot.png"):
from bokeh.io.export import export_png from bokeh.palettes import Spectral5 from bokeh.plotting import figure from bokeh.sampledata.autompg import autompg_clean as df from bokeh.transform import factor_cmap df.cyl = df.cyl.astype(str) df.yr = df.yr.astype(str) group = df.groupby(['cyl', 'mfr']) index_cmap = factor_cmap('cyl_mfr', palette=Spectral5, factors=sorted(df.cyl.unique()), end=1) p = figure(width=800, height=300, title="Mean MPG by # Cylinders and Manufacturer", x_range=group, toolbar_location=None, tooltips=[("MPG", "@mpg_mean"), ("Cyl, Mfr", "@cyl_mfr")]) p.vbar(x='cyl_mfr', top='mpg_mean', width=1, source=group, line_color="white", fill_color=index_cmap) p.y_range.start = 0 p.x_range.range_padding = 0.05 p.xgrid.grid_line_color = None p.xaxis.axis_label = "Manufacturer grouped by # Cylinders" p.xaxis.major_label_orientation = 1.2 p.outline_line_color = None export_png(p, filename="plot.png")透明背景
若要生成透明背景的 PNG,将Plot.background_fill_color与Plot.border_fill_color设为None:
plot.background_fill_color = None plot.border_fill_color = None尺寸可变性(sizing mode)警告
响应式 sizing 模式(responsive 等)可能生成尺寸与宽高比不符合预期的布局。为了导出结果稳定可靠,请使用默认的fixedsizing 模式。这一警告在 src/bokeh/io/export.py 的函数文档字符串中同样有标注。
export_png参数详解
从 src/bokeh/io/export.py 的函数签名与文档看,完整参数如下:
| 参数 | 类型 | 说明 |
|---|---|---|
obj | UIElement或Document | 要导出的布局(Row/Column)、Plot、控件或 Document |
filename | PathLike(如 str、Path),可选 | 保存文件名;默认从脚本名推导(如/foo/myplot.py生成/foo/myplot.png),不可用时使用临时文件 |
width/height | int,可选 | 仅当obj是 Plot 实例时生效的期望导出尺寸,否则忽略 |
scale_factor | float,默认1 | 输出 PNG 的缩放因子,可在保持元素相对比例的前提下获得更高分辨率 |
webdriver | SeleniumWebDriver或 PlaywrightBrowser/BrowserContext | 自定义浏览器实例,后端自动检测 |
timeout | int,默认5 | 等待 Bokeh 初始化的最长时间(秒),1.1.1 起引入 |
state | State,可选 | 指定状态对象;为None时使用当前隐式状态 |
backend | "playwright"或"selenium",可选 | 导出后端;为None时使用BOKEH_EXPORT_BACKEND设置(默认 Playwright) |
export_png返回保存后的文件绝对路径;如果截图宽或高为 0,会抛出ValueError("unable to save an empty image")。
直接获取图像对象:get_screenshot_as_png
如果不想落盘,只想在代码中拿到图像对象,可使用底层函数bokeh.io.export.get_screenshot_as_png:
from bokeh.io.export import get_screenshot_as_png image = get_screenshot_as_png(obj, height=height, width=width, driver=webdriver)该函数返回PIL.Image.Image对象,其内部实现(见 src/bokeh/io/export.py)会先通过_resolve_backend选出后端模块,再调用对应后端的get_screenshot_as_png,最终对截图做 RGBA 转换、按 DPR 裁剪并依据scale_factor缩放。注意这里的关键字参数名是driver(而非webdriver),用于接收 Selenium WebDriver 或 Playwright Browser / BrowserContext。
导出 SVG 图像
Bokeh 还可以用 SVG 元素替换默认的 HTML5 Canvas 绘图输出。SVG 是矢量格式,可在 Adobe Illustrator 等图形软件中编辑,或进一步转换为 PDF。
需要说明的是:SVG 后端的渲染性能不如默认的 Canvas 后端,当图形元素(glyph)数量大或交互操作(如平移)频繁时尤其明显。因此在交互密集的 Web 场景下建议保持 Canvas,仅在需要矢量输出时才切换到 SVG。
激活 SVG 后端
设置Plot.output_backend为"svg"即可:
# 方式一:构造时指定 plot = Plot(output_backend="svg") # 方式二:创建后赋值 plot.output_backend = "svg"与 PNG 一样,设置background_fill_color = None与border_fill_color = None可以生成透明背景的 SVG。
代码导出:export_svg与export_svgs
两种工具函数分别适用于"合并为单个 SVG"与"拆分为独立 SVG"两种需求:
from bokeh.io import export_svg # 把单个 plot 或整个布局保存为一个 SVG 文件 export_svg(plot, filename="plot.svg") from bokeh.io import export_svgs # 把布局中每个启用 SVG 的 plot 导出为相互独立的 SVG 文件 export_svgs(plot, filename="plot.svg")两个函数都返回文件名字符串列表(src/bokeh/io/export.py)。底层写文件逻辑_write_collection会为多个输出自动添加序号后缀:第一个文件用给定文件名,后续文件命名为plot_1.svg、plot_2.svg……。export_svgs在布局中没有找到任何 SVG 启用的 Plot 时会记录No SVG Plots were found.警告并返回空列表。
仓库中的可运行示例见 examples/output/export/export_to_svg.py,其绘图部分与 PNG 示例完全一致,仅把结尾换成export_svg(p, filename="plot.svg")。
浏览器端导出
除了代码导出,还可以在浏览器中直接保存 SVG:
- SVG-Crowbar 书签工具:在浏览器中加载 Bokeh 页面后运行该书签,它会弹出提示,把每个 plot 下载为独立的 SVG 文件。该工具与 Chrome 完全兼容,大多数情况下也适用于 Firefox;
- 工具栏的 SaveTool(保存工具):注意通过 SaveTool 导出的文件在原本工具栏所在位置会留下一块空白区域。
导出流水线的源码级原理
理解导出函数背后发生了什么,有助于排查"导出空白 / 超时 / 尺寸异常"等问题。两个后端的实现高度同构(src/bokeh/io/browser.py 与 src/bokeh/io/webdriver.py),核心步骤为:
- 生成临时 HTML:通过
get_layout_html(见 src/bokeh/io/util.py)把布局序列化为带内联资源的 HTML,写入tmp_html()创建的临时文件,主题则取自(state or curstate()).document.theme; - 导航并等待渲染完成:浏览器打开
file://临时文件,然后wait_until_render_complete轮询两个条件——先是_BOKEH_LOADED_EXPR(检查typeof Bokeh !== "undefined"且存在 document),随后执行_WAIT_SCRIPT等待首个 document 发出bokeh:idle事件(Playwright 侧通过page.wait_for_function等待window._bokeh_render_complete标志); - 确定裁剪区域:
_ROOT_VIEW_BBOX_SCRIPT读取第一个 root view 的getBoundingClientRect()与window.devicePixelRatio,据此把视口调整到恰好容纳布局的大小(Playwright 端在视口基础上再加 100px 余量,最后按坐标裁剪,避免某些窗口管理器无法精确设置窗口大小的问题); - 截图或提取 SVG:PNG 走
page.screenshot(clip=...)/web_driver.get_screenshot_as_png();SVG 则执行_SVG_SCRIPT(obj)或_SVGS_SCRIPT——后者在页面内用Bokeh.require("models/layouts/layout_dom")与Bokeh.require("models/plots/plot")遍历Bokeh.index中的视图,收集所有PlotView的 SVG 序列化结果。
值得一提的实现细节:Playwright 后端使用一个常驻的后台守护线程(_PlaywrightThread)运行 asyncio 事件循环(src/bokeh/io/browser.py),Windows 上强制使用ProactorEventLoop(因为 Playwright 以 asyncio 子进程方式启动其 driver);同时通过atexit注册清理函数,进程退出时自动关闭浏览器与线程。浏览器实例默认复用(_PlaywrightState.reuse),当新请求的scale_factor大于当前浏览器的启动倍率时会先重启浏览器——这也解释了为什么"先导出 1x 再导出高倍率图"会比反向顺序更快。Selenium 侧的webdriver_control(_WebdriverState)同样实现 driver 复用,并在 PATH 中探测不到任何可用驱动时报出安装建议。
仓库单元测试 tests/unit/bokeh/io/test_export.py 覆盖了export_png、export_svg、export_svgs、get_screenshot_as_png等函数的调用路径与参数校验,可作为理解各函数行为边界的参考。
常见问题与最佳实践小结
- 导出空白或超时:通常是 Chromium headless shell 未安装(运行
playwright install --only-shell chromium)或timeout过短(默认 5 秒),可将timeout调大后重试; - 导出尺寸不符预期:优先检查是否使用了响应式 sizing 模式,导出场景请使用默认
fixedsizing mode; - 高分辨率 PNG:使用
scale_factor(如2)而非直接放大布局,可以在保持元素相对比例的前提下获得高清输出; - 透明背景:同时把
background_fill_color与border_fill_color设为None; - 矢量输出:设置
plot.output_backend = "svg"后再用export_svg导出;布局中多图拆分用export_svgs; - 混合环境:若同时装有 Selenium 与 Playwright,
auto会优先选 Playwright;如需强制某个后端,用BOKEH_EXPORT_BACKEND环境变量或每次调用传backend参数即可。
至此,从依赖安装、后端选择到 PNG / SVG 导出的完整链路已经打通,你可以把这套能力直接应用到报表生成、文档插图与 CI 截图校验等场景中。
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考