Jupyter虚拟环境管理:Conda/venv与ipykernel实战指南
1. 项目概述为什么我们需要在Jupyter里管理虚拟环境如果你写过一段时间Python大概率遇到过这个场景项目A需要pandas1.3.5项目B需要pandas2.0.0而你的基础环境里装的是pandas1.5.0。直接升级或降级一个项目能跑了另一个准报错。更头疼的是你兴冲冲打开Jupyter Notebook准备分析数据结果import pandas时弹出一堆版本不兼容的警告或者干脆ModuleNotFoundError。这背后的核心矛盾就是Python的包依赖管理问题。虚拟环境正是为了解决这个“一地鸡毛”的困境而生的隔离工具。那么为什么要把虚拟环境的管理特别是和Jupyter Notebook结合起来单独拎出来讲呢因为很多人的工作流是这样的在终端Terminal里用conda或venv创建了一个干干净净的虚拟环境装好了所有依赖包满心欢喜。但一打开Jupyter发现内核列表里压根找不到这个新环境只能无奈地切回那个“大杂烩”的base环境。这相当于你精心准备了一间无菌实验室结果实验却跑到公共厨房去做隔离的意义荡然无存。因此“在Jupyter Notebook中管理内核”本质上是将虚拟环境的隔离能力无缝对接到我们最常用的交互式数据分析界面中让环境管理从命令行延伸到图形化交互界面形成闭环。这个过程涉及几个核心动作创建虚拟环境、在该环境中安装ipykernel这是让Jupyter认识该环境的关键、将环境注册为Jupyter的一个内核、以及在Notebook中自由切换使用。听起来步骤不少但一旦打通你的数据分析、模型实验、项目开发效率会得到质的提升。每个项目都有自己专属的、纯净的、可复现的Python运行时这才是现代Python开发的正确姿势。2. 核心工具链解析Conda、venv与ipykernel的选型与协作工欲善其事必先利其器。在搭建这套工作流之前我们得先搞清楚手头有几把“刀”以及它们各自最适合切什么“菜”。2.1 Conda vs venv/pip如何根据场景选择这是最常被问到的问题。简单来说Conda是一个跨平台的包管理和环境管理系统而venv是Python标准库自带的轻量级虚拟环境创建工具通常配合pip使用。Conda特别是Anaconda或Miniconda发行版的优势在于非Python依赖管理这是其杀手级功能。如果你项目里需要用到一些用C/C编译的库比如某些机器学习库的底层或者需要特定版本的编译器、系统库Conda可以一并帮你解决。它管理的包不限于Python可以是任何语言。预编译二进制包Conda Forge或Anaconda仓库中的许多包是预编译好的在Windows等平台上可以避免令人头疼的编译错误尤其是涉及科学计算栈NumPy, SciPy, TensorFlow时。环境隔离更彻底Conda不仅隔离Python包还试图隔离整个软件栈理论上环境更独立、更可复现。venv pip的优势在于轻量且标准venv模块自Python 3.3起内置无需额外安装。pip是Python官方的包安装器。这套组合是Python社区的“原教旨”方案。与PyPI无缝集成Python绝大多数开源包都发布在PyPI上pip install是获取它们最直接、最全面的方式。更符合纯Python项目如果你的项目是纯Python的Web后端、脚本工具等不涉及复杂的科学计算或系统依赖venv足够用且更简洁。我的选择建议数据科学、机器学习领域的新手或从业者尤其使用Windows系统强烈推荐从Miniconda入手。它能帮你避开大量环境配置的“坑”。专注于Web开发、自动化脚本或Linux/macOS系统的开发者可以直接使用venv。它更干净与Docker、CI/CD等工具链集成时概念更清晰。混用可以但需谨慎。原则上一个虚拟环境内最好只使用一种包管理器要么全用conda install要么全用pip install避免依赖解析冲突。如果不得已混用记住一个经验法则先用Conda安装尽可能多的包最后再用pip安装那些Conda仓库里没有的包。2.2 ipykernel连接环境与Jupyter的桥梁无论你用Conda还是venv创建了环境Jupyter Notebook本身是无法直接感知到的。Jupyter启动后会去寻找系统中已注册的“内核”。一个内核就是一个能够执行代码的进程。ipykernel这个Python包作用就是把一个Python环境“包装”成一个符合Jupyter通信协议的内核。当你在一个虚拟环境中执行python -m ipykernel install --user --namemy_env这条命令时你做了两件事在当前激活的虚拟环境中安装了ipykernel包。向当前用户目录下的Jupyter内核配置目录注册了一个名为“my_env”的内核规格文件kernel spec。这个文件里记录了启动这个内核时应该使用哪个Python解释器即你虚拟环境里的那个python。所以安装ipykernel是让Jupyter认识你虚拟环境的唯一关键步骤。少了这一步你在Notebook里就永远看不到这个新环境。注意ipykernel必须安装在目标虚拟环境内部。在base环境里安装ipykernel然后指望它能管理其他环境这是行不通的。3. 全流程实操从零创建并关联虚拟环境与Jupyter内核下面我们以最常用的MinicondaConda环境和标准Pythonvenv环境两种路径分别演示完整的操作流程。你可以根据自己的基础选择一条路径跟进。3.1 路径一使用Conda推荐给数据科学工作者假设我们的项目需要Python 3.9和pandas 2.0。步骤1创建并激活Conda虚拟环境打开终端Windows用Anaconda Prompt或系统终端macOS/Linux用系统终端。# 创建一个名为ds_projectPython版本为3.9的环境 conda create -n ds_project python3.9 -y # 激活该环境 conda activate ds_project激活后你的命令行提示符前通常会显示环境名(ds_project)表示后续操作都在这个环境内进行。步骤2在环境内安装必要包和ipykernel# 安装项目需要的包例如pandas conda install pandas2.0 -y # 关键步骤在此环境内安装ipykernel conda install ipykernel -y这里用conda install安装ipykernel能确保其依赖与当前Conda环境兼容。步骤3将环境注册为Jupyter内核# 注册内核名称设为“ds_project_py39” python -m ipykernel install --user --name ds_project_py39 --display-name Python 3.9 (ds_project)--user将内核安装到当前用户目录不需要系统权限。--name内核的内部标识符建议与环境名相关但可不同。--display-name在Jupyter Notebook界面中下拉列表里显示的名字可以起得更友好、更具体。步骤4验证与使用启动Jupyter Notebook。你可以在当前环境ds_project下启动也可以在base环境或其他任何有notebook包的环境下启动。因为内核是独立注册的。jupyter notebook在Notebook界面点击“New”按钮在下拉列表中你应该能看到“Python 3.9 (ds_project)”这个选项。选择它新建一个Notebook在单元格中输入import pandas; print(pandas.__version__)并运行确认输出为2.0.0且解释器路径是你虚拟环境下的路径可通过import sys; print(sys.executable)查看。3.2 路径二使用venv推荐给纯Python开发者假设我们有一个Web后端项目需要Python 3.10。步骤1创建并激活venv虚拟环境在项目目录下操作。# 创建名为venv的虚拟环境目录 python3.10 -m venv venv # 激活环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate步骤2安装包与ipykernel# 升级pip至最新可选但推荐 pip install --upgrade pip # 安装项目依赖例如flask pip install flask # 关键步骤安装ipykernel pip install ipykernel步骤3注册内核python -m ipykernel install --user --name web_backend --display-name Python 3.10 (Web Backend)步骤4验证步骤同Conda路径。在Jupyter新建Notebook时选择“Python 3.10 (Web Backend)”测试import flask和解释器路径。3.3 实操心得内核命名的艺术与位置内核名--name与显示名--display-name内部标识符--name应简短、无空格、唯一用于命令行管理。--display-name则可以包含空格、版本号、项目描述方便在图形界面识别。例如--name proj_ml_310对应--display-name ML Project (Py 3.10, TF 2.12)。内核安装位置使用--user标志是最安全、最通用的方式它把内核配置写在用户家目录下如~/.local/share/jupyter/kernels/。你也可以用--sys-prefix将内核安装到当前虚拟环境目录内这样当你删除环境时内核配置可能一并被清理但有时在容器或特定部署中更有用。新手一律用--user即可。一个环境注册多个内核通常没必要但你可以用不同的--display-name区分同一环境的不同配置例如一个用于常规分析一个用于GPU加速测试只要--name不同即可。4. 内核的进阶管理与维护技巧环境创建和内核注册只是开始日常工作中更需要的是管理和维护。4.1 查看、删除与重命名已注册的内核查看所有已注册内核jupyter kernelspec list这会列出所有内核的名称及其安装路径。删除一个内核jupyter kernelspec remove kernel_name将kernel_name替换为你在jupyter kernelspec list中看到的名称是--name参数的值不是显示名。删除内核不会删除对应的虚拟环境只是移除了Jupyter的关联配置。重命名内核显示名Jupyter没有直接重命名命令。你需要找到内核的配置文件。根据jupyter kernelspec list列出的路径找到对应内核目录下的kernel.json文件修改其中的display_name字段然后保存即可。4.2 为现有环境添加内核常见补救场景很多时候我们可能先创建了环境并安装了大量包之后才想起来要关联Jupyter。操作很简单激活目标虚拟环境。在该环境中安装ipykernel如果还没安装的话。执行python -m ipykernel install ...命令注册。一个关键检查点注册后务必检查kernel.json文件中的argv路径是否指向了当前虚拟环境的Python解释器。有时如果环境未激活或路径有误可能会错误地指向了系统Python。4.3 内核与环境的对应关系排查如果发现在Jupyter里选择了某个内核但导入的包版本不对或者提示包不存在基本可以断定内核没有正确关联到虚拟环境。排查步骤如下在Notebook中检查Python路径运行import sys; print(sys.executable)。看看输出的路径是否是你期望的虚拟环境下的python路径例如/path/to/your/env/bin/python或C:\Users\...\envs\your_env\python.exe。核对内核配置文件根据jupyter kernelspec list找到该内核的路径打开kernel.json查看argv列表的第一个元素是否就是上一步你期望的Python路径。修正如果不一致最彻底的方法是先jupyter kernelspec remove删除错误内核然后确保正确激活目标虚拟环境重新执行ipykernel install命令。4.4 在Jupyter中切换内核对于一个已打开的Notebook文件.ipynb如果你想更换其运行内核例如从base环境切换到项目专用环境在Notebook界面点击顶部菜单栏的“Kernel”。选择“Change kernel...”。从弹出的列表中选择你想要的新内核。这个操作只改变当前Notebook使用的内核不会影响其他Notebook或系统设置。5. 常见问题与故障排除实录即使按照步骤操作也可能会遇到一些“坑”。这里记录了几个最常见的问题和解决方案。5.1 问题安装了ipykernel但在Jupyter里看不到新内核可能原因1Jupyter Lab 与 Jupyter Notebook 的区别。你是在Jupyter Lab里看的吗Jupyter Lab的启动器界面有时需要刷新。尝试点击左侧文件浏览器图标然后点击右上角的刷新按钮或者完全重启Jupyter Lab。可能原因2内核注册到了不同的Jupyter实例。如果你系统上有多个Python环境都安装了Jupyter确保你启动Notebook/Lab的命令行环境和你注册内核的环境使用的是同一个Jupyter或者说同一个jupyter kernelspec list所对应的配置目录。一个检查方法是在注册内核后立即在同一个终端里启动jupyter notebook。解决方案最直接的方法是在你想启动Jupyter的环境通常是base里也安装nb_conda_kernels这个包仅限Conda环境。它可以自动发现所有Conda环境中的内核无需手动注册。安装后重启Jupyter即可。conda activate base # 切换到你的base环境 conda install nb_conda_kernels5.2 问题在Notebook中导入包时出现ModuleNotFoundError但终端里明明安装了可能原因这是最典型的内核与环境未正确关联的症状。Notebook使用的Python解释器不是你安装包的那个环境。排查与解决在Notebook单元格运行import sys; print(sys.executable)。在终端中激活你认为正确的虚拟环境然后运行which pythonmacOS/Linux或where pythonWindows。对比两个路径。如果不同说明内核指错了地方。按照4.3节的步骤修正内核配置。5.3 问题Conda环境注册内核后在Notebook中安装包!pip install装到哪里去了关键理解在Notebook单元格中运行!pip install package_name这个pip命令是由当前Notebook内核背后的Python环境决定的。如果内核正确关联了你的Conda虚拟环境那么!pip install就会将包装到那个Conda环境里。风险提示但是在Conda环境里混用pip安装仍需谨慎尤其是安装可能涉及二进制编译的包时可能会破坏Conda的依赖一致性。建议优先使用!conda install。如果非要用pip可以尝试在单元格中用系统命令调用当前环境的pip!{sys.executable} -m pip install package_name这样更精确。最佳实践项目的依赖最好通过环境配置文件如environment.ymlfor Conda,requirements.txtfor pip来管理在终端中创建环境时一次性安装好而不是在Notebook里临时安装。5.4 问题如何彻底清理一个不再需要的环境及其内核删除Conda环境conda deactivate # 确保先退出该环境 conda remove -n your_env_name --all删除关联的Jupyter内核jupyter kernelspec remove kernel_name_used_during_install注意删除环境不会自动删除内核反之亦然。两者需要分别清理。5.5 内核启动缓慢或失败可能原因内核规格文件kernel.json中的路径错误或者目标虚拟环境中的Python解释器损坏。解决方案检查kernel.json的argv路径是否有效。尝试在终端中手动激活该虚拟环境并启动Python解释器看是否正常。重新创建虚拟环境并重新注册内核。6. 项目化与可复现性固化你的环境配置个人使用方便了但团队协作或项目部署时如何保证别人能复现一模一样的环境呢这就需要将环境配置“代码化”。6.1 对于Conda环境使用environment.yml在你的项目根目录激活目标环境后可以导出其配置conda activate ds_project conda env export environment.yml生成的environment.yml文件包含了环境名、Python版本、所有包的精确版本包括通过pip安装的如果用了conda env export --from-history则只导出你显式安装的包。其他协作者拿到这个文件后只需执行conda env create -f environment.yml就能创建一个一模一样的环境。记得把environment.yml加入版本控制如Git。6.2 对于venv环境使用requirements.txt在激活的venv环境中pip freeze requirements.txt生成requirements.txt。其他人创建venv后使用pip install -r requirements.txt来安装所有依赖。为了更清晰可以手动维护一个requirements.in文件只写顶级依赖如flask2.0.0然后用pip-compile来自pip-tools包生成精确版本的requirements.txt。6.3 将内核配置纳入项目你甚至可以更进一步将Jupyter内核的安装也脚本化。例如在项目的README或一个安装脚本setup.sh或setup.bat中包含注册内核的命令# 假设环境已创建并激活 python -m ipykernel install --user --name myproject --display-name Python (MyProject)这样新成员在配置好环境后一键就能将内核注册到Jupyter。7. 总结与个人工作流分享打通虚拟环境和Jupyter内核管理是我认为每个Python开发者特别是数据相关从业者必须掌握的基础技能。它带来的不仅仅是依赖隔离更是一种清晰、有序、可复现的工作习惯。我个人目前的工作流是每个独立项目必先建独立Conda环境。环境名与项目目录名保持一致或高度相关。创建环境后第一件事就是安装ipykernel并注册内核显示名会包含Python版本和主要用途例如[EDA] Py3.10-pandas2.2。通过environment.yml管理所有依赖并将此文件纳入Git。在Jupyter Lab中工作利用其多标签页特性同时打开多个不同内核的Notebook分别对应不同的分析任务或模型实验互不干扰。项目归档或交接时除了代码和数据一定会提供environment.yml和内核注册说明。最后一个小技巧如果你觉得每次都要手动输入一长串ipykernel install命令很麻烦可以将其写成一行简单的Shell函数或别名Alias放在你的Shell配置文件如.bashrc或.zshrc里。例如我定义了一个函数kenv()在激活虚拟环境后运行kenv “显示名”就能自动以环境名为基础注册内核大大提升了效率。这套流程一旦跑顺你会发现自己再也不会陷入“包版本地狱”项目管理和协作也变得轻松许多。

相关新闻

答辩演讲稿撰写指南与实用技巧分享

答辩演讲稿撰写指南与实用技巧分享

AI Agent时代的科研革命 这三个工具让你的效率提升十倍 传统科研模式正在被AI彻底颠覆。过去需要几周甚至几个月完成的文献调研和综述写作,现在几天就能搞定。过去需要反复调试才能复现的实验,现在一键就能完成。这三个基于最新AI技术的科研工具&#x…

2026/10/2 23:30:40 阅读更多 →
留学生论文神器|搞定Turnitin查重+英文润色+AI痕迹消除✅

留学生论文神器|搞定Turnitin查重+英文润色+AI痕迹消除✅

对于留学生来说,论文审核逻辑和国内完全不一样。 不是单纯降重就行,核心难点是:Turnitin重复率、AI Writing检测、英文句式地道度、学术语态合规。 很多同学作业分数低、被导师质疑AI代写、重复率飘红,不是写得不好,…

2026/9/26 16:15:15 阅读更多 →
终极Windows任务栏美化神器:3分钟打造Mac风格居中体验

终极Windows任务栏美化神器:3分钟打造Mac风格居中体验

终极Windows任务栏美化神器:3分钟打造Mac风格居中体验 【免费下载链接】TaskbarX Center Windows taskbar icons with a variety of animations and options. 项目地址: https://gitcode.com/gh_mirrors/ta/TaskbarX 还在羡慕Mac电脑那优雅居中的任务栏吗&am…

2026/10/10 10:45:51 阅读更多 →

最新新闻

Hive substr中文乱码根源与5种实战解决方案

Hive substr中文乱码根源与5种实战解决方案

1. 项目概述:当Hive的substr遇上中文乱码,不是函数错了,是字符集在“说方言”你有没有遇到过这样的场景:一条日志字段里明明存着“用户_张三_20240516_订单完成”,用substr(col, 4, 6)想截出“张三”两个字&#xff0c…

2026/10/14 13:49:58 阅读更多 →
接口性能优化实战:从2秒到200ms的三层击破法

接口性能优化实战:从2秒到200ms的三层击破法

1. 这不是玄学,是可复现的接口性能优化实战“接口响应时间从2秒压到200毫秒以内”——这句话在很多技术团队的周会上被反复提起,听起来像一句目标口号,甚至带点理想主义色彩。但在我参与过的三个高并发业务系统重构中,它是一条必须…

2026/10/14 13:49:58 阅读更多 →
Hive substr中文乱码根源与字符安全截取方案

Hive substr中文乱码根源与字符安全截取方案

1. 项目概述:为什么一个看似简单的substr函数会突然在中文场景下“罢工”最近在某数据中台的日常ETL任务维护中,我连续三天被同一个报错堵在工位上:java.lang.StringIndexOutOfBoundsException: String index out of range,堆栈指…

2026/10/14 13:49:58 阅读更多 →
Ponytail 阅读进度条插件:滚动进度原理、接入与性能优化指南

Ponytail 阅读进度条插件:滚动进度原理、接入与性能优化指南

做内容站这几年,我一直有个执念:长文阅读体验不能只靠排版,还得给读者一点“进度反馈”。早先试过在文章顶部加一个手写的滚动进度条,但那种零散代码维护起来太麻烦,后来换成了开源的轻量级滚动进度插件,也…

2026/10/14 13:49:58 阅读更多 →
iOS容器底层探秘:NSMutableArray、NSDictionary、NSSet与NSCache深度解析

iOS容器底层探秘:NSMutableArray、NSDictionary、NSSet与NSCache深度解析

做iOS开发久了,数组、字典、集合这些容器天天在用,但真被人问一句NSMutableArray底层到底怎么扩容、NSDictionary凭什么查得那么快、NSCache和NSMutableDictionary除了线程安全还有什么本质区别、copy和mutableCopy的坑到底在哪,很多人会愣一…

2026/10/14 13:49:58 阅读更多 →
整车八自由度Simulink动力学模型搭建:从方程到仿真验证

整车八自由度Simulink动力学模型搭建:从方程到仿真验证

做整车动力学仿真的人,几乎都会经历从二自由度自行车模型起步,再逐步往多自由度模型走的过程。二自由度模型能把横摆和侧向运动的基本逻辑讲清楚,但一涉及到载荷转移、侧倾对轮荷分布的影响、四个车轮独立的转速动力学,它就完全不…

2026/10/14 13:48:57 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →