news 2026/10/8 8:53:40

3分钟自建RSSHub:插件化架构打造全网信息订阅与监控体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
3分钟自建RSSHub:插件化架构打造全网信息订阅与监控体系

前阵子群里有人吐槽:“现在想盯一个网站的内容更新,怎么这么难?要么天天手动刷,要么开一堆 App 被推送轰炸。”我回了一句:“你缺的是一个 RSS 订阅体系。”然后顺手把 RSSHub 加浏览器插件那套东西丢过去。十分钟后他在群里发了一串感叹号,说“这个太逆天了”。

这事其实一点都不神秘。把“万物皆可 RSS”这句话落到实处,靠的是一个插件化设计的开源项目——RSSHub。它的核心思路非常简单:任何网站,只要能抓到数据,就能通过一条“路由规则”变成标准的 RSS 订阅源。再配合浏览器端的自动发现插件、一个自建的阅读器,你确实可以在几分钟内把几十个网站、账号、关键词全部收进同一个信息流里。这就是标题里“3分钟订阅整个互联网”的真实含义,不是夸张,是操作流程本来就快。

这篇文章我会把这套东西从底层逻辑到落地实操完整讲一遍。适合谁看?信息重度消费者、做行业情报监控的产品经理、自媒体运营、科研人员,以及所有不想被算法喂饭、想重新拿回信息主动权的人。哪怕你完全没接触过 RSS,跟着步骤走也能跑通。

1. 先想明白为什么:RSS 这个老技术为什么值得重新捡起来

1.1 现在获取信息的几个真实痛点

说 RSS 之前,先看看我们现在每天是怎么获取信息的。打开今日头条、抖音、小红书,算法会告诉你“你可能喜欢”,于是你看到的信息越来越窄,越来越像你自己。需要跨平台追踪的 UP 主、公众号、微博博主、开源项目、论文作者,分散在各处,你被迫装了十几个 App。更要命的是,平台的推送逻辑是“它想让你看什么”,不是“你想看什么”。你明明只关心某个作者的新论文,平台却觉得你更该看热门八卦。

这种模式最大的问题是你永远在被动接收。你想主动订阅一个特定的人、一个特定的关键词,恰恰没有入口。搜索结果页是一次性的,过去的就过去了,你不会每天去重搜一遍;期刊网站你也很难天天刷,只能等着邮件提醒。

信息越来越丰富,获取越来越高效,其实是个错觉。效率高的是平台,不是你。

1.2 RSS 的核心价值:订阅粒度与信息主权

RSS 是二十多年前的老协议,但它的设计思想现在看仍然领先。它把“信息获取”拆成了两件事:你决定订阅什么,然后由阅读器统一拉取。没有算法,没有推荐,没有信息流排序。你订阅了某个作者,他发的东西就会一条不落地出现在你的阅读器里;你没订阅的,再热闹也和你无关。

这就是订阅粒度的问题。在 RSS 体系里,你可以做到非常精细的控制:

  • 订阅一个具体的 GitHub 仓库的 Release,只关注它是否发布新版本;
  • 订阅某个微博关键词的搜索结果,而不是某个博主的所有碎碎念;
  • 订阅一个学术作者的论文列表,他发了新文章你第一时间就能看到。

能做到这些,靠的不是某个大平台大发慈悲开放接口,而是一个插件化的中转服务——RSSHub。它的思路是把“抓取某个网站的特定内容”抽象成一条条可插拔的路由规则,社区里已经积累了上千条。你要做的只是找到你要的那条,填上参数,然后把它丢给阅读器。

RSSHub 这个名字起得很贴切:它像一个“路由枢纽”,你在一边输入目标网站,另一边输出标准 RSS。中间发生了什么你不用关心,你只需要知道,几乎任何有内容、有规律更新的网站,都可以通过它变成订阅源。

2. 插件化的核心秘密:路由机制是怎么把任意网页变成订阅源的

2.1 先拆解一下路由到底在干什么

在 RSSHub 里,每接入一个目标网站,就对应一条或多条“路由”。所谓路由,本质就是一段配置代码,它至少包含三样东西:

  • 匹配规则:URL 里哪段是动态参数,比如用户名、仓库名、关键词;
  • 抓取逻辑:请求目标网站的哪个地址,是否需要登录、是否需要渲染 JS;
  • 输出字段:把抓到的网页内容,映射成 RSS 标准里的标题、链接、摘要、发布时间。

如果你用过 Express 这类 Node.js 框架,对这个模式不会陌生。RSSHub 自己就是 Node.js 写的,路由的模式也很像,每条路由就是一个函数。比如 GitHub Release 这条路,用户在浏览器里访问的订阅地址可能是:

/rsshub/github/release/diygod/rsshub

翻译成实际行为就是:去 GitHub 抓 diygod 这个用户下 rsshub 仓库的 Releases 页面,把里面的版本号、更新说明、发布时间解析出来,生成一个 RSS XML。同样的规则,把用户名和仓库名换成别的,就变成了另一个项目的订阅源。

这就是“参数化”三个字的含义:规则是固定的,参数是活的。

2.2 为什么要做成插件化架构:一次跨领域的类比

这里我想用一个看起来完全不相关的领域来帮助你理解——SolidWorks 机械设计里的零件参数化插件。

用过 SolidWorks 的朋友都知道,做机械设计的时候,一个法兰盘、一根轴、一个齿轮,建模流程其实是高度重复的:选基准面、画草图、标注尺寸、拉伸切除、倒角……如果每个项目都从零开始建模,效率极低。参数化插件的做法是把这些重复动作抽象成规则,把关键尺寸暴露成参数,你改一个直径、改一个厚度,模型自动重新生成。

RSSHub 的插件化架构和这个如出一辙。RSSHub 的维护者不可能为几千个网站维护一套臃肿的主程序,所以把“针对某类网站的信息抓取”抽象成独立路由。主框架只负责三件事:请求分发、执行路由规则、把结果输出成 RSS。而每接入一个新的网站,只需要新增一个对应的路由插件,不动主框架。

插件化架构带来的三个直接好处,我觉得是这套体系能持续壮大的真正原因:

  • 社区协作门槛低:你不需要理解整个项目,只需要会写一条路由,就能为某个网站接入支持。RSSHub 的上千条路由就是这样被一点一点填出来的。
  • 单点故障可替换:某条路由挂了,只影响那一个订阅源,其他一切照常。你可以等社区修复,也可以自己临时改。
  • 路由即文档:每一条路由的参数说明本身就是使用文档,用户不需要看源码,只需要知道“填什么参数”。

说白了,插件化就是把“重复劳动”变成“规则复用”。SolidWorks 插件复用建模步骤,RSSHub 插件复用抓取逻辑。跨领域一看,设计思想是通用的。

2.3 近距离看一条路由的解析过程

拿一个典型的路由做示例,拆给你看。假设目标是订阅某个视频平台 UP 主的投稿动态,路由 URL 可能是:

https://rsshub.app/bilibili/user/video/22675798

这条地址拆开:

段落作用
bilibili目标站点标识,决定使用哪套抓取逻辑
user/video子路由,表示“用户投稿的视频列表”
22675798动态参数,即 UP 主的 UID

当请求到达 RSSHub,实际发生的事是:RSSHub 用这个 UID 拼接出目标站点对应的页面地址,然后发送 HTTP 请求,拿到页面 HTML 后根据路由里预设的解析规则,提取出每个视频的标题、简介、封面、发布时间,最后封装成 RSS 2.0 格式的 XML 返回。

这里有一个很多人会忽略的点:有些页面内容是通过 JavaScript 异步加载的,初始 HTML 里根本没有数据。RSSHub 对这类站点需要在路由层额外启用无头浏览器渲染,等 JS 执行完再去拿真实数据。这也是为什么自建实例时官方建议预留一定内存,因为一旦大量路由需要走实时渲染,资源消耗幕后就拉开了。

从“用户填参数”到“服务端抓页面”,再到“输出标准 RSS”,整套链路实际上就是一个从 URL 到 RSS 的转换器。理解了这条链路,后面我们看到任何“没有 RSS 的网站”都不会慌,因为你知道剩下的事只是找对路由和参数而已。

3. 从零实操:Docker 部署 RSSHub 加阅读器,3 分钟跑通

3.1 先选实例:公共实例还是自建

如果你只是想快速体验 RSSHub 的能力,最简单的办法是直接用官方提供的公共实例。RSSHub 的文档站上列出了大量现成的路由,你随便挑一个,拼好参数地址,直接粘贴到阅读器里就能订阅。我最初就是先这样试了一只“B站 UP 主”订阅源,确认输出正常后,才开始考虑正经搭建。

但公共实例有三个绕不开的问题:

  • 稳定性看运气:公共实例所有人共用,流量大,偶尔会出现限流甚至短暂不可用;
  • 部分目标网站对公共云 IP 不友好,反爬机制更严格;
  • 你无法查看日志,订阅源失效时很难排查到底是路由坏了还是实例被限了。

所以我的建议很直接:想认真用,就花 10 分钟自建一个。Docker 方案是目前最简单、最省心的选择。

3.2 Docker 自建 RSSHub:部署、更新、验证

先确认机器上已经装了 Docker 和 Docker Compose。没有的话先装上,这不是什么高级操作,网上教程一大堆。然后随便找个工作目录,写一个docker-compose.yml,内容大概是这样:

services: rsshub: image: diygod/rsshub:latest container_name: rsshub restart: always ports: - "1200:1200" environment: - NODE_ENV=production - CACHE_TYPE=memory - CACHE_EXPIRE=600 - LISTEN_INADDR_ANY=0 - CORS_ALLOW_ORIGIN=* volumes: - ./data:/app/data

保存后执行:

docker compose up -d

等服务跑起来,访问http://你的服务器IP:1200。能看到 RSSHub 的路由文档页面,说明服务已经正常。再顺手测试一条已知路由,比如用 GitHub 仓库的 Release 订阅,看能不能正常返回 XML,能返回就说明链路通了。

日常维护几乎为零,建议定期更新镜像:

docker compose pull docker compose up -d

这里有两个从实战中得出的提醒。一个是 RSSHub 默认监听 1200 端口,建议在前面套一层 Nginx 反代并且强制 HTTPS,否则订阅地址里带着 IP 和裸端口,在部分网络环境里很容易被拦截。另一个是环境变量里的CACHE_EXPIRE,我习惯设为 300 到 600 秒,这样即使目标网站短暂抽风,你也不会立刻在阅读器里看到一个刷新失败的源。设得太长又会让新内容延迟很久才出现,这个值需要平衡。

3.3 浏览器端神器:RSSHub Radar,让订阅源自己“跳”出来

自建完 RSSHub,先把浏览器插件装上。RSSHub Radar 是官方生态里的一个浏览器扩展,作用非常关键:当你打开任意一个网页时,它会在后台检查这个网站有没有对应的 RSSHub 路由规则,如果有,扩展图标上会亮起提示,点开后直接给出当前订阅地址。

这套体验完全改变了“找订阅源”的流程。以前你要先去 RSSHub 文档站翻路由清单,再对照参数拼接 URL,现在你只要正常浏览网页,它自动告诉你“这个页面可以订阅”。

以订阅某个 UP 主的视频为例:

  1. 在浏览器里打开他的主页;
  2. Radar 插件检测到页面匹配 B站的用户路由,图标亮起;
  3. 弹出面板里直接显示最终的订阅地址,点复制;
  4. 去阅读器里粘贴添加,完成。

整个过程不到 30 秒。

Radar 插件的本质是一个本地路由索引。它内置了 RSSHub 的文档数据库,负责匹配“URL 模式”和“路由规则”,不涉及任何抓取行为,所以非常轻量。

3.4 阅读器选哪个:FreshRSS 还是 Tiny Tiny RSS

订阅源有了,需要一个地方统一阅读。这张对比表是我自己的使用感受:

维度FreshRSSTiny Tiny RSS
部署难度简单,一个容器搞定略复杂,依赖 PostgreSQL 或 MySQL
多用户支持不支持,适合个人原生支持,适合团队共享
自定义扩展官方插件与主题较多有社区插件体系
界面风格清爽简洁高可定制,支持主题化
刷新机制依赖 cron 定时任务自带后台任务

我的建议很个人化:一个人用就上 FreshRSS,省心;团队几个人共用,或者你特别想折腾界面和数据关联,再考虑 Tiny Tiny RSS。

FreshRSS 的部署同样走 Docker:

services: freshrss: image: freshrss/freshrss:latest container_name: freshrss restart: always ports: - "8080:80" environment: - TZ=Asia/Shanghai - CRON_MIN=*/5 volumes: - ./data:/var/www/FreshRSS/data

启动后,浏览器访问http://你的服务器IP:8080,按安装向导初始化,然后在订阅管理里把雷达插件复制过来的地址粘贴进去,点添加。做完这一步,你的订阅体系已经成立了。

3.5 完整流程实测:3 分钟复现“订阅整个互联网”

下面是完整复现一遍。假设我的目标是订阅五个东西:一个 B 站 UP 主、一个 GitHub 仓库的 Release、一个微博关键词、一个行业科技媒体的全文、一个学术作者的论文列表。按顺序操作:

  • 安装 Docker 并部署 RSSHub,约 1 分钟(前提是已经安装了 Docker,否则会多花几分钟);
  • 部署 FreshRSS,约 1 分钟;
  • 浏览器装好 RSSHub Radar,打开目标网站逐个复制订阅地址,1 分钟内完成五个源的添加。

这就是“3 分钟”说法的来源。当然,如果目标是监控一批特定网站,主要工作其实花在前期调研,而不是操作本身。

这个流程实测下来稳不稳?稳。但有几个细节需要处理,不然会出问题:

  • 某些目标网站的路由需要额外的参数或环境变量支持,比如那些需要走无头浏览器的站点,自建实例时要按路由文档要求打开对应的环境变量;
  • RSSHub 的公共实例偶尔抽风,如果你要重度使用,一定要优先自建;
  • FreshRSS 的定时任务是靠 cron 驱动的,所以我上面特意设置了CRON_MIN=*/5,让它每五分钟拉取一次。

把这些处理好,你得到的是一套完全自主的信息获取系统。没有推荐算法干预,没有平台推送噪音,只有你真正在乎的内容。

4. 科学场景实战:用 RSS 精准追踪特定作者的文献更新

4.1 科研和行业情报场景下的痛点

前面说的都是折腾各种网站,实际上 RSS 的价值在科研场景里被严重低估了。做科研的人都有这个体会:盯住一个大牛比盯住一本期刊更高效。某个领域出了新论文、新进展,真正值得你关注的就是那么几个团队。但学术信息的获取方式至今仍然很原始:靠期刊订阅邮件、靠 Google Scholar 提醒、靠人脉互相转发。

这些方式要么延迟高,要么噪音大。邮件提醒经常被邮箱过滤规则误伤,期刊订阅只能监控特定刊物,跨库的效果很差。你想实现的目标其实很简单:指定一个作者,他一旦有新文献上线,我第一时间知道,且只让我看到这一条。

RSS 天然适合干这件事。因为任何“搜索结果页”和“作者公开页面”,本质上都是定期更新的内容源。只要有一条路由能把这些页面转换成 XML,追踪就成立了。

4.2 把作者检索变成可订阅源

RSSHub 里有一类路由正是为学术检索服务的。以 Google Scholar 为例,Scholar 的搜索结果页背后是一个支持强大检索语法的接口,你可以把检索词从“某个关键词”换成“某个作者”。核心参数我建议这样组织:

https://你的RSSHub地址/google/scholar/author:作者名字+Surname

具体实现的原理是:RSSHub 拿到这个参数后,把author:xxx作为检索式发给 Scholar 的搜索接口,拿回结果页,然后解析出论文标题、作者列表、摘要、引用次数和发布出处,输出成 RSS 条目。

注意:RSSHub 的路由参数在不同版本里可能有调整,具体名字以你部署实例的文档页为准。我在这里不写死路径,因为项目迭代很快,写死了反而容易误导。真正重要的是那套思路:找一个支持构建检索式的学术数据库,把检索式放进路由参数里,出来的 RSS 就是你定制的监控源。

同样的逻辑可以延伸到 PubMed。PubMed 的检索式本身是通过http://www.ncbi.nlm.nih.gov/pubmed/?term=...传递的,RSSHub 的 PubMed 路由支持把完整的检索式作为参数传进去,于是你可以订阅“某个关键词 + 某位作者”这样高度组合化的检索式,比单独订阅期刊的范围精确得多。

4.3 追踪链路搭建实录

我实际搭建过一个追踪三位研究者文献更新的订阅组,步骤可以做一次记录:

  1. 打开 Google Scholar,按作者名搜索,找到目标作者的学术主页,确认 URL 里包含该作者的唯一 ID;
  2. 在 RSSHub 文档站里找到 Google Scholar 相关路由,把作者的 ID 或检索式填入参数;
  3. 先用浏览器直接访问拼好的订阅 URL,看看能否输出 XML,以及摘要里是否包含了你关心的字段;
  4. 把这个地址挂进 FreshRSS,设置刷新频率为每 30 分钟一次;
  5. 给这个订阅源打上标签“文献追踪”,和其他资讯源做区分。

实测效果:这个订阅组跑了一个多月,新论文的发现时间基本能和 Google Scholar 官方提醒持平,甚至更快,因为 RSS 抓取是分钟级的。最关键的体验是,我的邮箱里再也不会混入学术提醒了,全部内容都安静地待在自己的信息流里,统一处理。

这里有一个很实际的避坑经验:Google Scholar 的反爬机制比较敏感,如果请求频率太高,容易返回 403 页面或验证码。所以自建 RSSHub 时,请务必把路由对应的缓存时间设大,比如一小时。缓存会让同一个订阅地址在短时间内只抓取一次,既保证更新速度,又不过度打扰目标网站。

5. 实战过程中的坑与排查记录

5.1 订阅源突然不更新?先定位问题在哪一层

订阅体系跑久了,总会遇到某个源“断更”。别急着怀疑是目标网站没更新,按以下顺序排查,问题很快能定位:

  • 先用浏览器直接访问订阅地址,看有没有内容输出。有,说明问题在阅读器刷新逻辑或网络链路;没有,问题在 RSSHub 或目标网站。
  • 再访问 RSSHub 的日志页面或容器日志。如果在日志里看到 403、429、5xx,基本可以断定目标网站拒绝访问或路由抓取逻辑失效。
  • 去看目标网站本身有没有改版。如果页面结构变了,路由里的解析规则就会失配,输出为空或字段丢失,这种情况只能等社区修复,或者自己改路由。

我遇到过最典型的案例是某个新闻类订阅源突然停更,排查后发现目标网站启用了新的反爬策略,RSSHub 发出的请求被当成爬虫拦截。最终解决方式是给 RSSHub 实例配置了代理出口和合理的 User-Agent 头,才恢复正常。

5.2 部署层面的几个高频问题

部署阶段最容易踩的坑:

现象原因解决办法
访问实例返回 504内存不足,Node 进程崩溃给容器预留至少 512MB 内存
镜像拉取失败网络问题配置镜像加速源,或使用代理
部分路由提示需要 Redis缓存类型选的是 memory,而已开启 Redis 依赖按路由文档开启REDIS_URL环境变量
HTTPS 证书反复报错没有配反代加一层 Nginx 或 Caddy 做终止 TLS

另外,如果你访问某个路由时提示“需要在环境变量中启用某功能”,别硬试,先去看这个路由在文档里的说明,很多时候是作者特意屏蔽了不稳定的源,等真稳定了才放开。

5.3 反爬限制:目标网站不是你想抓就能抓的

真正让 RSSHub 路由失效的头号原因是目标网站的反爬策略。常见的有:限制请求频率、检查 User-Agent、校验 Cookie、对数据中心 IP 段进行整体阻断。

作为使用者,你能做的调整有限,但有三个习惯能减少踩雷:

  • 自建实例的出口 IP 质量很重要,别用被滥用的公共 IP 段;
  • 对访问频率高的订阅源,把CACHE_EXPIRE调高,让每次抓取之间的间隔更长;
  • 大部分专业源和学术源对低频请求容忍度很高,反而是资讯类、社交媒体类更容易遇到风控。

5.4 像 SolidWorks 参数化插件一样,自己造一条路由

最后说一下扩展。RSSHub 的真正魅力在于,你不仅能“用别人写好的路由”,还能自己写路由。这就像 SolidWorks 里你不仅是使用现成的参数化插件,你还能根据自己的零件改进设计,把新的建模流程固化成新插件。

自己写路由并没有想象的难。RSSHub 项目的文档里已经给出了路由开发模板,一个最简单的路由核心代码大概是这样:

module.exports = { route: '/example/:id', parameters: { id: '唯一标识' }, name: '示例源', categories: ['其他'], async handler(ctx) { const { id } = ctx.params; const response = await fetch(`https://example-site.com/item/${id}`); // 解析页面内容,映射为 RSS 条目 return { item: [...] }; }, };

这段代码的意图很直白:接收参数id,请求目标页面,解析内容,返回条目数组。你只要会一点 JavaScript 基础,能看懂简单的网页结构,就能写出自己的路由。写完放在项目的lib/routes/目录下,本地测试通过后,可以直接提交 Pull Request 回馈给社区。

我自己写的第一条自定义路由,是给某个小众设计资源站用的。那个站没有任何 RSS 输出,首页也只展示最近十篇文章。按常规思路只能每天手动刷。后来我照着 RSSHub 的代码模板写了一条路由,把首页的列表解析成订阅源,挂进 FreshRSS 后就再也没手动打开过那个网站。

从设计理念上说,这和 SolidWorks 参数化插件是相通的:把重复的动作抽象成规则,把变化的部分暴露成参数,主程序保持精简,扩展能力全部交给插件。RSSHub 正是因为走了这条路,才可能覆盖上千个形态各异的网站。而 CSV 你也能利用这个机会,把任何你想要盯住的信息源变成自己的订阅,不需要等待哪个平台大发慈悲开放接口。

我个人在实际使用过程中最深的一点体会是:RSS 这套体系,越早搭越划算。刚开始接入了十几个源,确实还感觉不到和普通刷网页有多大区别。但当你慢慢积累了上百个精准源之后,那种信息完全按你意图流动的秩序感,是所有算法推荐都替代不了的。真正值得长期关注的,从来不是信息哪里最多,而是你能不能在最需要的地方,第一时间看到它。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 8:53:04

Java课程设计:飞翔的小鸟游戏源码与实现详解

简介:这是一份面向Java初学者与在校学生的飞翔的小鸟游戏完整实现源码,配套详细开发教程,适合用作期末大作业、课程设计或毕业设计参考。项目采用Java语言编写,代码注释清晰,新手也能看懂,部署简单&#xf…

作者头像 李华
网站建设 2026/10/8 8:52:00

ADS中DAC控件参数设置与量化噪声仿真验证指南

写这篇ADS软件操作的记录之前,我先说说自己的情况。我做射频链路仿真有些年头了,平时用得最多的就是Keysight ADS。早些年我基本只用它的谐波平衡(HB)和S参数仿真,后来做带数字预失真(DPD)和宽带…

作者头像 李华
网站建设 2026/10/8 8:51:59

NVIDIA板级设计工程师笔试核心能力解析

1. 这不是一场普通笔试:NVIDIA Board Design Engineer校招笔试的真实图谱如果你在春招秋招季搜到“NVIDIA 2025 Board Design Engineer 校招笔试”这个标题,别急着点开题库或背诵真题——先停三秒。这不是一道“考完就忘”的选择题测试,而是一…

作者头像 李华
网站建设 2026/10/8 8:51:08

ATTCK框架落地指南:行为驱动检测与威胁狩猎实战

这两年做企业安全运营的人,嘴边绕不开三个字母:ATT&CK。MITRE ATT&CK框架刚火起来的时候,我也没有特别在意,第一反应是它又是个威胁情报展示平台,甚至一度觉得矩阵图只是给领导汇报用的“花架子”。真正改变我…

作者头像 李华
网站建设 2026/10/8 8:50:23

IntelliJ IDEA插件开发:状态持久化、ToolWindow与打包实战

简介:IDEA插件开发手册(下)是一份面向中高级IDE插件开发者的PDF指南,针对基于JetBrains Runtime 17.0.9并兼容IDEA 2023/2024的插件开发环境,系统讲解语言类插件开发的核心技术。手册承上启下,重点剖析PSI程…

作者头像 李华
网站建设 2026/10/8 8:50:18

基于YOLO+LPRNet的中文车牌识别:从数据到部署全流程解析

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

作者头像 李华