网页端签名参数wsgsig逆向全流程:从定位到工程化落地

做网页端数据采集和自动化的人,手里多少都会攒着几个“说不清名字的参数”。wsgsig就是我在某个出行平台的网页端里遇到的那一个。它挂在每个关键请求的URL尾巴上,名字一看就是缩写:sig是签名(signature),前面那一小段wsg大概率是内部模块或场景代号。换句话说,这地方做了参数签名校验,想靠“复制Cookie”或者“简单改改参数”去调接口,基本行不通。

这篇东西我不打算写成一篇“教你怎么绕过某某平台风控”的教程,那既不合规也没意思。我更想把它当成一次完整的参数逆向与工程化落地复盘来聊:从第一次在抓包里看到wsgsig,到定位它的生成逻辑,再到把黑盒还原成可复算的函数,最后聊清楚落地到自动化脚本时要处理的那些坑。适合正在研究各类网页端签名机制、或者想系统梳理“签名参数逆向”思路的朋友。

1. 初见wsgsig:藏在URL尾部的签名参数

1.1 第一次抓包时看到的画面

事情的开头很普通。我想基于某出行平台的网页端,做一些合规范围内的数据观察和分析。打开浏览器开发者工具,切到Network面板,刷新页面,随便触发一个搜索或者列表加载请求,然后一眼扫过去——URL末尾多了一截东西:

https://xxx.example.com/search?keyword=xxx&page=1&wsgsig=0f6b2a...

这串参数长度不短,像是一段十六进制字符串,偶尔还带一些大写字母。第一反应是“又来了,签名”。为了确认它到底是不是必选参数,我先把wsgsig手动删掉,重新发送同一个请求,返回结果直接变成一个错误码,提示大概意思是“参数校验失败”。

到这里基本可以下一个结论:这个接口并不关心你有没有登录态,它关心的是请求参数有没有被篡改。也就是说,wsgsig不是权限凭证,而是一个完整性校验值——相当于给请求参数盖了一个章,服务端收到请求后自己再算一遍这个章,对不上就直接拒绝。

这种设计在很多网页端都能见到。不同平台的命名方式五花八门,有的叫sign,有的叫token,有的叫_signature,而这里的wsgsig是“模块前缀+signature缩写”的组合。理解了命名习惯,就不容易被参数吓到,本质上它就是一段可计算的字符串。

1.2 参数职责和基本特征推测

在正式动手逆向之前,我习惯先通过外部观察把候选参数的特征列出来,这样后面定位代码时才有方向。wsgsig的几个明显特征是这样的:

观察项现象推测
出现位置URL query末尾生成时机在请求发出前
字符形态十六进制+大写/小写混合可能是MD5/SHA家族摘要
是否变化请求参数或时间变化时随之变化参与计算的输入是动态的
是否必填删掉后请求被拒绝服务端做了强校验
与Cookie关系不明显无登录态时也存在与Session无关,属于请求级签名

这里有个容易被忽略的细节:wsgsig并不是只在“需要登录”的接口里出现。有些平台的列表页、搜索页,哪怕你完全没登录,请求里也会带类似参数。这说明签名校验往往前置在网关层,用来拦截批量脚本和异常流量,而不是等到业务逻辑再判断。

另一个细节是,wsgsig在请求里的位置有时候会被URL编码处理。比如原始生成的字符串里如果带有+、=、/这些字符,放到URL里可能变成%2B、%3D、%2F。抓包的时候如果看到这类编码痕迹,恢复原始字符串时要注意解码,否则后面比对签名时会莫名其妙对不上。

2. 从调用栈倒推定位:把生成函数从混淆代码里挖出来

2.1 断点位置选在哪儿最省事

要找到wsgsig的生成逻辑,最直接的手段不是读完整份压缩后的JS,而是“在请求发出前截住它”。我习惯用Chrome开发者工具的Sources面板,给fetch或者XHR打一个全局断点。

操作路径很固定:打开Sources面板,右侧找到Event Listener Breakpoints,展开Fetch和XHR,把request勾上。这时候再去页面里触发一次搜索,浏览器会在真正发请求前停下来,调用栈里会完整展示“到底是哪一行代码把这个请求送出去的”。

关键技巧是:不要一上来就在调用栈最底层去找加密函数,而是先看当前作用域里有没有已经生成好的wsgsig参数。很多时候,断点停下来时,请求参数对象就躺在Scope面板里,里面已经包含完整的签名值。这样你至少先拿到了“参数输入和最终签名结果”的一对样本,后面判断算法会轻松很多。

如果断点停在的位置只能看到URL字符串,看不到参数对象,那就沿着调用栈往上走一层。通常发请求的函数会有类似request({ url, params })的结构,params里一定藏着wsgsig。在这一层再做一次断点,右键点击调用栈里的上层函数,选择Restart frame,可以重新执行一次这个过程,方便反复观察。

2.2 三个最磨人的干扰项

定位过程不会一路顺风。以我这次的经验,下面三个干扰项最耽误时间,提前知道能省不少功夫。

第一是压缩混淆。网页端的JS几乎都是压缩过的,变量名全是t、n、e这种单字母。函数一层嵌套一层,找起来很痛苦。应对办法是:先不看变量名,而是看字符串常量。因为签名算法里必然要拼接某些固定文本,比如盐值、字段名、特定标识。在代码里搜索wsgsig这个字符串本身,或者搜索接口路径的一部分,往往能直接跳到生成签名的附近。

第二是异步时序。有些平台把签名生成放在Web Worker里,或者在一个延迟执行的Promise回调里。单纯靠断点去“等”可能等不到。这时候可以在代码里搜索特征字符串,找到后用关键字断点(XHR/fetch Breakpoints)精确命中。也可以直接把生成签名的那段逻辑附近的代码打上日志断点,输出中间变量的值。

第三是加密SDK的包装。某些站点会引入自研的加解密SDK,对外只暴露一个encrypt()函数,内部做了一堆跳转。你以为找到了入口,结果进去发现还有十层。这种场景不用死磕SDK内部,先通过输入输出推测算法类型,再做交叉验证就好。

2.3 用“最小复现脚本”锁定生成入口

通常我不满足于“我只能在一个特定页面上让它跑起来”,我更希望有一个不依赖界面的最小复现方式。做法是:把生成的签名函数单独抠出来还不够,因为它的上下文依赖太多;更可靠的办法是,把请求参数传到页面已有的函数入口里,在浏览器控制台执行一遍,看能不能得到一致的wsgsig。

具体来说,找到生成wsgsig的入口函数后,用控制台手动构造一组参数,调用一次这个函数,对比它与真实请求里的参数是否一致。如果一致,说明你已经找到了正确的计算入口。之后要做的,就是把这块逻辑移植到你自己的环境里。

这里有个非常容易踩的坑:很多签名函数内部依赖全局对象,比如window、document、location。你在控制台里调用没问题,因为控制台本身就在页面环境里,但一旦把这些代码搬到Node环境,分分钟报错。所以第一版本的移植,尽量不要直接复制整个函数,而是“用页面环境做计算,用外部脚本做调度”,后面我会讲到这一点。

3. 还原可复算的逻辑:用对照验证把黑盒变成白盒

3.1 从指纹特征判断算法家族

拿到若干组“参数→签名”样本后,就可以开始判断算法类型了。判断依据不复杂,主要看长度、字符集和变化规律。

常见的摘要算法里,MD5输出32位十六进制,SHA1输出40位,SHA256输出64位。如果wsgsig的固定长度是32位,优先怀疑MD5或其变体;如果是40位或64位,往SHA家族靠。如果长度不固定,那可能是Base64编码后的加密结果,或者带自定义截断。

我当时看到的wsgsig是32位,偶尔全小写,偶尔混合大小写。这种风格很像是先算MD5,再做了一些字符串变换。另外我注意到,即使两次请求的参数完全相同,只要时间戳变化,签名也会变。这说明生成逻辑里至少包含一个动态因子,最常见的就是时间戳。

到这里只能说“很像MD5系”,不能下结论。真正确定算法,还是通过复现验证来反向确认。

3.2 我的“复算验证三步法”

这一步是整个逆向过程里最有价值的环节。我把它总结成三步,每步都有明确的通过标准。

第一步是固定输入复算。取一次真实请求里参与计算的参数(字段名与字段值)和时间戳,用候选算法(比如MD5)计算出一个结果,和请求里实际的wsgsig比对。一致,则进入下一步;不一致,检查拼接顺序、大小写、是否包含额外盐值。

第二步是改动敏感性验证。把参与计算的某个参数值改一个字符,观察离线的签名计算结果是否完全变化。好的摘要算法应该对输入极为敏感,任意改动都会导致结果面目全非。如果改了明文字符后,签名只有局部变化,说明内部可能做了分段处理,或者根本不是普通摘要。

第三步是批量抽样比对。找10到20组真实请求,提取每组的时间戳、参数、最终签名,用离线的还原逻辑批量计算,比对一致率达到100%才算合格。如果只是样本里碰巧对上一两次,没有说服力。

这三步走完,基本上可以确认“黑盒”已经变成了“白盒”——至少在这一版前端代码里,你知道了签名是怎么来的。

3.3 撞上的几个“伪结论”

还原过程中很容易推断出错误结论。我踩过最典型的两个坑,一个是“误以为拼接顺序是字典序”,另一个是“误以为时间戳是秒级”。

网站上很多签名逻辑喜欢先把参数名按字典序排序再拼接,这是比较常见的习惯。但这个平台的生成逻辑里,有一组参数顺序是按请求体的原始顺序来的,并非字典序。如果我一开始就默认排序,后面所有的验证都对不上。排查方法很简单:手动构造两个只有顺序不同的参数组合,分别看结果是否一致,就能判断排序逻辑。

时间戳的坑则更隐蔽。请求里能看到类似timestamp=1678...这种秒级时间戳,但签名计算时用的可能是毫秒级时间戳。你拿秒级时间戳参与计算,网上怎么都对不上,后来把时间戳换成毫秒级,一次性成功。从那以后我养成了一个习惯:凡是签名对不上,先怀疑时间精度。

这类“伪结论”非常消耗时间,但也能帮你积累直觉。到后面你看到一个签名,基本能预判出它内部大概做了哪几步变换。

4. 把签名能力工程化:两条落地路线与关键取舍

4.1 受控浏览器方案:让站点代码自己算

算法还原成功之后,面临的问题变成“怎么稳定地拿到有效签名”。最稳妥的方案是直接在受控浏览器环境里跑页面自带的签名逻辑,完全不去碰内部实现。

我当时用的是Playwright做了一套受控浏览器脚本。核心逻辑很简单:打开目标页面,等待页面里加载完包含签名生成逻辑的脚本,然后在page.evaluate()里调用页面上已经存在的签名生成函数,把参数传进去,拿到返回值再拼到请求里。

这条路线的最大优点是“以不变应万变”。只要页面自己还能正常运行,你就不需要维护算法逻辑。哪怕平台调整了拼接顺序、换了盐值,只要前端代码同步更新了,你每次重新加载页面拿到的都是最新版本逻辑。

但缺点也明显:受控浏览器的资源开销大,单位时间里能发起的请求量有限;而且页面依赖的首次渲染和脚本加载都需要时间,不适合高并发场景。另外,有些页面会做WebDriver检测,受控浏览器容易被识别出来,这属于另一个层面的问题,会在后面提到。

4.2 脚本重写方案:去掉浏览器这个沉重包袱

如果目标平台页面体量很大,或者你需要在服务端批量跑任务,受控浏览器方案就不太够用了。这时候更适合把签名逻辑用脚本语言重写一遍,直接在服务端计算。

以Python重写为例,最终代码结构大概长这样:

# 示意代码,仅为说明逻辑结构,不代表任何目标站点的真实实现 import hashlib import time def calc_wsgsig(params, salt): # 1. 按约定顺序拼接待签名文本 raw_string = build_raw_string(params, salt) # 2. 取毫秒级时间戳参与计算 timestamp = int(time.time() * 1000) # 3. 做摘要并截取 md5_once = hashlib.md5((raw_string + str(timestamp)).encode("utf-8")).hexdigest() # 4. 二次摘要后再做字符串变换 final = hashlib.md5(md5_once.upper().encode("utf-8")).hexdigest() return final[8:24].upper()

上面这段代码只是示意。实际工程中,你需要把真实的拼接规则、盐值、变换步骤都维护到独立的配置模块里。脚本重写方案的优势是轻量、可控、速度快,一百个请求可以在很短时间内完成签名计算和发送。

但它的成本也不低:一旦平台端改了算法,你就得重新逆向、重新开发、重新测试。另外,平台可能同时存在多个前端版本或AB测试变体,不同版本签名逻辑可能不同,你需要设计一份“版本选择”的逻辑来兜底。

4.3 工程化里绕不开的四件事

无论选哪条路线,只要把签名能力做到工程级应用层,下面四件事一定会遇到。

第一是时效管理。签名通常带有隐含的有效期,有的短到一两分钟,有的长达半小时。你需要根据实际测试确定这个窗口。可以在本地记录签名生成时的时间戳,超过有效期就重新计算。不要把所有请求都等失效后再重试,那样浪费太多时间。

第二是缓存复用。当并发比较高时,不要每发一个请求就重新生成一次签名。同一个时间窗口内,相同参数集合的签名可以缓存复用。尤其当签名算法里有基于当前时间戳的动态因子时,缓存能大幅减少重复计算。但注意:既然时间戳参与了计算,缓存窗口就必须小于平台侧的有效期,否则你缓存的签名已经失效。

第三是并发控制。受控浏览器方案里尤其要注意,别让多个线程同时调用同一个页面实例去生成签名,这会导致上下文冲突。一般的做法是加一个互斥锁,或者维护一个签名生成的连接池,每个浏览器实例独立处理请求。

第四是失效兜底。网络环境里什么事情都可能发生。你的脚本必须能识别“签名失效”的返回特征。通常平台会返回特定的业务错误码,或者HTTP状态码加一段固定的错误信息。把这个识别逻辑做进去,遇到签名失效时自动刷新签名并重放一次请求,整体稳定性会上一个台阶。

这部分内容看似零碎,但恰恰是工具落地时最花时间的环节。算法本身也许一天就能还原出来,工程层面的稳定性却需要反复打磨。

5. 签名之外的隐形关卡:风控识别与合规边界

5.1 参数签名只是第一道门

当你把wsgsig处理明白之后,大概率会有一个错觉:只要签名正确,就能自由地向接口发起请求。但真的这么做,很快会撞上另一道墙——风控。

签名校验的本质是“参数完整性验证”,它告诉服务端“这批参数没有被篡改”。但服务端还会持续观察你这批请求是不是由真实用户发出的。判断依据包括单位时间内的请求频率、请求来源IP、TLS指纹、HTTP头的顺序、浏览器环境特征,甚至包括你的鼠标轨迹和滚动行为。

我实测下来,签名正确只能保证“请求本身能通过参数校验”,并不代表请求会被正常受理。一旦频率过高,平台不会直接拒绝请求,而是可能让你进入验证码流程,或者返回一些看似正常但内容已经过脱敏处理的数据。这种软性的指标比签名本身更难处理,而且持续对抗它既不体面也不稳定。

所以如果你做自动化不是为了学习,而是为了长期稳定地获取某类数据,更理智的做法是优先研究平台是否有官方的开放接口,或者通过正规渠道申请数据权限。签名逆向更适合用在验证一个思路、做短周期的技术调研,以及自己账号下的数据整理与备份。

5.2 合规边界和我自己的使用原则

写到这里,我想把话说得更直接一些。参数逆向本身是一种中性的技术能力,就像一把螺丝刀,可以用来拆电脑,也可以用来拆不该拆的东西。关键要看使用的场景和边界。

我个人的使用原则很简单:只在自己拥有合法权限的范围内做验证和测试,不批量抓取任何涉及他人隐私或商业敏感的数据,不做绕过付费、恶意压测、干扰平台正常运行的事情。如果某个需求超出了这条边界,我会停下来重新评估方案。

具体到操作上,我也做了一些限制。比如本地验证用的都是自己可控的测试账号;生成的请求量控制在一个极低的频率,不影响平台的正常服务;脚本也不对外开放、不做商用。技术研究是为了搞清楚原理,而不是为了无限度地获取数据。

从逆向wsgsig这个参数开始,到最后理解整套校验与风控的架构,这个过程的价值不在于你“拿下”了一个平台的签名,而在于你对网页端请求的安全设计有了整体的感知。以后再遇到其他平台的签名参数,你会知道该从哪里入手、如何验证、如何落地,也知道哪些线不能碰。这种系统性的认知,比某个具体参数的计算方法有用得多。

相关新闻

终端应用失控治理指南:从软件资产盘点、应用白名单到落地避坑

终端应用失控治理指南:从软件资产盘点、应用白名单到落地避坑

前段时间帮某公司做终端安全审计,翻完一沓报告我发现最扎眼的不是漏洞数量,而是一份软件清单:IT台账里登记的标准软件一共7款,可我从几十台抽查终端上识别出来的第三方应用,少则十五六个,多则二十几个。销售…

2026/10/20 2:48:43 阅读更多 →
Agent Skills实战:把提示词变成标准化技能包,让智能体行为稳定可控

Agent Skills实战:把提示词变成标准化技能包,让智能体行为稳定可控

最近不少人都在聊 Agent Skills,各种教程、案例分享铺天盖地。我自己的感受是,这玩意儿确实有点东西,但它不是什么玄乎的新技术,本质上是把原来散落在 Prompt 里的经验、流程、工具调用方式,打包成了一种更标准、更可复…

2026/10/20 2:48:43 阅读更多 →
AI专利申请被驳回?从审查员视角拆解高频理由与撰写优化

AI专利申请被驳回?从审查员视角拆解高频理由与撰写优化

做了几年AI相关的专利撰写和审查意见答复,我最大的感受是:技术团队做方案时很有激情,但到了专利申请阶段,往往低估了“审查视角”的存在感。很多项目明明技术含量不低,第一通审查意见里还是甩过来一连串驳回理由&#…

2026/10/20 2:48:43 阅读更多 →

最新新闻

电商客服与导购智能体合并设计:意图识别、工具调用与知识库实践

电商客服与导购智能体合并设计:意图识别、工具调用与知识库实践

做客服类智能体这几年,我的电脑里存过三版方案。第一版是规则加菜单按钮,用户点两下就烦了;第二版是纯大模型问答,什么都能聊,但一问到订单和售后就露馅;第三版才是真正跑通业务价值的——把电商客服导购智…

2026/10/20 3:48:13 阅读更多 →
极空间私有部署AI智能体:从聊天工具到自进化记忆伙伴

极空间私有部署AI智能体:从聊天工具到自进化记忆伙伴

“养完‘虾’,我又在极空间里养了‘马’!记忆更新,自带自进化”——这话你乍一看可能觉得我在搞什么新式养殖业,其实是我在自己搭的家庭服务器“极空间”里,先后部署了两代AI智能体。第一代号“虾”,是个只…

2026/10/20 3:48:13 阅读更多 →
Context-Mode详解:大模型中的上下文模式切换与管理

Context-Mode详解:大模型中的上下文模式切换与管理

1. 这个项目到底在解决什么问题先别急着看代码,我们把“context-mode”这五个字母拆开。它不是一个标准化的开源项目名,更像是一个约定俗成的代号——但凡做过多模态模型、RAG 检索增强、或者长上下文推理的朋友,看到这个词基本都会心一笑&am…

2026/10/20 3:48:13 阅读更多 →
用Flutter for OpenHarmony开发猫咪驱虫记录App:跨端实战与踩坑复盘

用Flutter for OpenHarmony开发猫咪驱虫记录App:跨端实战与踩坑复盘

家里有猫的朋友应该都经历过这个场景:三个月前信誓旦旦地给主子做完体内驱虫,三个月后死活想不起来上次到底是体内还是体外、用的哪款药,翻了十几分钟相册才勉强对上。宠物医生的建议是体外每月一次、体内每季度一次,这种频率全靠…

2026/10/20 3:48:13 阅读更多 →
Python实现冒泡排序:从基础逻辑到性能优化与实战避坑

Python实现冒泡排序:从基础逻辑到性能优化与实战避坑

1. 别急着写代码:冒泡排序到底在解决什么问题很多人第一次接触排序算法,都是从冒泡排序开始的。标题写的是“用Python实现冒泡排序”,看起来像是一个再基础不过的练习题,但我带过不少刚入行的朋友,发现一个很普遍的现象…

2026/10/20 3:48:13 阅读更多 →
病历数据挖掘实战:SPSS+Python混合建模,聚类关联与神经网络

病历数据挖掘实战:SPSS+Python混合建模,聚类关联与神经网络

数据挖掘这事儿,最怕的不是算法不会调,而是数据拿到手之后不知道怎么下手。尤其是病历数据这种典型的非结构化、多噪声、多缺失的字段集合,光清洗就得占掉大半时间。我最近把手头一批慢性胃炎门诊病历完整跑了一遍,从统计描述到聚…

2026/10/20 3:47:13 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →