1. 项目概述hindsight到底在解决什么问题如果你在技术社区搜索 hindsight 这个词大概率会碰到两条线索一条是 Google 的强化学习团队提出的 Hindsight Experience Replay解决稀疏奖励问题另一条是 Mozilla 开源的 Firefox 历史记录分析工具恰好也叫 Hindsight。我这次聊的是后者一个用 Python 写的、专门对 Firefox 浏览器历史数据进行深度解析和可视化分析的小工具。先说清楚它能做什么你只要给它一个 Firefox 的 profile 目录路径它就能把 places.sqlite 里那些底层数据库记录变成一份一目了然的 HTML 报告——你哪天访问过什么网站、访问了多少次、停留多久、是不是在深夜偷偷刷的页面甚至地理位置对应的经纬度它都能给你标出来。听起来像是有后见之明所以这个工具叫做 Hindsight 确实很贴切。我自己是在做浏览器取证browser forensics相关工作的时候接触到这个项目的。当时接了一个内部需求要排查一台工作机上 Firefox 的历史记录是否存在异常访问行为。一开始我直接用 SQLite 浏览器打开 places.sqlite 手工查表结构几十个字段时间戳还是微秒级精度但基准点是 1970 年换算起来相当头疼。后来搜到了 Hindsight几分钟跑完一份 30 多页的报告工作效率直接翻了好几倍。适合谁来读这篇笔记如果你在做安全取证、员工行为审计、个人数据导出备份或者纯粹想搞明白浏览器历史到底怎么存储的都可以参考一下。我会把工具的安装、核心参数、实操坑点、排查思路完整地梳理一遍全程基于我自己的真实操作经验。2. 分析逻辑拆解为什么浏览器历史这么难读2.1 Firefox 历史数据的存储机制要理解 Hindsight 的价值得先明白 Firefox 是怎么存历史的。我们知道 Chrome 会把历史放在一个叫 History 的 SQLite 文件里Firefox 对应的则是 places.sqlite位于 profile 目录下。这个文件包含了大量表其中 moz_historyvisits 和 moz_places 两张表是核心。moz_places 负责存每个 URL 的固定信息包括 url 字段、title 字段、rev_host反转域名、visit_count 等。moz_historyvisits 则负责存每一次访问记录包括 visit_time、visit_type、from_visit(来源页面)、place_id外键指向 moz_places.id。问题在于这些表的设计初衷是给浏览器自身高效读写用的不是给人类做分析用的。visit_time 用的是 PRTime 格式单位是微秒基准是 1601 年 1 月 1 日——因为 Firefox 内部使用 Windows NT 的文件时间标准。你直接写 SQL 查出来一长串数字第一反应肯定不知道是哪年。Hindsight 做的一个关键工作就是帮你把这些原始数据规格化把 PRTime 转成可读的 UTC 时间戳把 rev_host 反转成正常域名把 visit_type 的数字转成人话比如 1 代表用户直接输入的跳转2 代表链接跳转7 是地址栏输入。这种细节处理如果手工做SQL 要写好几层嵌套还容易算错。2.2 数据分析链路从原始 SQLite 到可视化报告再往深一层看Hindsight 不仅仅做字段转换它构建了一条完整的数据分析链路。第一步是读取 profile 目录下的所有相关文件除 places.sqlite 之外还包括 favicons.sqlite网站图标、cookies.sqliteCookie 记录、formhistory.sqlite表单历史。这些文件都会映射到内存中的 SQLite 数据库。第二步是提取数据。Hindsight 的提取器extractor模块会执行一系列预定义的 SQL 查询把每个维度的数据拉出来再封装成 Python 对象。这个过程中它会做去重、排序、过滤空值尽量减少后续分析的噪音数据。第三步是生成报告。开发者可以在配置文件里指定报告格式是 CSV 还是 HTML。如果选择 HTML 报告它还会调用 Python 标准库里的 html 模块生成一个带搜索框、排序表头和可视化图表的交互式页面。图表部分的实现很有意思——它直接在 HTML 里嵌入了 JavaScript 的绘图库不需要你额外配置任何外部服务。这条链路本身算不上复杂但它把从头写一遍解析脚本的成本降到了接近零。我在几年前第一次用的时候其实也想过自己写脚本去解析 places.sqlite但是越用越发现解析数据库只是最表层的工作各种边界情况的处理才是真正耗费时间的地方——比如数据损坏时的容错、旧版本数据库的兼容、FTS 索引的构建这些 Hindsight 已经帮你处理好了。2.3 为什么不用现成的浏览器清除历史记录功能对比很多人会问既然 Firefox 自带历史记录页面直接打开看不就行了吗这句话只对了一半。Firefox 自带的历史记录侧边栏和关于:历史页面展示的是经过浏览器自身清洗后的数据——它按天分组、仅显示部分字段、没有导出接口你无法对数据做复杂的过滤和二次分析。另外还有一个实际使用中的盲区Firefox 自带的界面搜索历史记录时只匹配 URL 和标题不能按访问时长排序也不能按来源页面追踪。例如你想找出某天从 A 页面跳到 B 页面然后坚持了 30 秒以上的所有记录原生界面肯定做不到。但 Hindsight 可以做到因为它离数据库最近本质上是直接和原始数据打交道。所以结论很简单如果只是偶尔想找回昨天看的那个页面浏览器自带的页面就够了如果你要的是数据粒度级别的分析和结构化导出Hindsight 这类工具才是正确的选择。3. 实操用 Hindsight 跑出你的第一份历史分析报告3.1 安装两种方式实测对比Hindsight 是一个 Python 项目安装方式有 pip 和源码运行两种。我推荐优先用 pip 安装因为依赖管理最省心。pip install hindsight装完之后命令行工具就注册好了。但要注意这个包在某些 Python 版本下会有依赖冲突尤其是如果你机器上同时装着多个版本的 SQLite 驱动。我自己在 macOS 上用 Python 3.11 装的时候遇到过二进制依赖编译失败的情况后来临时切到 Python 3.9 才顺利装上。如果你不想折腾环境可以直接从 GitHub 拉源码跑git clone https://github.com/obsidianforensics/hindsight.git cd hindsight python run.py源码方式的好处是你可以直接改代码比如定制 SQL 查询语句、调整报告输出的排序逻辑。我在做二次开发的时候就是用源码方式毕竟 pip 包里的代码改了不方便追踪。安装完成后测试一下hindsight -h如果能看到参数帮助列表说明安装成功。接下来只需要准备一个 Firefox 的 profile 路径。3.2 定位 Firefox Profile 目录并启动分析Firefox 的 profile 目录在不同操作系统下位置不一样操作系统默认路径WindowsC:\Users\用户名\AppData\Roaming\Mozilla\Firefox\Profiles\xxx.default-releasemacOS~/Library/Application Support/Firefox/Profiles/xxx.default-releaseLinux~/.mozilla/firefox/xxx.default-release如果你不确定当前使用哪个 profile可以在 Firefox 地址栏输入 about:profiles它会列出所有 profile 并用路径展示根目录位置。注意要选择正在使用中的那个不是所有的 profile 都有完整的历史记录。拿到 profile 路径后基本命令长这样hindsight -i /Users/你的用户名/Library/Application Support/Firefox/Profiles/xxxx.default-release -o 分析结果其中-i指定输入目录-o指定输出目录。输出目录不需要提前创建Hindsight 会自动建好但如果目录里已有同名文件它会提示报错——实测下来这个提示报错有点反直觉它会说 Output file already exists看起来像是程序崩溃了实际上删掉旧文件或者换个输出目录名就行。执行之后终端会打印出详细的日志包括读取了哪些文件、提取了多少条历史记录、处理了多少个 cookie 等。整个流程通常在 30 秒内完成具体时间取决于你的历史数据量。我试过一个用了两年的 profile占地大概 2GB分析时间也只要 40 秒左右。3.3 3.3 输出报告的核心看点跑完分析后输出目录里会出现一个 HTML 文件。我强烈建议你用浏览器打开它而不是用文本编辑器看源码。报告首页是一个概览仪表盘已分析的 profile 信息、提取的记录总数、从上一次分析开始的新记录数等。往下滚动你会看到一个覆盖整个时间轴的访问频率直方图每个柱子代表某个时间段内的访问量峰值。这个图对于快速定位某天突然大量访问某个域名非常有效。再往下是详细的访问记录表格包含时间、URL、标题、访问类型、来源页面等字段。表格支持按列排序也可以直接在搜索框输入关键词过滤。我在排查敏感域名访问的时候就是直接搜索域名关键字然后按访问次数倒序排列很快就能圈出可疑记录。如果还需要把数据导出给其他工具处理可以用 CSV 输出hindsight -i profile目录 -o 输出目录 -f csvCSV 文件的字段设计很完整包括时间戳UTC 格式、访问 URL、访问标题、来源 URL、访问类型描述、浏览器名称等。我用 Pandas 直接读这个 CSV 做后续统计分析一点障碍都没有。4. 深度参数玩法让 Hindsight 按你的思路干活4.1 按时间范围过滤排查指定时段的访问记录日常使用中最频繁的需求是只看某一段时间内的数据。Hindsight 内置了时间过滤参数支持两种格式一种是自然语言另一种是 Unix 时间戳。hindsight -i profile目录 -o 输出目录 --begin 2024-01-01 --end 2024-01-31我实测过自然语言格式对时区的处理它用的是本机时区的解释。如果你要精确到 UTC 时刻建议直接给 Unix 时间戳hindsight -i profile目录 -o 输出目录 --begin 1704067200 --end 1706659200这个参数组合在排查某次安全事件发生在 1 月 15 日 22:30 前后该浏览器访问了什么的场景里特别有用。你不需要把整份大报告导出来翻找直接锁定时间窗口输出数据量小了分析速度更快也更精准。4.2 4.2 用正则表达式精确筛URL另一个值得关注的是 URL 过滤参数它支持正则表达式匹配。比如你想找出所有包含 login 或 auth 的访问记录hindsight -i profile目录 -o 输出目录 --url_regex .*(login|auth).*这个参数底层实现是直接把正则应用到提取后的 URL 列表上所以匹配速度极快。我做一个数据泄露核查时用一步正则就把历史里所有包含 api. 和 token 的请求筛了出来省掉了手工逐行看表格的时间。注意事项正则表达式默认是区分大小写的如果你想忽略大小写需要把参数改成--url_regex ...配合--regex_ignore_case选项。我就是因为没加忽略大小写参数第一次筛选的时候漏掉了一部分大写 URL 的记录排查问题时走了弯路。4.3 4.3 地理位置映射看访问终端的物理位置轨迹Hindsight 还集成了地理位置解析功能。数据处理阶段它会为每个外连 IP从历史记录的下行流量信息推断出来的服务器 IP不是访问者本机 IP调用本地的 GeoIP 库解析出经纬度。开启方式是hindsight -i profile目录 -o 输出目录 --geoip如果提前下载了 GeoIP 数据库文件再加一个--geoip_db 数据库文件路径。解析出来的结果会展示在 HTML 报告的地图图层上你可以看到某一天的访问记录大致分布在哪些地理区域。这个功能对分析账号是否有异地登录风险有辅助价值但要注意——它定位的是你访问的目标服务器的大致位置不是你的真实位置比如你访问了一个部署在新加坡的网站地图上显示新加坡不代表你在新加坡。4.4 多 Profile 合并与浏览器类型扩展实际工作中一台电脑上可能有多个 Firefox profile日常账号一个、开发测试账号一个、甚至还有旧版本残留的 profile。Hindsight 支持逐个分析但如果你希望把多个 profile 的数据合并到同一份报告里需要自己写个简单脚本把输出目录拼起来——它目前没有内置合并参数。不过 Hindsight 的架构是支持扩展它的后端不仅能解析 Firefox还支持 Chrome 和 Chrome-based 浏览器的历史数据。具体用法是在命令行加-b chrome指定浏览器类型hindsight -i chrome历史目录 -o 输出目录 -b chromeChrome 的数据存储方式和 Firefox 不同其历史文件的字段结构也有差异但 Hindsight 已经封装好了所以你不需要为 Chrome 再去写一套解析脚本。它在调查跨浏览器数据关联性的时候发挥的作用是很大的同一台机器上一个人通常不会只在单一浏览器留下痕迹把 Firefox 和 Chrome 的数据放到同一份报告里对比才能还原完整的浏览轨迹。5. 常见问题与排查技巧实录5.1 profile 目录被占用导致数据库锁定报错一个很常见的问题是如果你运行 Hindsight 的同时 Firefox 还开着profile 目录里的 places.sqlite 会被 Firefox 进程独占锁定Hindsight 会因为无法读取数据库而报错具体表现为 DatabaseError: database is locked 或 SQLite 相关的 reader 错误。解决办法很简单分析前先完全退出 Firefox包括托盘中的后台进程。我在 macOS 上经常遇到的情况是 Firefox 明明关掉了窗口但后台还有个进程没退干净要用ps aux | grep firefox查一下直接 kill 相关进程后再运行 Hindsight。这个坑的深层原因是 SQLite 在 WAL 模式下的并发控制策略。Firefox 自身允许读写并发但外部进程无法在它持有写锁的时候打开数据库。Hindsight 没有内置重试机制所以最好从源头规避。5.2 旧版本 Firefox 数据库兼容性问题Firefox 的数据库结构不是一成不变的每次大版本升级都可能调整表结构或字段语义。Hindsight 对较新的 schema 支持较好但如果你分析的是一个古董级版本的 profile有可能遇到 Unknown column 或 no such table 错误。我的经验是先用 SQLite 工具打开 places.sqlite 看看表结构版本。若发现表结构里缺少某些 Hindsight 查询所依赖的字段有两条路可选——要么升级 Firefox 后用它的导入历史功能把旧数据迁移出来要么拿旧版 Hindsight 分支改查询语句。但说实话旧版本 profile 通常数据价值有限除非是取证场景必须保留原件否则我不太建议花大量时间去兼容。5.3 报告中文内容乱码默认情况下 Hindsight 输出的 HTML 报告使用 UTF-8 编码大部分中文标题能正常显示。但如果你发现某个页面下去后标题变成乱码问题多半出在数据源本身——Firefox 的 places.sqlite 在存储标题时如果遇到了历史遗留的非法字符编码会以某种非 UTF-8 方式保存Hindsight 提取后直接写入 HTML 就可能乱。处理办法是在命令行加--encoding_latin1或者--windows1252参数让 Hindsight 按特定编码重新解析。如果你不确定原始编码先逐个试看哪个参数下乱码消失。我在分析一台 Windows 老机器的 profile 时遇到过这种情况加--windows1252后中文标题就正常了。5.4 报告文件过大导致浏览器卡顿分析超过两年的历史记录时生成的 HTML 报告可能高达几百 MB浏览器打开会直接卡死或崩溃。这时候要用 CSV 输出替代 HTMLhindsight -i profile目录 -o 输出目录 -f csvCSV 文件通常只有几十 MB用 Pandas 或 Excel 打开即可。另一个办法是配合时间过滤参数把数据切分成月度或季度的小文件hindsight -i profile目录 -o 输出目录 --begin 2024-01-01 --end 2024-06-30 -f csv我习惯在分析大规模数据时优先 CSV 按月归档然后单独对重点月份生成 HTML 报告做可视化展示两全其美。5.5 排查用户访问时区和 UTC 时间混乱的问题Hindsight 报告默认展示 UTC 时间戳这对跨时区分析很有用但如果用户记的是本地时间就会产生混淆。我在一次排查中发现某人访问某网站的本地时间是晚上 23 点但报告显示 UTC 时间是下午 15 点两者差 8 小时中国时区。这个不是 Hindsight 的问题而是时区换算问题。要解决这个问题可以在生成报告后自己写个脚本把 UTC 时间加回来。Hindsight 没有内置时区显示参数这也是它的一个小短板。我在实际分析时会记清楚数据对应哪个时区处理前先统一换算成本地时间避免人为误判。6. 从工具到方法论hindsight 思维在数据分析中的延伸价值Hindsight 这个工具虽然只是针对 Firefox 历史数据的解析器但它的设计思路和分析路径给了我不少启发应用到其他数据分析场景中也特别有用。第一点是降低原始数据的读取门槛。很多数据分析任务卡在第一步——数据格式复杂、字段语义不明、时间基准特殊。与其自己从头写解析器不如先调研有没有现成工具可以把脏数据清洗成规范格式。正如 Hindsight 把 PRTime 转成人类可读的时间戳在做日志分析、流量分析、甚至是 Excel 导出的 ERP 数据时第一步永远是统一格式和字段语义。第二点是报告输出比查询结果更重要。Hindsight 的设计中非常突出的一点是它默认输出 HTML 报告而不是一串命令行打印的查询结果。这让项目非技术背景的同事也能直接查看和理解。我自己后来在公司内部做日志平台时借鉴了这个思路——查询结果可以导出一个自包含的 HTML 页面带过滤表格和时间轴可视化结果就是运维同事不再需要天天喊我帮忙查数自己打开页面就能定位问题。第三点是领域知识需要工具化为配置而非代码。Hindsight 把 Firefox 数据库结构的理解封装进了代码里你不需要知道 moz_historyvisits 是什么就可以完成分析。但这些领域知识是真实存在的、可积累的。对我而言把一个领域的经验沉淀成可用工具比每次都从 SQL 层开始写要高效得多。如果后续你还想扩展这个工具的能力可以考虑加一步把 Hindsight 输出的 CSV 导入到 Elasticsearch 或 SQLite 再做全文检索和统计聚合。在我的实际项目中我就用 Hindsight 输出的数据做了更细粒度的用户行为时间线分析——比如把某一个月内访问过的所有网页按主题聚类看看真实的工作路径和工作习惯。最后再分享一个我常用的技巧每次跑完 Hindsight 后我会顺手把 profile 目录复制一份压缩存档标注好日期。为什么因为后续如果需要重跑分析或者做数据对比直接解压旧 snapshot 就可以不需要动正在用的 profile。也是因为 Firefox 会自动清理和整理历史记录你当下不导出三个月后想倒查某天的情况数据可能已经被覆盖清除了。及时发现并归档存储才是发挥后见之明真正价值的起点。
企业数字化 ERP 产品动态
相关推荐
Redis缓存击穿解决方案与Redisson分布式锁实践 1. 项目背景与问题定义在微服务架构中,缓存是提升系统性能的关键组件。Redis作为主流的内存数据库,常被用作缓存中间件。但实际应用中,缓存击穿问题就像一把悬在头顶的达摩克利斯之剑——当热点key过期瞬间遭遇高并发请求,数据库可… · 2026/11/3 14:44:47
FUXA开源组态软件:工业自动化SCADA与HMI开发指南 1. FUXA组态软件概述FUXA是一款基于Web的开源组态软件,主要用于工业自动化领域的SCADA(监控与数据采集)、HMI(人机界面)和可视化看板开发。作为一款轻量级解决方案,它允许工程师快速构建现代化的工业数据可… · 2026/11/3 14:44:47
后见之明偏差如何毁掉复盘?用时间线与决策记录破解 “hindsight is 20/20”,这句英文俗语我读第一遍的时候只觉得是在说“事后看一切清楚”。后来干了十几年研发,参与过几十场大大小小的复盘会,才真正理解“事后”这两个字的杀伤力。每当线上事故复盘,总有人拍桌子说“这个配置错误… · 2026/11/3 14:43:44
Rust 构建 Nostr relay:从 git 克隆到 WebSocket 事件处理 1. 从 "buzz" 这个项目名说起:它到底想解决什么问题 第一次看到 "buzz" 这个标题,加上 Nostr、relay、Rust、git 这几个关键词,我脑子里第一反应是:这大概率是一个用 Rust 写的 Nostr 中继(relay&… · 2026/11/3 15:22:53
回形针虽小,藏着工程学:从弹性原理到办公选型全解析 如果你问我办公室里最不起眼但最离不开的小东西是什么,我的答案不是订书机,也不是便利贴,而是那盒躺在抽屉角落里、看起来毫无技术含量的回形针(paperclip)。这东西单价低到可以忽略不计,形状也就那么几道弯… · 2026/11/3 15:22:53
一枚回形针的无限可能:从夹持原理到手工搭塔指南 paperclip,就是回形针。我估计你工位抽屉里至少有一盒,但很少正眼看它。我最近把它当成一个正经项目研究了一遍,从结构原理、日常用法到手工搭塔,前后折腾了两三周,收获比我预想的多。这篇东西不打算讲教科书式的大道理… · 2026/11/3 15:22:53
深入理解JavaScript中的$subscribe机制与应用 1. $subscribe 的基本概念与用途在JavaScript编程中,$subscribe通常出现在响应式编程框架或状态管理库中,特别是像RxJS这样的库。它主要用于订阅一个可观察对象(Observable),当这个对象发出新值时,订阅者就能接收到更新。举个例子… · 2026/11/3 15:22:53
Mentor DxDesigner原理图设计参数设置与DRC避坑指南 简介:本资源是一份面向电子设计工程师、EDA初学者及高校相关专业学生的Mentor原理图与PCB协同设计入门教程,聚焦DxDesigner原理图创建、参数配置、页面管理、DRC验证及原理图转Layout打包全流程,解决从零搭建规范项目到工程交付的关键实操问题… · 2026/11/3 15:22:53
智能运维核心技术解析与制造业实践指南 1. 智能运维如何成为企业数字化转型的核心引擎 第一次接触智能运维这个概念是在三年前的一次数据中心故障排查中。当时我们团队花了整整36小时才定位到一个由内存泄漏引发的连锁故障,而事后分析日志发现系统早在故障发生前12小时就出现了异常指标。这件事让我深刻意… · 2026/11/3 15:21:52
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46