密码找回这件事几乎每个做过系统运维或安全测试的人都遇到过。用户拍着脑袋说“我密码肯定没记错”结果试了十几次都登不进去或者接手一个老系统前任管理员留下的加密压缩包、数据库备份、内部工具账号密码早就无从考证。这时候最直接的想法就是能不能用程序把密码试出来Python 因为语法简洁、生态丰富成了很多人第一个想到的工具。这篇内容就围绕“用 Python 做密码恢复”这个主题把背后的原理、可落地的实现方式、参数怎么定、坑在哪里一次讲透。需要先把话说在前面这里讨论的所有技术只适用于你自己拥有合法权限的系统、你自己加密的文件、你自己搭建的测试环境以及明确授权的安全评估场景。未经授权对他人系统或数据做密码尝试性质完全不同于技术研究这一点没有任何模糊空间。下面所有示例都基于本地文件、自建靶场和离线数据展开不涉及任何在线服务的未授权访问。这篇文章适合三类人看一是刚接触 Python 想做点实际项目的初学者二是需要处理自己遗留加密文件的普通用户三是对密码强度、哈希存储这些概念感兴趣、想搞明白“为什么我的密码能被试出来”的技术爱好者。哪怕你只会写 print也能跟着把流程跑通。1. 先搞清楚“破解”到底在破什么很多人一上来就问“用什么库能破解密码”这个问题本身就问偏了。密码恢复不是一个单一动作而是根据你面对的目标类型走完全不同的技术路线。目标搞错了工具选得再花哨也是白费。1.1 三种常见目标类型与对应思路现实里能遇到的“密码”大致分三类每一类的处理逻辑差别很大。第一类是本地加密文件比如加密压缩包、加密文档、加密磁盘镜像。这类目标的特点是密码验证完全在本地完成不依赖网络你可以无限次尝试速度只受自己机器性能限制。这是最适合用 Python 练手、也最容易做出成果的场景。第二类是哈希值恢复比如你拿到一串 MD5、SHA1 或者更现代的哈希想知道原文是什么。这里的关键认知是哈希是单向的理论上不可逆所谓“破解”其实是用候选密码逐个计算哈希再比对。所以能不能成功取决于候选密码是否覆盖了真实密码。第三类是在线登录接口也就是对着一个登录页面反复提交账号密码。这类场景必须极其谨慎只在自己搭建的测试环境里做而且要考虑频率限制、账号锁定、日志告警等机制。真实系统上做这个既低效又高风险不在本文的实操范围内。把这三类分清楚你就明白为什么网上很多教程看起来能跑换到自己场景就失效——它们针对的目标类型和你手里的东西根本不是一回事。1.2 为什么 Python 是合适的工具选 Python 不是因为它“最强”而是因为它在几个关键维度上平衡得最好。密码恢复的核心瓶颈通常不在语言本身而在候选密码的生成逻辑、哈希计算的速度、以及多进程调度的效率。Python 的hashlib直接调用底层 C 实现单个哈希计算并不慢multiprocessing能把多核吃满字符串处理和文件读取的代码写起来又足够短改起来快。对比一下用 C 写当然更快但开发调试成本高改个候选生成规则要重新编译用现成的专业工具当然更省事但你想定制候选规则、想理解每一步在干什么就得自己动手。Python 正好卡在“够快、够灵活、够好懂”这个位置上。我个人的经验是单机做离线哈希恢复Python 多进程方案能跑到每秒几十万到上百万次尝试的量级对付大多数个人场景完全够用。1.3 一个必须先建立的认知成功率由候选集决定这是全文最重要的一句话密码恢复的成功率几乎完全取决于你的候选密码集合是否包含真实密码而不是你的程序跑得多快。程序再快候选集里没有正确答案跑一万年也没用程序慢一点但候选集精准几分钟就出结果。所以真正需要花心思的地方是候选集的构建策略——字典从哪来、规则怎么变形、掩码怎么设计。后面的章节会重点讲这块。很多人失败就失败在拿一个几万条的通用字典硬跑跑完没结果就以为技术不行其实是候选集太窄。2. 核心组件拆解一个密码恢复程序由什么构成不管目标类型是什么一个完整的密码恢复程序都可以拆成四个模块候选生成器、验证器、调度器、结果记录器。把这四块想清楚代码结构自然就清晰了。2.1 候选生成器决定成败的核心候选生成器负责源源不断地吐出“可能的密码”。它的设计直接决定成功率。常见的生成策略有这么几种实际使用中往往是组合起来用。字典直读是最基础的方式从文本文件里一行一行读候选密码。字典的质量比数量重要得多一个针对目标场景定制的小字典效果往往好过一个几十 GB 的通用大字典。规则变形是在字典基础上做变换比如首字母大写、末尾加数字、字母替换a 换成 、o 换成 0、追加常见年份等。真实用户的密码很多是“单词变形”的结构规则变形能大幅扩展覆盖面。掩码生成适合你知道密码大致格式的情况比如“8 位纯数字”“字母开头加 6 位数字”。掩码用占位符描述每一位的字符范围程序按位穷举。这种方式在格式已知时效率极高。纯暴力穷举是按字符集全排列理论上能覆盖所有组合但组合数随长度指数增长实际只在很短密码上可行。6 位纯数字是 100 万种组合8 位纯数字就是 1 亿种再往上就基本不现实了。下面这个表格把几种策略对比一下方便你按场景选策略适用场景覆盖能力效率实现难度字典直读有高质量字典取决于字典高低规则变形密码有规律中等偏上中中掩码生成格式已知该格式内全覆盖高中纯暴力穷举极短密码理论全覆盖极低低实操中我一般按“字典直读 → 规则变形 → 掩码 → 短密码穷举”的顺序推进前面的跑完没结果再上后面的避免一上来就做最笨的穷举。2.2 验证器判断候选对不对的那一步验证器的职责很单一拿到一个候选密码判断它是不是正确的。不同目标类型验证方式完全不同。对加密压缩包验证方式是尝试用候选密码解压能解出内容就是对的。对哈希值验证方式是用同样的算法计算候选密码的哈希和目标哈希比对。对加密文档验证方式是尝试打开文档。这里有个性能细节值得说验证器的单次开销越小整体速度越快。比如哈希比对如果目标哈希是 MD5那每次验证就是一次 MD5 计算很快如果是刻意设计得慢的哈希算法比如带大量迭代的密钥派生函数单次验证就慢得多这时候优化重点就变成减少无效验证而不是抠那点计算速度。2.3 调度器把多核用起来单进程跑密码恢复CPU 通常只有一个核在忙其余核闲着浪费严重。调度器的作用就是把候选任务分发给多个进程并行处理。Python 里做并行要注意一点由于全局解释器锁的存在多线程对纯计算任务帮助不大真正能提速的是多进程。multiprocessing模块的Pool是最常用的方式把候选密码分批交给进程池每个进程独立验证谁先找到就通知主进程停止。调度设计里有个容易忽略的点找到结果后如何快速停止其他进程。如果处理不好主进程已经拿到答案了其他进程还在傻跑白白浪费时间。常见做法是用一个共享的事件标志所有进程定期检查这个标志一旦置位就主动退出。2.4 结果记录器别让跑出来的结果丢了这个模块最不起眼但踩坑的人不少。跑了几小时终于出结果结果程序崩溃或者输出被覆盖前功尽弃。稳妥的做法是每验证成功一个就立即追加写入文件并刷新缓冲区同时在控制台打印。不要攒在内存里最后统一写。另外建议记录尝试进度比如每处理十万个候选就输出一次当前进度和耗时这样你能估算剩余时间也能在中断后知道从哪继续。3. 动手实现从零写一个可用的密码恢复脚本理论说够了直接上代码。下面以“恢复自己加密压缩包的密码”和“恢复一个哈希值”两个场景为例把完整流程走一遍。所有代码都在本地环境运行目标都是自己创建的文件。3.1 场景一恢复自己加密压缩包的密码先准备环境。Python 3.8 以上即可标准库够用不需要额外装包。如果你要处理的是 ZIP 格式标准库zipfile直接支持如果是 RAR 或 7z需要对应的第三方库安装前确认来源可靠。先创建一个测试用的加密压缩包方便验证脚本是否有效import zipfile # 创建一个测试压缩包密码设为 test1234 with zipfile.ZipFile(demo.zip, w, zipfile.ZIP_DEFLATED) as zf: zf.writestr(secret.txt, this is a test file) # 注意标准库写加密 zip 支持有限实际测试可用系统工具创建标准库对写入加密 ZIP 的支持比较有限实际测试时可以用系统自带的压缩工具创建一个带密码的压缩包密码自己设定比如test1234然后拿下面的脚本来恢复验证逻辑是否正确。核心恢复脚本如下import zipfile import sys from multiprocessing import Pool, Event TARGET demo.zip FOUND Event() def try_password(password): if FOUND.is_set(): return None try: with zipfile.ZipFile(TARGET) as zf: zf.extractall(pwdpassword.encode(utf-8)) FOUND.set() return password except Exception: return None def generate_candidates(dict_path): with open(dict_path, r, encodingutf-8, errorsignore) as f: for line in f: pwd line.strip() if pwd: yield pwd def worker(pwd): result try_password(pwd) if result: print(f[] 找到密码: {result}) with open(result.txt, a, encodingutf-8) as f: f.write(result \n) return result if __name__ __main__: candidates list(generate_candidates(wordlist.txt)) print(f候选总数: {len(candidates)}) with Pool(processes4) as pool: for i, res in enumerate(pool.imap_unordered(worker, candidates, chunksize100)): if res: pool.terminate() break if i % 10000 0: print(f已尝试 {i} 个)这段代码有几个设计点值得解释。FOUND事件用于在找到密码后让其他进程尽快退出imap_unordered不保证顺序但吞吐更高适合这种“找到就停”的场景chunksize100减少进程间通信开销候选量大时效果明显。实测下来4 进程处理一个十万条的字典几秒钟就能跑完。如果你的压缩包密码在字典里基本是秒出。3.2 场景二恢复一个哈希值哈希恢复的逻辑更纯粹给定目标哈希和算法用候选密码逐个计算比对。先算一个测试哈希import hashlib print(hashlib.md5(test1234.encode()).hexdigest()) # 输出类似a1b2c3...实际值以运行结果为准拿到这个哈希后用下面的脚本恢复import hashlib from multiprocessing import Pool TARGET_HASH 把上面算出来的哈希填这里 ALGO md5 def check(pwd): h hashlib.new(ALGO, pwd.encode(utf-8)).hexdigest() if h TARGET_HASH: return pwd return None def load_dict(path): with open(path, r, encodingutf-8, errorsignore) as f: return [line.strip() for line in f if line.strip()] if __name__ __main__: words load_dict(wordlist.txt) with Pool(processes4) as pool: for res in pool.imap_unordered(check, words, chunksize500): if res: print(f[] 原文: {res}) pool.terminate() break哈希恢复比压缩包恢复快得多因为单次验证只是一次哈希计算没有文件解压的开销。4 进程跑 MD5每秒几十万次尝试是常态。如果目标哈希是更慢的算法速度会相应下降这时候候选集的精准度就更重要。3.3 加入规则变形扩大覆盖面光靠字典直读很多真实密码试不出来因为用户会在单词上做变形。加一层规则变形命中率能明显提升。下面是一个简单的变形生成器def mutate(word): yield word yield word.capitalize() yield word.upper() yield word 123 yield word 2024 yield word ! yield word.replace(a, ).replace(o, 0) yield word.capitalize() 123把字典里每个词都过一遍mutate候选数量会膨胀好几倍但覆盖的密码模式也多了很多。实际使用时可以根据目标场景裁剪规则比如你怀疑密码是“英文名年份”就重点保留年份追加的规则。这里有个经验规则不是越多越好。规则太多候选集膨胀跑得慢而且大量无效候选稀释了效率。我一般先上最常用的几条首字母大写、末尾加 123、末尾加年份、常见字符替换跑完没结果再逐步加规则。3.4 掩码生成格式已知时的利器如果你确定密码是某种固定格式掩码生成比字典高效得多。下面是一个简单的掩码实现支持数字和字母占位import itertools def mask_generate(pattern): # ?d 表示数字?l 表示小写字母?u 表示大写字母 pools [] for ch in pattern: if ch ?d: pools.append(0123456789) elif ch ?l: pools.append(abcdefghijklmnopqrstuvwxyz) elif ch ?u: pools.append(ABCDEFGHIJKLMNOPQRSTUVWXYZ) else: pools.append(ch) for combo in itertools.product(*pools): yield .join(combo)比如?l?l?l?l?d?d?d?d表示“4 个小写字母加 4 个数字”组合数是 26^4 × 10^4约 45 亿种。这个量级单机跑起来要很久所以掩码设计要尽量精确位数越少越好。如果知道是 6 位数字?d?d?d?d?d?d只有 100 万种几分钟就能跑完。掩码的威力在于“精确打击”但前提是你对格式的判断准确。判断错了跑再久也没用。所以用掩码前先尽量收集关于密码格式的线索。4. 性能优化与常见问题排查脚本能跑通只是第一步真正拉开差距的是性能调优和问题排查能力。这一块的经验很多是踩坑踩出来的。4.1 进程数到底设多少合适一个常见误区是把进程数设成 CPU 核数的好几倍以为能更快。实际上密码恢复是纯计算密集型任务进程数超过物理核数后上下文切换开销反而拖慢整体速度。我的经验是设成物理核数或核数减一留一个核给系统调度。可以用下面的方式查看核数import os print(os.cpu_count())如果是 8 核机器设 7 到 8 个进程比较合适。设 16 个进程不会更快只会让每个进程分到的时间片变少。4.2 候选集太大导致内存爆掉如果直接把整个大字典读进列表再交给进程池几十 GB 的字典会直接把内存吃满。解决办法是用生成器分批喂给进程池或者用imap而不是map让任务按需分发。前面示例里用imap_unordered就是这个考虑。如果字典实在太大可以考虑先做预处理去重、去空行、按可能性排序把最可能命中的候选放前面这样即使跑不完也能先覆盖高概率部分。4.3 找到结果后进程没停干净这是并行脚本的经典问题。主进程拿到结果后调用pool.terminate()但某些情况下子进程还在处理手头的任务导致程序退出延迟。更稳妥的做法是结合共享事件标志让子进程自己检查并退出主进程再 terminate 兜底。前面示例里FOUND事件就是干这个的。4.4 常见问题速查表把实际遇到的高频问题整理成表方便对照排查现象可能原因解决方向跑完字典没结果候选集不含真实密码加规则变形或换更精准字典速度远低于预期进程数设置不当调整为物理核数附近内存持续上涨一次性加载大字典改用生成器分批处理程序退出卡住子进程未及时停止加共享事件标志压缩包验证报错加密方式不支持确认库是否支持该格式哈希比对总不匹配编码或大小写问题统一用 UTF-8注意哈希大小写4.5 几个容易被忽略的实操心得第一先小规模验证逻辑再上大规模。拿一个已知密码的小字典跑一遍确认能找到再换大字典。很多人直接上大字典跑了几小时没结果根本分不清是逻辑错了还是候选集不够。第二记录每次尝试的配置和结果。字典用了哪个、规则开了哪些、跑了多久、有没有命中这些信息记下来下次遇到类似场景能直接复用经验。第三注意字符编码。密码里可能有中文、特殊符号统一用 UTF-8 处理读取字典时加errorsignore避免个别坏行导致整个程序崩溃。第四别迷信大字典。一个针对场景定制的小字典命中率往往高于通用大字典。比如你怀疑密码和某个项目相关就把项目名、相关词汇、常见变形整理成小字典效果比盲目跑通用字典好得多。5. 从技术到认知密码强度到底该怎么看跑通这些脚本之后你会对“密码强度”有完全不同的理解。以前觉得“加个感叹号就安全了”实际跑一遍就知道常见变形规则几分钟就能覆盖。5.1 为什么短密码和常见词最危险从恢复的角度看密码的安全性取决于搜索空间的规模。纯数字 6 位是 100 万种组合现代机器几分钟跑完8 位纯数字是 1 亿种也就几小时的事。而一个 12 位的随机大小写字母加数字加符号的组合搜索空间大到单机基本不可能穷举。但现实中大多数人的密码并不是随机字符串而是“常见词简单变形”。这类密码的搜索空间远小于理论值因为攻击者不需要穷举所有组合只需要覆盖常见的构词模式。这就是为什么字典加规则的方法命中率那么高。5.2 对普通用户的实用建议基于上面的原理给普通用户的建议很直接密码长度优先于复杂度能用长密码就别纠结特殊符号避免用常见词做基础再简单变形不同系统用不同密码避免一处泄露处处沦陷重要的账号开启额外的验证方式。对开发者来说存储用户密码时绝对不能用快速哈希算法直接存要用专门设计的、计算开销大的密钥派生函数并且加盐。这样即使数据泄露逐个恢复的成本也高到不可接受。这一点在写任何涉及账号的系统时都是底线。5.3 技术能力的边界与责任掌握密码恢复技术本质上是在理解“密码验证机制是如何工作的”。这个理解可以用来做安全评估、可以用来找回自己的数据、也可以用来设计更安全的系统。但它同样可以被滥用所以边界必须清楚只对自己有权限的目标动手只在授权范围内测试发现系统弱点时走正规渠道反馈而不是利用。我在实际做安全测试时最深的体会是技术本身是中性的决定性质的是使用场景和授权状态。同样一段脚本用在自己搭建的靶场上叫学习用在没授权的系统上就是另一回事了。这个界限每个学技术的人都应该刻在心里。最后分享一个实操中的小习惯每次写完恢复脚本我都会先拿自己设的测试密码跑一遍确认逻辑正确、速度符合预期再考虑用在真实场景。这个“先自测再实战”的习惯帮我避免过很多次因为逻辑错误白跑几小时的情况。技术这东西稳扎稳打比追求花哨重要得多。
企业数字化 ERP 产品动态
相关推荐
Claw桌面端微信整合工具配置与优化指南 1. 项目背景与核心价值Claw作为一款新兴的桌面端工具,其与个人微信的深度整合能力正在改变我们的日常沟通方式。这个方案最吸引我的地方在于它完美解决了传统微信客户端的一些痛点:比如多设备登录限制、消息管理效率低下等问题。经过两周的实测ÿ… · 2026/11/3 16:32:54
知识推理与表示:AI核心技术解析与实践指南 1. 知识处理与推理的核心价值 在人工智能领域,知识处理与推理技术就像人类大脑的"思考引擎"。我从事AI研发十余年,见证了这个领域从早期的规则系统发展到如今的复杂神经网络架构。知识推理不仅仅是简单的信息检索,它涉及对知识的深… · 2026/11/3 16:32:54
微信支付回调通知收不到:链路、验签与兜底排查 凌晨两点被电话叫起来,说小程序里一堆用户付了钱但订单还是"待支付",客服后台全是投诉。打开日志一看,我们自己的订单表里根本没有那批支付记录,说明支付平台的异步回调通知压根没到。这就是典型的微信支付回调通知收不… · 2026/11/3 16:32:54
RLPR-Qwen2.5:无验证器也能实现推理飞跃? RLPR-Qwen2.5:无验证器也能实现推理飞跃? 【免费下载链接】RLPR-Qwen2.5-7B-Base 项目地址: https://ai.gitcode.com/OpenBMB/RLPR-Qwen2.5-7B-Base
导语:OpenBMB团队推出的RLPR-Qwen2.5-7B-Base模型,通过创新的RLPR框架实… · 2026/11/3 18:47:28
阿里通义Wan2.1图生视频模型轻量化部署与效率优化指南 阿里通义Wan2.1图生视频模型轻量化部署与效率优化指南
阿里通义Wan2.1图生视频模型是一款高性能的AI视频生成工具,能够将文本描述或图像转化为高质量视频内容。本文将从技术原理、环境搭建、参数调优到行业应用四个维度,全面解析该模型的轻量化部署方案与… · 2026/11/3 18:47:28
Moonlight-16B震撼登场:Muon优化效率提升2倍 Moonlight-16B震撼登场:Muon优化效率提升2倍
导语:MoonshotAI推出的Moonlight-16B-A3B大语言模型,通过Muon优化器实现训练效率翻倍,以5.7T tokens训练量超越同类模型性能,重新定义大模型训练效率新标准。
行业现状&a… · 2026/11/3 18:47:28
3步颠覆视频创作:阿里通义Wan2.1量化模型实战指南 3步颠覆视频创作:阿里通义Wan2.1量化模型实战指南
让设计师/教育者/创作者0代码实现专业级动态视频生成
🔍 痛点诊断:视频创作的三大困境
你是否曾遇到:明明创意十足,却被困在Premiere的时间轴里整整三天?为… · 2026/11/3 18:47:28
LG发布2360亿参数K-EXAONE大模型:多语言+256K长上下文 LG发布2360亿参数K-EXAONE大模型:多语言256K长上下文 【免费下载链接】K-EXAONE-236B-A23B 项目地址: https://ai.gitcode.com/hf_mirrors/LGAI-EXAONE/K-EXAONE-236B-A23B
LG电子旗下AI研究机构LG AI Research正式推出参数量达2360亿的大语言模型K-EXAONE-… · 2026/11/3 18:47:28
ERNIE 4.5-VL震撼登场:424B参数多模态AI新体验 ERNIE 4.5-VL震撼登场:424B参数多模态AI新体验 【免费下载链接】ERNIE-4.5-VL-424B-A47B-Base-PT 项目地址: https://ai.gitcode.com/hf_mirrors/baidu/ERNIE-4.5-VL-424B-A47B-Base-PT
导语:百度ERNIE系列再添重磅成员——ERNIE 4.5-VL… · 2026/11/3 18:46:28
双极步进电机控制实战: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