AutoJs自动化脚本实战:封装控件查找、点击与等待工具方法
写自动化脚本这件事最磨人的往往不是流程有多复杂而是同一批基础方法在每次新脚本里都要重新写一遍。AutoJs里click、text、findOne这些API虽然开箱即用可真跑起来你就会发现直接嗑原生API的脚本大多活不过三天控件还没加载完就去点击了、屏幕黑着就执行了、日志刷了几百行也看不出来卡在哪一步。我整理了几个自己从实际脚本里拆出来的AutoJs工具方法都是从坑里爬出来之后留下的版本拿过去改改参数就能用。适合正在写AutoJs脚本、想少走弯路的朋友也适合那些刚接触自动化、还没建立起自己工具库的新手。1. 这些工具方法解决的是哪几类老问题1.1 时序与控件结构问题AutoJs脚本跑崩排在第一位的原因永远是时序。手机App的页面渲染不像PC端那么可控同一个页面网络好与不好时控件的出现时间能差出一两秒。直接text(确认).findOne()只能等一次控件没渲染出来就返回空后续代码再对这个空对象调click()当场抛异常。更隐蔽的是控件层级问题。很多App会把一段文案放在text属性里把另一段描述放在content-desc里同一个按钮在不同页面状态下的属性还不一样。如果你只认一种属性换一个页面就全军覆没。这种问题不把查找逻辑封装成带兜底的策略根本没法优雅处理。1.2 机型差异与可维护性问题坐标写死的脚本换一台分辨率不同的手机就是废的。就算在同一台手机上App更新一次UI坐标全部偏移也是常有的事。所以只要涉及点击和滑动就必须基于控件位置去计算而不是取固定坐标。还有一个经常被忽略的问题脚本维护。今天写一个签到脚本明天写一个巡检脚本每个人开头都放一遍sleep(1000)、click(x, y)、console.log最后改起来到处是雷。工具方法本身就是一个沉淀的过程把共性的东西抽出来脚本主逻辑才能保持干净。1.3 使用前的基本环境要求下面这些方法基于AutoJs常见发行版的API编写4.x及以上基本通用。使用前需要确保三件事无障碍服务已开启这是AutoJs执行控件操作的前提悬浮窗权限已授予方便在跑批时随时看日志存储权限和截图权限已开放涉及截图存档时要用。环境不对工具方法写得再漂亮也跑不起来所以这个检查放最前面说。2. 控件查找与等待让脚本不再“踩空”2.1 为什么不用现成的findOne超时机制AutoJs的findOne(timeout)确实能设置超时但超时后的行为是直接抛异常中断整个脚本。你只能在调用处包一层try/catch一个两个还好脚本里几十处查找都这么包代码全是噪音。我更习惯用findOnce(0)配合轮询自己实现等待逻辑。findOnce(0)不抛异常找不到就返回null把控制权完全留给自己找不到就继续等、等够了就返回空对象让上层决定怎么处理而不是让脚本直接暴毙。2.2 waitForNode与多属性兜底查找核心就是这个waitForNode方法。传入一个选择器和一个超时时间内部每200毫秒查一次直到查到节点或超时返回null。200毫秒这个间隔是实测出来的太短会频繁占用主线程太长又会让脚本反应迟钝。function waitForNode(selector, timeout) { const start Date.now(); while (Date.now() - start timeout) { const node selector.findOnce(0); if (node) { return node; } sleep(200); } return null; }在此基础上我做了一层属性兜底。很多App的按钮文案不是放在text里而是放在content-desc里单查一种很容易漏。所以就有了一组更实用的方法function findByText(textStr, timeout) { return waitForNode(text(textStr), timeout || 3000); } function findByDesc(descStr, timeout) { return waitForNode(desc(descStr), timeout || 3000); } function findByTextOrDesc(textStr, timeout) { const node findByText(textStr, timeout); if (node) { return node; } return findByDesc(textStr, timeout); }这样做的代价是多等一个超时周期但换来的是更高的命中率。实际使用中我的建议是优先用findByText失败了再走findByTextOrDesc。因为同一个页面里text属性的匹配通常比desc更精确。2.3 寻找可点击父节点的方法还有一个高频问题你要找的控件确实存在但它本身不可点击。比如一个TextView外面包了一层LinearLayout点击事件在父容器上。直接对这个子节点调用click()点下去没有任何反应。解决思路是向上逐层寻找最近的“可点击”祖先节点。用parent()一层层往上走直到某个节点的clickable()返回true。function clickableParent(node) { let current node; while (current current.parent()) { if (current.clickable()) { return current; } current current.parent(); } return null; } function tapByText(textStr) { const node findByText(textStr, 3000); if (!node) { return false; } const target node.clickable() ? node : clickableParent(node); if (!target) { return false; } const b target.bounds(); return click((b.left b.right) / 2, (b.top b.bottom) / 2); }这套逻辑是我遇到过最多坑的地方尤其那种“明明找到了节点点了却没反应”的诡异问题十有八九就是点击事件挂在父级身上。3. 像真人一样的点击与滑动封装3.1 点击太规律有什么问题直接click(500, 1000)然后sleep(1000)再click(500, 1000)再sleep(1000)这种脚本跑起来就算不被平台识别成异常行为至少也容易因为点击位置落在控件边缘而误触。等间距的延时对系统来说太规律了对用户来说也太假了。我并不是要教大家怎么规避监管而是从脚本稳定性角度出发操作节奏带一点随机性能让脚本更接近真实操作习惯也减少因为每次都点同一个坐标导致的可预测性故障。3.2 随机延时与随机点工具随机延时的实现很简单但建议封装成一个方法统一管理区间。我自己习惯设定一个基准区间比如300到800毫秒这个区间用下来点击最稳。function randomDelay(min, max) { const ms Math.floor(Math.random() * (max - min 1)) min; sleep(ms); return ms; } function centerOf(node) { const b node.bounds(); return { x: Math.round((b.left b.right) / 2), y: Math.round((b.top b.bottom) / 2) }; } function randomPointIn(node, ratio) { const b node.bounds(); const offsetX Math.round((b.right - b.left) * (ratio || 0.1)); const offsetY Math.round((b.bottom - b.top) * (ratio || 0.1)); return { x: b.left offsetX Math.round(Math.random() * ((b.right - b.left) - 2 * offsetX)), y: b.top offsetY Math.round(Math.random() * ((b.bottom - b.top) - 2 * offsetY)) }; }randomPointIn里有个细节偏移量是按控件宽高比例算的不是固定像素。大按钮可以偏移多点小按钮必须偏移少点。如果固定偏移12像素碰到一个只有20像素高的按钮很容易点到按钮外面去。这个比例我一般取0.1到0.15既能制造随机感又保证落点仍在控件内部。把随机延时和随机落点组合起来就是一套完整的点击工具function tapNode(node, options) { const minDelay (options options.minDelay) || 300; const maxDelay (options options.maxDelay) || 800; const delayMs randomDelay(minDelay, maxDelay); const pt randomPointIn(node, 0.15); const result click(pt.x, pt.y); if (options options.afterDelay) { sleep(options.afterDelay); } return { success: result, delayMs: delayMs, x: pt.x, y: pt.y }; }返回success和实际点击坐标很重要。脚本跑完后回看日志你能知道刚才到底点没点上以及点在哪个位置排查问题快得多。3.3 基于控件的滑动辅助滑动也是同样的道理。列表页的下拉刷新、选项卡的左右切换如果直接写死swipe(500, 1200, 500, 400, 300)换一台屏幕比例不同的手机滑动轨迹就完全不对了。我的做法是先拿到目标控件的bounds然后按控件区域计算起止点。比如在一个列表区域下拉刷新起点取列表中上方终点取列表下方滑动距离和控件实际高度保持匹配。function swipeFromNode(node, direction, distanceRatio) { const b node.bounds(); const cx (b.left b.right) / 2; const startY b.top (b.bottom - b.top) * 0.3; const endY b.top (b.bottom - b.top) * (0.3 (distanceRatio || 0.5)); const duration 300 Math.floor(Math.random() * 300); if (direction down) { return swipe(cx, startY, cx, endY, duration); } if (direction up) { return swipe(cx, endY, cx, startY, duration); } return false; }duration我同样做了随机化但幅度比点击小300到600毫秒之间。因为滑动太快容易触发App的惯性滚动太慢又会显得很刻意这个区间是观感最自然的。4. 屏幕、锁屏与前台应用的兜底处理4.1 黑屏和锁屏是所有自动化的头号敌人我见过太多人写好的脚本白天跑得好好的一到晚上或者手机放口袋里超过十分钟再执行就全程瘫痪。原因很简单屏幕关了甚至锁屏了控件树还在但所有操作都会被系统拦下来。所以每次跑任务前先保证屏幕是亮的。device模块提供了屏幕状态检测和唤醒接口但不同发行版API有差异所以我在外层做了一层兼容。部分版本用device.wakeUp()能直接点亮有些则必须发送电源键。function ensureScreenOn() { if (device.isScreenOn()) { return true; } if (typeof device.wakeUpIfNeeded function) { device.wakeUpIfNeeded(); sleep(800); } else { press(power); sleep(800); } if (!device.isScreenOn()) { press(power); sleep(1200); } return device.isScreenOn(); }关于解锁我的经验是如果跑的是无人值守的定时脚本强烈建议关闭锁屏密码改成滑动解锁或者不锁屏。密码解锁的方法不是不能写但每个ROM对锁屏页的控件结构都不一样为了这个做大量适配纯属投入产出不成比例。真遇到锁屏更稳妥的办法是让设备本身保持不锁屏状态或者把脚本的执行时段放在不锁屏的场景里。4.2 确保目标应用处于前台的通用做法另一个常见问题是执行任务时前台不是目标App。可能用户中途切出去聊天了也可能上一次任务结束的时候把App退到了后台。这时候直接按控件操作操作的是错误的界面轻则任务失败重则误触其他应用。我写了一个组合方法先检测当前前台应用如果不是目标包名就拉起它再等界面稳定。function ensureFrontApp(packageName, waitText) { if (!currentPackage()) { return false; } if (currentPackage() ! packageName) { launch(packageName); sleep(1500); } if (waitText) { return findByText(waitText, 5000) ! null; } return true; }waitText这个参数很关键有时候launch()成功了但页面还在广告页或者加载页直接往下走还是会翻车。传一个目标页面独有的文案进去就相当于给应用启动加了一层“页面级等待”。4.3 拦截弹窗和权限提示App启动后最常见的意外是各种弹窗更新提示、活动弹窗、权限申请、青少年模式。这些弹窗的关闭按钮文案五花八门但大多是“取消”“以后再说”“我知道了”“暂不”这几种。我把关闭弹窗做成了一个批量尝试方法function closePopups(keywords) { const list keywords || [取消, 以后再说, 我知道了, 暂不, 确认]; for (let i 0; i list.length; i) { const node findByText(list[i], 800); if (node) { const target node.clickable() ? node : clickableParent(node); if (target) { const pt randomPointIn(target, 0.2); click(pt.x, pt.y); sleep(600); } } } }注意这里的超时时间我只给了800毫秒比正常查找短很多。因为弹窗检测只是一个“扫一遍”的动作如果每个关键词都等3秒脚本每次启动都要白等十几秒浪费严重。5. 截屏、区域裁剪与文本识别的封装5.1 什么时候必须走图片识别路线控件查找不是万能的。有些App的界面是用自绘组件或者WebView渲染的控件树里只能看到一个大容器看不到具体文案。这个时候就必须走“截图 图片处理 文本识别”这条路。但OCR是个开销不小的操作全屏识别一次时间可能超过一秒频繁调用会拖垮整个脚本。所以我的原则是能通过控件属性解决的绝不OCR必须OCR的也要先裁剪区域只识别关键区域。5.2 截图存档与区域裁剪截图存档本身就能派上用场。跑批任务结束后留档、任务出问题后回看现场都离不开截图。命名上我会带时间戳方便排序查找。function getTimeStr() { const d new Date(); return d.getFullYear() - (d.getMonth() 1) - d.getDate() _ d.getHours() - d.getMinutes() - d.getSeconds(); } function captureAndSave(fileName) { const img captureScreen(); if (!img) { return null; } const dir /sdcard/脚本截图/; files.createWithDirs(dir); const path files.join(dir, fileName _ getTimeStr() .png); images.save(img, path); img.recycle(); return path; }区域裁剪的核心价值在于减少OCR的识别范围。比如一张页面上只有一个通知栏区域需要识别那就先把这部分裁出来识别速度能快好几倍。function clipRegion(img, region) { // region 格式: [x, y, width, height] const clip images.clip(img, region[0], region[1], region[2], region[3]); return clip; }5.3 文本识别的耐心型轮询封装对于纯图片界面判断“某段文字是否出现”比判断控件更麻烦。我封装了一个轮询方法每秒钟截一次图对指定区域做文本识别直到识别出目标关键词或超时。function waitForTextByOcr(keyword, timeout, region) { const start Date.now(); while (Date.now() - start timeout) { const img captureScreen(); if (img) { let clip img; if (region) { clip clipRegion(img, region); } const result ocr.recognize(clip); const content result result.text ? result.text : ; clip.recycle(); img.recycle(); if (content.indexOf(keyword) 0) { return true; } } sleep(1000); } return false; }这里要特别提醒OCR接口在不同AutoJs发行版上的调用方式差异很大上面代码里的ocr.recognize需要替换成你所使用版本的实际API。另外OCR必然存在误识别率尤其是中文。关键词尽量选字形独特、不会轻易被识别错的短词别拿一句长文案当匹配条件。6. 日志、重试与失败隔离让脚本自己会说话6.1 带时间戳的日志才叫日志没有时间戳的日志等于没有日志。脚本跑一个小时中间某个步骤失败了光看到几十行消息根本定位不到是哪个时间点、哪个环节出的问题。带时间戳的输出配合重试和完成标记就能把整个执行过程串成一条清晰的时间线。function logWithTime(msg) { const now new Date(); const h now.getHours(); const min now.getMinutes(); const s now.getSeconds(); const ts (h 10 ? 0 h : h) : (min 10 ? 0 min : min) : (s 10 ? 0 s : s); console.log([ ts ] msg); return ts msg; }6.2 有限重试与失败隔离无人值守自动化脚本最怕无限循环。一个操作失败后重试是必要的但绝对不能无限重试否则一旦遇到顽固问题脚本会卡在同一个步骤几个小时整个任务时间线全部拖垮。我封装了一个带重试次数的工具失败后等待时间递增给系统更多恢复时间。这比固定间隔重试效果好得多。function retry(fn, options) { const maxRetries (options options.retries) || 3; const gap (options options.gap) || 1000; let lastErr null; for (let i 0; i maxRetries; i) { try { return fn(); } catch (e) { lastErr e; logWithTime(第 (i 1) 次尝试失败: e.message); sleep(gap * (i 1)); } } logWithTime(重试 maxRetries 次仍失败放弃该步骤); throw lastErr; }重试间隔的递增逻辑是第一次失败等1秒第二次等2秒第三次等3秒。这是一个简单的线性退避不搞指数退避是因为在手机自动化场景里每次失败往往只是临时性的界面抖动线性递增足够了。失败隔离的意思更进一步单个步骤失败不能拖垮整个任务。我把每个任务包在一个守卫方法里任务内异常不会中断脚本主体。function runWithGuard(taskName, fn) { try { logWithTime(开始执行: taskName); fn(); logWithTime(完成: taskName); } catch (e) { logWithTime(任务异常[ taskName ]: (e e.message)); } }跑批脚本里有多个步骤时这个守卫方法特别有用。步骤A失败了步骤B照样能继续跑最后日志汇总时一目了然。6.3 任务编排的小框架最后把这些组合成一个极简的任务编排结构。我习惯在脚本里维护一个任务列表按顺序执行每项任务之间有随机间隔避免整个流程像发条一样零误差地推进。function runBatch(taskList) { for (let i 0; i taskList.length; i) { const task taskList[i]; runWithGuard(task.name, task.fn); if (i taskList.length - 1) { randomDelay(800, 1500); } } }7. 装进一个盒子仓库文件与完整示例7.1 tools.js的文件骨架上面所有工具方法我在实际项目中都是集中放在一个tools.js文件里脚本主逻辑只负责业务编排不掺杂物操作细节。文件骨架大概是这个结构// 配置区 const CONFIG { clickMinDelay: 300, clickMaxDelay: 800, nodeTimeout: 3000, screenShotDir: /sdcard/脚本截图/ }; // 节点相关 function waitForNode(selector, timeout) { /* ... */ } function findByText(textStr, timeout) { /* ... */ } function findByDesc(descStr, timeout) { /* ... */ } function findByTextOrDesc(textStr, timeout) { /* ... */ } function clickableParent(node) { /* ... */ } // 点击滑动相关 function randomDelay(min, max) { /* ... */ } function centerOf(node) { /* ... */ } function randomPointIn(node, ratio) { /* ... */ } function tapNode(node, options) { /* ... */ } function swipeFromNode(node, direction, distanceRatio) { /* ... */ } // 设备状态相关 function ensureScreenOn() { /* ... */ } function ensureFrontApp(packageName, waitText) { /* ... */ } function closePopups(keywords) { /* ... */ } // 截图与识别相关 function getTimeStr() { /* ... */ } function captureAndSave(fileName) { /* ... */ } function clipRegion(img, region) { /* ... */ } function waitForTextByOcr(keyword, timeout, region) { /* ... */ } // 日志与重试相关 function logWithTime(msg) { /* ... */ } function retry(fn, options) { /* ... */ } function runWithGuard(taskName, fn) { /* ... */ } function runBatch(taskList) { /* ... */ } module.exports { waitForNode, findByText, findByDesc, findByTextOrDesc, clickableParent, randomDelay, randomPointIn, tapNode, swipeFromNode, ensureScreenOn, ensureFrontApp, closePopups, captureAndSave, waitForTextByOcr, logWithTime, retry, runWithGuard, runBatch };使用的时候在脚本开头require(./tools.js)然后就可以把注意力全部放在业务逻辑上。7.2 一个具体的自动巡检示例举个例子。假设每天早上要打开一个内部信息平台App进入“今日公告”页面确认“无新增内容”或者把最新公告文案记录下来。这个场景很适合用来展示整套工具方法的组合使用。const tools require(./tools.js); function morningInspection() { tools.ensureScreenOn(); const appOk tools.ensureFrontApp(com.example.internal, 今日公告); if (!appOk) { tools.logWithTime(未能进入今日公告页); return; } tools.closePopups([我知道了, 取消, 以后再说]); const titleNode tools.findByText(最新公告, 3000); if (!titleNode) { tools.logWithTime(页面中未找到公告标题区域); tools.captureAndSave(inspect_fail); return; } const contentNode tools.findByTextOrDesc(今日无新增, 2000); if (contentNode) { tools.logWithTime(今日无新增内容); } else { tools.logWithTime(检测到新增内容已截图留档); tools.captureAndSave(inspect_new); } } tools.runWithGuard(早间巡检, function () { tools.retry(morningInspection, { retries: 3, gap: 2000 }); });这段代码里retry包在最外层意味着如果第一次巡检因为网络波动失败最多会自动补跑两次每次间隔递增。单次巡检的异常被外层守卫接住不会导致整个脚本直接退出。截图留档的命名带时间戳事后排查也能对得上。7.3 拿走后必改的几个地方直接从本文复制代码回去用之前有四处一定要改否则换一台手机就跑不通目标App的包名。currentPackage()和launch()依赖完整包名不同应用包名不同需要自己确认。等待文案。示例里的“今日公告”“最新公告”是占位文案要替换成目标App里真实存在且唯一的界面文字。截图目录和文件名。/sdcard/脚本截图/只是我个人的目录习惯可以改成任何有读写权限的路径。随机延时的区间。如果你的目标场景网络较差控件加载慢建议把clickMaxDelay从800放大到1200给界面渲染留更多余量。拿回去之后别直接扔到生产脚本里跑先在目标设备上单独跑一遍findByText、tapNode这几个方法确认节点能找得到、点击有反应再把整套方法接入正式流程。这样即使有问题排查范围也能缩到最小。我在实际使用中最大的体会是工具方法从来不是一次性写完就完事的它们会跟着脚本一起慢慢长。每遇到一个新的坑就往tools.js里补一个新方法补多了之后你会发现新脚本的开发速度越来越快因为大部分通用能力早就沉淀在里面了。最后再分享一个小技巧随机延时的基准区间不是固定的它应该和你的网络环境挂钩。家里WiFi稳定的时候可以把区间压缩到200到500毫秒出门用流量时放大到500到1200毫秒这样脚本的节奏永远跟得上真实环境的响应速度。

相关新闻

棋盘题训练全解析:从二维数组建模到回溯与AI博弈剪枝

棋盘题训练全解析:从二维数组建模到回溯与AI博弈剪枝

如果你的程序设计训练清单里有“棋盘”这两个字,我先给你交个底:这题目可以做得无比简单,也可以做到让你怀疑人生。简单版是打印一个二维数组,复杂版是带 AI 对弈、悔棋、复盘、甚至棋谱导出的完整小游戏。多数人做的是中间那条路…

2026/10/15 0:35:33 阅读更多 →
论文 AI 降重改写避坑指南,几款常用降AI率工具怎么选才靠谱

论文 AI 降重改写避坑指南,几款常用降AI率工具怎么选才靠谱

摘要:本文围绕论文写作中的改写与降重需求,对比了几款常见的AI辅助工具,从改写能力、语言润色、引用规范等维度做了横向梳理,并给出按写作阶段和语种匹配的选型思路。结论是先看清自己卡在改写还是润色,再决定用哪一类…

2026/10/15 0:34:33 阅读更多 →
2026 重复率和 AI 率同时超标?一站式AI智能降重工具实测指南

2026 重复率和 AI 率同时超标?一站式AI智能降重工具实测指南

一、前言:2026 高校论文审核新难题 随着高校学术审核体系不断升级,知网、维普等主流检测平台全面上线AIGC 智能检测功能,当代毕业生的论文写作与修改迎来双重考验。以往论文仅需攻克重复率超标问题,如今还要规避AI写作痕迹检测风险…

2026/10/15 0:34:33 阅读更多 →

最新新闻

基于MP2642GR-Z与PIC18F87K22的2S电池主动均衡模块设计

基于MP2642GR-Z与PIC18F87K22的2S电池主动均衡模块设计

/* 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 1:14:56 阅读更多 →
SPFA负环检测实战:POJ 2240套利问题与对数转化技巧

SPFA负环检测实战:POJ 2240套利问题与对数转化技巧

/* 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 1:14:56 阅读更多 →
Android本地MP3播放器实战:MediaPlayer状态机与前台服务设计

Android本地MP3播放器实战:MediaPlayer状态机与前台服务设计

/* 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 1:14:56 阅读更多 →
51单片机汽车自动照明系统:光敏+超声波协同远近光智能控制

51单片机汽车自动照明系统:光敏+超声波协同远近光智能控制

/* 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 1:14:56 阅读更多 →
SLS只读助手:会话权与日志读权限拆开

SLS只读助手:会话权与日志读权限拆开

人只能给指定数字员工开会话,员工再用单独角色读日志。两层写进同一条策略,就会出现打得开却查不到,或者名单可见的库被整库读走。 目录 前言 一、会话权和读日志权拆开 二、开工前只读盘点 三、角色只读指定 Logstore 四、人的策略只绑一组、只对一名员工

2026/10/15 1:14:56 阅读更多 →
Apollo-11 自动化任务调度框架:状态机与插件化设计实践

Apollo-11 自动化任务调度框架:状态机与插件化设计实践

/* 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 1:13:56 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →