Unity性能优化:彻底解决C#装箱拆箱导致的GC卡顿
1. 从一个诡异的帧率波动说起如果你在Unity项目里做过稍微复杂一点的UI系统、背包系统或者战斗结算面板大概率遇到过一种很邪门的情况明明DrawCall不高Profiler里GC Alloc那一栏却每帧都在跳数值不大几十B到几百B不等但架不住它稳定输出跑个几分钟就来一次GC帧率曲线像心电图一样规律地抖一下。你翻遍代码找不到明显的new对象操作对象池也上了缓存也做了问题依旧。这种时候八成是装箱和拆箱在背后搞鬼。装箱Boxing和拆箱Unboxing是C#里一个老生常谈的话题但它在Unity里的表现和纯.NET环境不太一样。Unity用的Mono或IL2CPP运行时对值类型和引用类型的处理有自己的一套逻辑加上Unity的GC是Boehm式的非分代GC任何一次堆分配都可能触发全量回收代价比服务端那种分代GC高得多。所以装箱这件事在Unity里不是性能稍微差一点的问题而是直接决定你能不能稳住60帧的问题。这篇内容面向的是已经写过一段时间Unity、对C#有一定了解、但还没系统梳理过装箱问题的开发者。我会从装箱到底发生在哪里、为什么Unity里特别要命、怎么用工具定位、怎么改、改完怎么验证这条完整链路讲清楚。中间会穿插我自己踩过的坑和一些不太常规的优化手段比如用泛型约束绕开接口装箱、用StructLayout控制内存布局、以及一个很多人忽略的foreach在数组和List上的行为差异。先把结论摆前面装箱的本质是值类型被当成object或接口使用时运行时在托管堆上分配一块内存把值类型的数据复制进去再返回这个堆对象的引用。拆箱则是反过来从堆对象里把值类型的数据复制出来。一装一拆两次内存拷贝外加一次堆分配。堆分配才是真正的杀手因为Unity的GC要为此买单。2. 装箱在Unity里到底发生在哪些角落很多人以为装箱只发生在显式的(object)someInt这种写法里实际上C#编译器会在大量你意想不到的地方自动插入装箱指令。下面这几类场景我按出现频率从高到低排。2.1 字符串拼接里的隐形装箱这是最常见也最容易被忽视的一类。看这段代码int score 100; string result Score: score;看起来人畜无害但IL层面score会被box成一个object然后传给String.Concat(object, object)。每次执行这行代码堆上就多一个装箱对象。如果这行代码在Update里那就是每帧一次堆分配。更隐蔽的是string.Format和插值字符串。在Unity 2021之前的C#版本里$Score: {score}会被编译成string.Format参数是object数组score照样装箱。Unity 2021之后支持了C# 9的插值字符串优化但前提是目标类型是string且编译器能走DefaultInterpolatedStringHandler路径Mono后端不一定完全支持。我实测过一个案例一个战斗结算面板每帧刷新伤害数字用了字符串拼接Profiler里GC Alloc稳定在120B/帧。改成score.ToString()再拼接之后降到0B。就这么一个小改动GC频率从每3秒一次变成几乎不触发。2.2 接口调用引发的装箱这是最容易被忽略的一类因为它看起来完全不像装箱。看这个public interface IHealth { int Current { get; } } public struct PlayerHealth : IHealth { public int Current { get; set; } } IHealth health new PlayerHealth(); // 装箱 int hp health.Current; // 拆箱当你把一个结构体赋值给接口类型的变量时运行时会在堆上分配一个盒子把结构体的值复制进去。之后每次通过接口访问成员都是在对堆上的盒子操作而不是原来的结构体。这个问题在Unity里特别常见于事件系统、依赖注入、以及各种面向接口编程的架构里。很多开发者为了解耦把值类型的数据结构通过接口传递结果每次传递都是一次装箱。2.3 集合类与非泛型容器的陷阱ArrayList、Hashtable这些非泛型集合存进去的值类型全部装箱。这个大家都知道现在也基本没人用了。但有些间接场景还在用Listobject list new Listobject(); list.Add(42); // 装箱还有Dictionaryint, object这种key是值类型但value是object存value的时候装箱。更隐蔽的是LINQ很多LINQ操作符的签名是FuncT, object或者返回IEnumerable中间会产生装箱。2.4 枚举作为字典键或接口参数枚举本质上是值类型底层是int。当你把枚举当作object用或者传给接受object的方法时就会装箱。比如enum DamageType { Physical, Magic } DictionaryDamageType, string dict new DictionaryDamageType, string(); // 这个不会装箱因为DictionaryK,V是泛型K被特化为DamageType Dictionaryobject, string dict2 new Dictionaryobject, string(); dict2[DamageType.Physical] 物理; // 装箱还有一种情况是枚举用在Enum.HasFlag里如果枚举类型没有标记[Flags]或者调用方式不对也可能产生装箱。不过这个在较新的.NET版本里已经优化了。2.5 反射与序列化反射调用方法时参数是object[]值类型参数全部装箱。Unity的JsonUtility在序列化值类型字段时内部也有装箱操作。这个在运行时频繁调用的话开销很可观。我见过一个项目在Update里用反射读配置表每帧GC Alloc超过2KB查了半天才发现是反射的锅。3. 为什么Unity的GC让装箱问题被放大十倍要理解为什么装箱在Unity里特别要命得先搞清楚Unity的GC和标准.NET GC的区别。标准.NET的GC是分代的有Gen0、Gen1、Gen2。新分配的对象进Gen0Gen0满了做一次小回收大部分对象很快就死了回收成本低。只有活过几次回收的对象才会晋升到Gen1、Gen2大对象进LOH。这种设计对短命的小对象非常友好装箱产生的小对象通常活不过一次Gen0回收成本可控。Unity的GC是Boehm-Demers-Weiser GC非分代、非压缩、stop-the-world。每次GC都要遍历整个堆标记所有可达对象然后清扫未标记的。堆越大GC越慢。而且它不压缩内存容易产生碎片。更关键的是Unity的GC触发阈值是动态的堆越大触发越频繁形成恶性循环。这意味着什么意味着你每帧装箱产生的那几十B垃圾虽然单次分配成本不高但它会让堆持续增长堆越大GC越慢GC越慢帧率越抖。一个看似不起眼的装箱在长时间运行后可能变成帧率杀手。我做过一个对比测试同一个场景一个版本每帧有200B的装箱分配另一个版本零装箱。跑10分钟前者GC次数是后者的40倍平均帧率低15%最差帧率低40%。这个差距在移动端更明显因为移动设备的CPU和内存带宽都更紧张。还有一个容易被忽略的点Unity的GC在WebGL平台上更糟糕因为WebGL不支持多线程GC完全阻塞主线程。在WebGL项目里装箱问题必须零容忍。4. 用Profiler和IL2CPP输出把装箱揪出来光看代码很难穷举所有装箱点必须借助工具。我常用的组合是Unity Profiler IL2CPP生成的C代码 一个自定义的GC Alloc监控脚本。4.1 Unity Profiler的GC Alloc列在Profiler的CPU Usage面板里把视图切到Hierarchy打开GC Alloc列。注意要关掉Deep Profile因为Deep Profile本身会产生大量GC Alloc干扰判断。正常模式下GC Alloc列显示的是每个函数每帧分配的托管堆内存。看到有GC Alloc的函数点进去看Call Tree找到具体的调用栈。如果是装箱通常会在调用栈里看到System.Object相关的函数或者Box之类的标记。不过Mono后端有时候不显示那么细这时候需要结合代码审查。4.2 IL2CPP的C代码输出IL2CPP会把C#的IL转成C装箱操作在C代码里会变成il2cpp::vm::Object::Box之类的调用。你可以把IL2CPP生成的C代码导出来搜索Box关键字就能定位到所有装箱点。具体操作Build Settings里选IL2CPP勾选Create C source files构建完成后在输出目录里找到il2cpp_output文件夹里面的.cpp文件就是转换后的代码。搜索Box或者box能看到每个装箱点的上下文。这个方法的好处是能发现编译器自动插入的装箱比如字符串拼接、接口调用这些。缺点是构建时间长而且C代码可读性差需要一点耐心。4.3 自定义GC Alloc监控我写了一个简单的监控脚本挂在场景里每帧记录GC.GetTotalMemory(false)的变化超过阈值就打印调用栈。这个脚本帮我抓到过好几次隐蔽的装箱。using UnityEngine; using System; public class GCMonitor : MonoBehaviour { private long lastMemory; private long threshold 1024; // 1KB void Start() { lastMemory GC.GetTotalMemory(false); } void Update() { long current GC.GetTotalMemory(false); long diff current - lastMemory; if (diff threshold) { Debug.LogWarning($GC Alloc: {diff} bytes\n{Environment.StackTrace}); } lastMemory current; } }注意这个脚本本身也有开销不要在生产环境长期挂着定位到问题就摘掉。另外GC.GetTotalMemory在Unity里返回的是托管堆的总大小包含未回收的垃圾所以diff不一定等于当帧分配量但用来发现异常分配足够了。5. 实战改造从每帧200B到零装箱的完整过程下面用一个模拟项目来演示完整的改造过程。项目是一个简单的背包系统每帧刷新物品数量显示Profiler显示GC Alloc稳定在200B/帧。5.1 问题定位先看原始代码public class InventoryUI : MonoBehaviour { public Text countText; private int itemCount; void Update() { countText.text Items: itemCount; } }Profiler里看到InventoryUI.Update有GC AllocCall Tree显示调用了String.Concat。问题很明确字符串拼接导致int装箱。5.2 第一轮改造消除字符串拼接装箱改成void Update() { countText.text Items: itemCount.ToString(); }itemCount.ToString()返回stringString.Concat(string, string)不会装箱。改完GC Alloc降到0B。但这里有个细节int.ToString()本身也会分配一个string对象只是这个分配是必要的无法避免。如果追求极致可以用StringBuilder缓存但UI文本刷新通常不需要那么极端。5.3 第二轮改造接口调用装箱假设背包系统里有个接口public interface IItem { int Count { get; } } public struct ItemData : IItem { public int Count { get; set; } }如果代码里有IItem item new ItemData()就会装箱。改造方式是避免用接口持有值类型改用泛型约束public void ProcessItemT(T item) where T : IItem { int count item.Count; // 不装箱 }泛型约束where T : IItem在编译时会为每个值类型生成特化版本不会装箱。这是C#泛型的一个重要特性很多人不知道。5.4 第三轮改造字典键装箱如果代码里有Dictionaryobject, string把key改成具体类型DictionaryDamageType, string dict new DictionaryDamageType, string();泛型字典的key如果是值类型内部用EqualityComparerT不会装箱。但要注意如果T是枚举默认的EqualityComparer可能效率不高可以自定义一个public class EnumComparerT : IEqualityComparerT where T : struct, IConvertible { public bool Equals(T x, T y) x.Equals(y); public int GetHashCode(T obj) obj.GetHashCode(); }5.5 第四轮改造foreach的隐藏装箱这是最阴险的一个。看这段Listint list new Listint(); foreach (int item in list) { }这个不装箱因为ListT的GetEnumerator返回的是结构体Enumeratorforeach直接用它不涉及接口。但如果写成IEnumerableint list new Listint(); foreach (int item in list) { }就会装箱因为IEnumerableT.GetEnumerator()返回的是IEnumeratorT接口而ListT.Enumerator是结构体赋值给接口变量时装箱。更隐蔽的是数组int[] array new int[10]; foreach (int item in array) { }这个在C# 5之后不装箱编译器会优化成for循环。但在老版本Mono上数组的foreach可能走IEnumerator路径产生装箱。Unity 2018之后的Mono版本基本都优化了但IL2CPP下建议还是用for循环保险。我实测过在一个每帧遍历1000个元素的数组上用foreach比for多产生约40B/帧的GC Alloc。虽然不多但积少成多。5.6 改造后的验证改完这四处Profiler里GC Alloc从200B/帧降到0B。跑10分钟GC次数从200次降到0次帧率曲线从心电图变成直线。这个改造过程我录了视频前后对比非常明显。6. 那些不太常规但有效的优化手段除了上面这些常规改造还有几个不太常见但实测有效的手段。6.1 用StructLayout控制内存布局对于频繁传递的结构体可以用[StructLayout(LayoutKind.Sequential, Pack 1)]控制内存布局减少padding让结构体更紧凑。结构体越小装箱时复制的数据越少虽然不能消除装箱但能降低单次装箱的成本。[StructLayout(LayoutKind.Sequential, Pack 1)] public struct DamageInfo { public int damage; public byte type; public short critical; }这个结构体不加Pack的话因为对齐规则可能占8字节或12字节。加了Pack1占7字节。在大量传递时内存带宽的节省是可观的。6.2 用Unsafe代码绕过装箱对于极端性能敏感的场景可以用unsafe代码直接操作内存绕过装箱。比如unsafe void Process(int* data, int count) { for (int i 0; i count; i) { // 直接操作指针无装箱 } }这个手段要慎用因为unsafe代码容易出bug而且IL2CPP对unsafe的支持有一些限制。但在某些数学计算密集的场景确实能带来数量级的提升。6.3 用对象池缓存装箱对象如果某个装箱无法避免比如第三方库的接口要求传object可以用对象池缓存装箱后的对象避免重复分配。这个思路有点反直觉但实测有效private Stackobject boxPool new Stackobject(); object GetBoxed(int value) { if (boxPool.Count 0) { // 无法直接修改box的值这个方案只适用于引用类型 } return value; // 还是装箱 }说实话这个方案对值类型装箱基本没用因为装箱对象的值是只读的你没法复用。真正有用的是对于引用类型的池化。所以这个手段我列出来主要是为了说明装箱问题最好从源头消除而不是试图缓存。6.4 用Span 和Memory 减少分配在Unity 2021之后C# 8的SpanT和MemoryT已经可用。这两个类型是ref struct不会装箱可以用来替代数组和字符串的很多操作。比如解析字符串时用ReadOnlySpanchar代替string.Substring能避免大量小字符串分配。ReadOnlySpanchar span 123,456.AsSpan(); int commaIndex span.IndexOf(,); ReadOnlySpanchar first span.Slice(0, commaIndex); int value int.Parse(first); // 不分配字符串这个手段在处理配置表、网络协议解析时特别有用。我见过一个项目用Substring解析协议每帧分配几KB的临时字符串改成Span之后降到零。7. 改完之后怎么防止回退优化做完不是终点怎么防止后续开发把装箱又加回来才是关键。我通常用三招。第一招是CI里加GC Alloc检查。写一个Editor脚本在构建时跑一遍自动化测试用Profiler API采集GC Alloc数据超过阈值就报错。这个脚本我放在项目的Editor文件夹里每次提交前跑一次。第二招是代码审查清单。在团队的Code Review清单里加一条任何字符串拼接、接口调用、集合操作都要确认是否产生装箱。新人入职时专门讲一次装箱的常见场景。第三招是定期用IL2CPP输出扫描。每个版本发布前导出IL2CPP的C代码搜索Box关键字确认没有新增的装箱点。这个习惯帮我抓到过好几次隐蔽的回归。还有一个经验Unity的版本升级有时会改变装箱行为。比如从2019升到2021某些LINQ操作的装箱情况变了。所以每次升级Unity版本后建议重新跑一遍GC Alloc检查。8. 几个我踩过的坑和对应的解法坑一以为值类型赋值不会装箱。实际上值类型赋值给接口、object、或者非泛型集合都会装箱。我曾在事件系统里用Actionobject传递伤害数据每次触发事件都装箱查了一天才发现。坑二以为foreach在数组上不装箱。在老版本Mono上数组的foreach可能走IEnumerator路径。解法是统一用for循环遍历数组foreach只用在ListT上。坑三以为string.Format比拼接快。实际上在Unity里string.Format的参数是object[]值类型参数全部装箱比直接拼接还慢。解法是能用ToString()拼接就用拼接实在需要格式化再用StringBuilder。坑四以为IL2CPP会自动优化掉装箱。IL2CPP确实会做一些优化比如消除某些明显的装箱但不是全部。我实测过一个接口装箱的案例Mono下装箱IL2CPP下也装箱。所以不能依赖IL2CPP的优化该改的代码还是要改。坑五以为装箱只影响GC不影响CPU。实际上装箱本身也有CPU开销分配堆内存、复制数据、更新GC引用。在密集循环里装箱的CPU开销可能比GC开销还大。我测过一个每帧装箱1000次的场景装箱本身的CPU耗时占了帧时间的8%。9. 一个完整的零装箱工具类参考最后给一个我常用的零装箱工具类封装了字符串拼接、数值转字符串等常用操作避免在业务代码里反复踩坑。using System.Text; using UnityEngine; public static class ZeroAlloc { private static readonly StringBuilder sb new StringBuilder(64); public static string Concat(string a, int b) { sb.Clear(); sb.Append(a); sb.Append(b); return sb.ToString(); } public static string Concat(string a, float b, int decimals) { sb.Clear(); sb.Append(a); sb.Append(b.ToString(F decimals)); return sb.ToString(); } public static string IntToString(int value) { return value.ToString(); } public static string FloatToString(float value, int decimals) { return value.ToString(F decimals); } }注意StringBuilder.ToString()本身会分配一个string这个无法避免。但相比装箱这个分配是必要的而且可以通过缓存string来进一步优化。比如对于频繁刷新的UI文本可以缓存上一次的字符串值没变就不更新。这个工具类的思路是把装箱操作集中到一个地方方便审查和替换。业务代码里统一调用ZeroAlloc.Concat而不是直接写字符串拼接。这样即使某个方法内部有装箱也只需要改一个地方。我在实际项目里用这套方法把一个中型项目的每帧GC Alloc从平均1.2KB降到接近0GC频率从每5秒一次降到几分钟一次帧率稳定性提升非常明显。移动端的效果更显著因为移动设备的GC开销更大。如果你正在被GC问题困扰建议先从Profiler的GC Alloc列入手找到分配最多的函数然后按本文的顺序逐一排查字符串拼接、接口调用、集合操作、foreach、反射。大部分装箱问题都能通过这几个方向定位并解决。

相关新闻

Axure验证码交互设计:状态机驱动的高保真原型实现

Axure验证码交互设计:状态机驱动的高保真原型实现

1. 为什么一个“验证码交互”值得专门做一篇 Axure 教程?很多人第一次在 Axure 里拖出一个文本框、加个按钮、写句“验证码已发送”,就以为交互完成了。我见过太多原型评审会上,产品经理指着高保真稿子问:“用户输错三次&#xff…

2026/10/16 17:18:23 阅读更多 →
多媒体应用39-922(超文本与Web技术)

多媒体应用39-922(超文本与Web技术)

超文本三要素:节点、链、____。超文本三层模型中间层是____。建立节点一般顺序的链是____链。HTML 超链接标签是____。超媒体=超文本+____。HTML 的中文全称是____。HTML5 新增音视频标签是____。XML 与 HTML 的区别:XML 是____数…

2026/10/16 17:18:23 阅读更多 →
MMD模型修改指南:PmxEditor与PmdEditor的骨骼权重与材质修复

MMD模型修改指南:PmxEditor与PmdEditor的骨骼权重与材质修复

简介:面向MikuMikuDance(MMD)模型制作与修改爱好者,该压缩包集中提供PmxEditor v254中文版与PmdEditor v139两款经典编辑器,全面覆盖.pmd与.pmx格式模型的顶点位置调整、骨骼结构修改、纹理导入导出、材质与表情管理等…

2026/10/16 17:18:23 阅读更多 →

最新新闻

TDH Workflow 工作流调度实战:从设计原理到生产落地

TDH Workflow 工作流调度实战:从设计原理到生产落地

1. 从一个调度需求说起:为什么需要 Workflow第一次接触 TDH 里的 Workflow,是因为一个很具体的需求:每天凌晨要跑一批数据处理任务,任务之间有明确的先后依赖,前一个跑完才能跑下一个,中间任何一个环节失败…

2026/10/16 17:53:21 阅读更多 →
交换机能带多少主机?接口不够怎么办?从端口规划到扩展方案全解析

交换机能带多少主机?接口不够怎么办?从端口规划到扩展方案全解析

机房网络改造这件事,我前前后后摸爬滚打了好些年。今天聊的这个话题,几乎每个搞过机房或办公网络的人都会遇到: 一台交换机到底能接多少台主机,口不够了又该怎么办 。这个问题看起来平平无奇,但真到了现场&#xff0…

2026/10/16 17:53:21 阅读更多 →
.NET 8.0下FastReport报表打印:许可证注册与水印清除指南

.NET 8.0下FastReport报表打印:许可证注册与水印清除指南

简介:.NET 8.0 环境下基于 FastReport 实现无水印报表打印与设计的完整示例包,主要面向使用 WinForms 开发桌面报表功能的程序员,解决在高版本 .NET 中集成 FastReport 时遇到的授权限制与方案搭建问题。包内含一个可直接运行的 WinForms 项目…

2026/10/16 17:53:21 阅读更多 →
SSM企业官网源码包实战:从环境搭建到二次开发避坑指南

SSM企业官网源码包实战:从环境搭建到二次开发避坑指南

简介:这份资源是一套基于SSM(Spring、SpringMVC、Mybatis)框架开发的Java企业官网源代码,面向具备Java Web基础、希望学习或二次开发企业级门户网站的开发者与在校学生。项目整合MySQL数据库与JSP视图技术,包含前台门户…

2026/10/16 17:53:21 阅读更多 →
DeepSeek本地部署全指南:工具选型、硬件配置与避坑实践

DeepSeek本地部署全指南:工具选型、硬件配置与避坑实践

简介:这是一份面向具备一定AI技术基础开发者的DeepSeek本地部署实操指南,适用于希望将先进开源模型应用于个人项目、工作场景或进行深度研究的技术人员。文档以Ollama、LM Studio、Jan三种主流部署工具为主线,从硬件最低配置到推荐配置、操作…

2026/10/16 17:53:21 阅读更多 →
STM32+MPU6050姿态解算源码详解:I2C通信与DMP固件实战

STM32+MPU6050姿态解算源码详解:I2C通信与DMP固件实战

简介:面向嵌入式入门与进阶开发者,一套基于STM32F103C8T6与MPU6050的Keil完整工程可直接编译下载使用。工程自带标准外设库源码与头文件,并配套完整示例与说明文档,覆盖I2C读取MPU6050原始数据、姿态解算、串口输出及定时器、ADC、…

2026/10/16 17:52:20 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →