基于Django Channels与Paramiko的Web SSH终端实战:从架构到审计
简介这是一份面向Python Web开发者与运维人员的实战项目源码借助Django框架在浏览器中复刻Xshell的远程终端能力让用户无需安装桌面客户端即可通过Web界面与远程服务器进行SSH交互。项目围绕Django的模型、视图、模板与URL配置四大核心组件展开并借助paramiko库实现SSHv2协议的加密连接、命令执行与结果回传同时涉及用户认证、权限控制与操作日志等安全设计适合希望深入理解Web终端与网络安全的进阶学习者。资源包共29个文件以20个py源码为主辅以js、html、css等前端资源及md说明文档压缩后约85KB目录结构清晰涵盖项目配置、应用模块与静态资源。目前已有1279人学习下载读者可从中获得完整的Django项目骨架、SSH连接实现思路与终端交互界面参考便于二次开发与技能提升。1. 浏览器里跑一个“xshell”这套 Django 方案到底能解决什么很多做运维平台或内部工具的同学都遇到过同一个尴尬手里一堆 Linux 机器日常巡检、查日志、重启服务每次都要开本地终端软件切来切去账号密码散落在各个工具里想审计谁在什么时候执行了什么命令基本靠人肉回忆。更麻烦的是团队里非运维岗的同事临时要看一眼日志你还得给他配密钥、教他用终端沟通成本比干活还高。这套「Python-通过Django在web上实现xshell的功能」的资源核心就是解决这件事用 Django 做后端把 SSH 连接、命令执行、结果回显搬到浏览器里做一个轻量级的 Web 终端。它适合三类人——想给内部运维平台加一个网页终端模块的后端开发、需要统一入口管理多台机器的运维、以及正在学 Django 通道Channels和 WebSocket 实战的进阶学习者。说白了它不是一个要替代专业终端的产品而是一个能嵌进你自己系统里的“连接层”。2. 拆开看架构Django Channels Paramiko 是怎么把终端搬进浏览器的2.1 为什么是 Channels 而不是普通视图普通 Django 视图是“请求-响应”模型客户端发一个 HTTP 请求服务端处理完返回连接就断了。但终端交互是长连接、双向、持续输出的场景你敲一个字符要发到服务端服务端执行命令后要实时把输出推回来中间可能持续几十秒。用 HTTP 轮询去做延迟高、连接开销大体验会很差。Django Channels 在 Django 之上加了一层 ASGI 处理支持 WebSocket 协议正好匹配这种双向流式通信。浏览器端用 WebSocket 连上来Channels 的 Consumer 负责维持这个连接Paramiko 在服务端建立到目标 Linux 机器的 SSH 会话两边数据通过 Consumer 中转。这个分工要理清楚Channels 管“浏览器到 Django”这一段Paramiko 管“Django 到目标机”这一段Consumer 是中间的桥。常见做法是把 Consumer 写成异步的用async_to_sync包装 Paramiko 的阻塞调用或者干脆把 SSH 操作丢到线程池里执行避免阻塞事件循环。这一点如果处理不好会出现“一个用户连上来其他用户全部卡住”的情况后面避坑章节会细说。2.2 环境准备与依赖安装先把项目骨架搭起来。假设你已经有一个 Django 项目如果没有用下面的命令创建。注意 Channels 对 Django 版本有要求Django 4.x 配 Channels 4.x 是比较稳的组合。# 创建虚拟环境避免污染全局包 python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate # 安装核心依赖 pip install django4.2 pip install channels4.0 pip install channels-redis4.1 pip install paramiko3.4 # 创建项目和应用 django-admin startproject webterminal cd webterminal python manage.py startapp terminal参数说明channels-redis不是必须的但如果你打算多进程部署比如用 Daphne 起多个 worker就必须用 Redis 做通道层channel layer否则 WebSocket 消息在不同进程间无法路由。开发阶段可以用InMemoryChannelLayer但上线前一定要换掉。装完之后在settings.py里注册 Channels 并配置 ASGI 应用# settings.py INSTALLED_APPS [ daphne, # 必须放在 channels 前面用于接管 runserver django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, channels, terminal, ] ASGI_APPLICATION webterminal.asgi.application # 开发阶段用内存通道层上线换 Redis CHANNEL_LAYERS { default: { BACKEND: channels.layers.InMemoryChannelLayer, }, }逻辑说明把daphne放在INSTALLED_APPS首位是为了让runserver命令被 Daphne 接管这样开发时也能直接处理 WebSocket 请求不用额外起 ASGI 服务器。ASGI_APPLICATION指向项目的 asgi 配置Channels 会从这里读取路由。2.3 路由与 Consumer 的编写先配置 ASGI 路由把 WebSocket 请求指向我们写的 Consumer# webterminal/asgi.py import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from terminal import routing os.environ.setdefault(DJANGO_SETTINGS_MODULE, webterminal.settings) application ProtocolTypeRouter({ http: get_asgi_application(), websocket: AuthMiddlewareStack( URLRouter(routing.websocket_urlpatterns) ), })# terminal/routing.py from django.urls import re_path from . import consumers websocket_urlpatterns [ re_path(rws/terminal/(?Phost_id\d)/$, consumers.TerminalConsumer.as_asgi()), ]参数说明URL 里带了host_id表示每台被管理的机器对应一个 WebSocket 地址。AuthMiddlewareStack会把 Django 的 session 认证信息带进 Consumer这样你可以在connect方法里判断用户有没有登录、有没有权限操作这台机器。生产环境一定要加权限校验否则任何人猜到 host_id 就能连上。接下来是核心的 Consumer# terminal/consumers.py import json import paramiko from channels.generic.websocket import AsyncWebsocketConsumer from asgiref.sync import sync_to_async class TerminalConsumer(AsyncWebsocketConsumer): async def connect(self): self.host_id self.scope[url_route][kwargs][host_id] # 校验登录状态未登录直接拒绝 if not self.scope[user].is_authenticated: await self.close() return await self.accept() self.ssh_client None self.channel None async def receive(self, text_data): data json.loads(text_data) action data.get(action) if action connect: # 建立 SSH 连接放到线程池执行避免阻塞 await self._open_ssh(data) elif action input: # 把用户输入写到 SSH 通道 if self.channel: await sync_to_async(self.channel.send)(data[data]) elif action resize: # 调整伪终端窗口大小 if self.channel: await sync_to_async(self.channel.resize_pty)( widthdata[cols], heightdata[rows] ) async def _open_ssh(self, data): def _connect(): client paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect( hostnamedata[host], portdata.get(port, 22), usernamedata[username], passworddata.get(password), key_filenamedata.get(key_path), timeout10, ) chan client.invoke_shell(termxterm) chan.settimeout(0.0) return client, chan try: self.ssh_client, self.channel await sync_to_async(_connect)() await self.send(json.dumps({type: connected})) # 启动循环读取 SSH 输出 import asyncio asyncio.create_task(self._read_loop()) except Exception as e: await self.send(json.dumps({type: error, message: str(e)})) async def _read_loop(self): import asyncio while True: if self.channel is None: break try: # recv 是阻塞的用 sync_to_async 包装 data await sync_to_async(self.channel.recv)(4096) if not data: break await self.send(json.dumps({ type: output, data: data.decode(utf-8, errorsreplace) })) except Exception: break await asyncio.sleep(0.01) async def disconnect(self, close_code): if self.channel: self.channel.close() if self.ssh_client: self.ssh_client.close()逻辑说明connect里做认证receive里根据前端发来的 action 分发处理。_open_ssh把 Paramiko 的连接和invoke_shell放在sync_to_async里执行因为 Paramiko 是同步阻塞库直接在异步函数里调用会卡住整个事件循环。_read_loop是一个持续读取 SSH 输出的任务读到数据就通过 WebSocket 推给前端。disconnect里要记得关闭通道和客户端否则连接会泄漏。参数说明invoke_shell(termxterm)里的 term 类型影响终端转义序列的解析前端 xterm.js 一般用 xterm 或 xterm-256color。settimeout(0.0)把通道设为非阻塞模式配合循环读取。recv(4096)每次读 4KB太小会增加循环次数太大可能延迟回显4096 是个折中值。2.4 前端 xterm.js 的接入前端用 xterm.js 渲染终端界面它负责把用户按键转成数据发给 WebSocket同时把服务端推来的输出写进屏幕。// static/js/terminal.js import { Terminal } from xterm; import { FitAddon } from xterm-addon-fit; import xterm/css/xterm.css; const term new Terminal({ cursorBlink: true, fontSize: 14, fontFamily: Menlo, Monaco, monospace, theme: { background: #1e1e1e, foreground: #d4d4d4 } }); const fitAddon new FitAddon(); term.loadAddon(fitAddon); term.open(document.getElementById(terminal)); fitAddon.fit(); const hostId document.getElementById(terminal).dataset.hostId; const ws new WebSocket(ws://${window.location.host}/ws/terminal/${hostId}/); ws.onopen () { // 连接建立后发送 SSH 认证信息 ws.send(JSON.stringify({ action: connect, host: 192.168.1.10, port: 22, username: ops, password: your-password })); }; ws.onmessage (event) { const msg JSON.parse(event.data); if (msg.type output) { term.write(msg.data); } else if (msg.type error) { term.write(\r\n\x1b[31m连接失败: ${msg.message}\x1b[0m\r\n); } }; // 用户按键 - 发给服务端 term.onData((data) { if (ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify({ action: input, data: data })); } }); // 窗口大小变化时同步给服务端 window.addEventListener(resize, () { fitAddon.fit(); if (ws.readyState WebSocket.OPEN) { ws.send(JSON.stringify({ action: resize, cols: term.cols, rows: term.rows })); } });逻辑说明term.onData捕获用户的每一次按键包括回车、退格、CtrlC 等控制字符原样发给服务端。服务端把这些字符写进 SSH 通道远端 shell 解析后返回输出再通过term.write渲染。resize事件同步终端尺寸否则vim、top这类全屏程序会显示错乱。参数说明fitAddon.fit()根据容器大小自动计算行列数容器必须有明确的宽高否则算出来是 0。密码在前端明文传输是个隐患生产环境应该改成后端根据 host_id 从数据库取凭据前端只传 host_id不传密码。3. 把连接管起来主机配置、凭据管理与多会话隔离3.1 主机信息建模把机器信息存到数据库前端只传 host_id凭据由后端查表获取这样既安全又方便管理。# terminal/models.py from django.db import models from django.contrib.auth.models import User class Host(models.Model): name models.CharField(max_length64, verbose_name主机别名) ip models.GenericIPAddressField(verbose_nameIP地址) port models.IntegerField(default22, verbose_nameSSH端口) username models.CharField(max_length64, verbose_name登录用户) auth_type models.CharField( max_length10, choices[(password, 密码), (key, 密钥)], defaultpassword ) password models.CharField(max_length256, blankTrue, verbose_name密码) key_file models.FileField(upload_tokeys/, blankTrue, verbose_name密钥文件) owner models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name归属用户) created_at models.DateTimeField(auto_now_addTrue) class Meta: verbose_name 主机 verbose_name_plural 主机参数说明auth_type区分密码和密钥两种认证方式key_file用 FileField 存上传的私钥。密码字段实际存储时应该加密Django 没有内置的字段级加密常见做法是用cryptography库的 Fernet 做对称加密或者接入 KMS。owner字段用于权限隔离查询时过滤ownerrequest.user防止越权访问别人的机器。3.2 凭据加密与取用# terminal/crypto.py import base64 import os from cryptography.fernet import Fernet from django.conf import settings def _get_fernet(): # 密钥从环境变量读取不要硬编码在代码里 key settings.SSH_CRED_KEY.encode() return Fernet(key) def encrypt_password(plain: str) - str: f _get_fernet() return f.encrypt(plain.encode()).decode() def decrypt_password(token: str) - str: f _get_fernet() return f.decrypt(token.encode()).decode()逻辑说明Fernet 是对称加密密钥必须固定且保密否则重启后解不开之前存的数据。密钥生成用Fernet.generate_key()生成后放到环境变量或配置中心不要提交到代码仓库。取用凭据时在 Consumer 里根据 host_id 查库、解密再传给 Paramiko。3.3 多会话隔离与资源限制每个 WebSocket 连接对应一个独立的 Consumer 实例实例之间天然隔离。但要注意两点一是同一个用户可能开多个标签页连同一台机器每个连接都会新建一个 SSH 会话目标机的sshd有 MaxSessions 限制开太多会被拒绝二是如果某个会话执行了tail -f这类持续输出的命令读取循环会一直跑占用线程池资源。常见做法是加一个会话数限制在connect时检查当前用户已建立的连接数超过阈值就拒绝。另外给_read_loop加一个空闲超时比如 30 分钟没有输入就主动断开避免僵尸会话堆积。# 在 Consumer 里加空闲检测 import time async def _read_loop(self): last_active time.time() while True: if time.time() - last_active 1800: await self.send(json.dumps({type: error, message: 会话超时})) await self.close() break # ... 原有读取逻辑 if data: last_active time.time()参数说明1800 秒是 30 分钟按团队实际使用频率调整。如果用户经常挂着终端看监控可以调大或者改成只在无输出时计时。4. 避坑与排查那些让我加班到凌晨的细节4.1 现象一个用户连上后其他用户全部卡死原因Paramiko 的recv是阻塞调用如果直接在异步的_read_loop里调用而没有用sync_to_async包装整个事件循环会被卡住所有 WebSocket 连接都无法处理消息。解决所有 Paramiko 的阻塞方法connect、recv、send、resize_pty都必须用sync_to_async包装。另外sync_to_async默认在线程池里执行线程池大小有限如果并发连接很多需要调整ASGI_THREADS环境变量或者改用ThreadPoolExecutor自己管理。4.2 现象终端里vim打开后花屏方向键变成^[[A原因前端 xterm.js 的 term 类型和服务端invoke_shell的 term 不匹配或者前端没有正确处理转义序列。另外resize没有同步导致远端以为终端还是默认的 80x24。解决确保invoke_shell(termxterm-256color)前端 xterm.js 初始化时也设置termName: xterm-256color。在connect成功后立即发送一次resize把当前的行列数同步过去。如果还有花屏检查 WebSocket 传输时有没有对特殊字符做转义json.dumps默认会转义非 ASCII但控制字符是 ASCII 范围内的一般没问题。4.3 现象连接几秒后自动断开日志显示Connection reset by peer原因目标机的sshd配置了ClientAliveInterval如果一段时间没有数据交互会主动断开。另外有些云主机的安全组对空闲连接有超时限制。解决在 Paramiko 的Transport上开启 keepaliveclient paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(...) transport client.get_transport() transport.set_keepalive(30) # 每 30 秒发一次心跳参数说明30 秒是常见值要小于目标机ClientAliveInterval的设置。如果目标机是 60 秒设 30 秒就够。同时在前端加一个心跳每 20 秒发一个空消息保持 WebSocket 活跃。4.4 现象上传密钥文件后连接报not a valid RSA private key file原因Paramiko 对密钥格式有要求OpenSSH 新格式以-----BEGIN OPENSSH PRIVATE KEY-----开头在旧版本 Paramiko 里不支持需要转成 PEM 格式。另外密钥文件权限不对也会报错。解决用ssh-keygen -p -m PEM -f your_key把密钥转成 PEM 格式。或者在代码里用paramiko.RSAKey.from_private_key_file()显式指定类型。文件权限在服务端保存时设为 600Paramiko 会检查。4.5 现象生产环境用 InMemoryChannelLayer多 worker 下 WebSocket 消息丢失原因InMemoryChannelLayer只在单个进程内有效Daphne 起多个 worker 时WebSocket 连接可能落在 worker A而消息从 worker B 发出B 找不到 A 的通道消息就丢了。解决换成channels_redisCHANNEL_LAYERS { default: { BACKEND: channels_redis.core.RedisChannelLayer, CONFIG: { hosts: [(127.0.0.1, 6379)], }, }, }参数说明Redis 地址按实际部署填如果有密码加在 hosts 元组里。上线前务必确认 Redis 连通性否则 WebSocket 会直接连不上。5. 进阶把命令审计和会话回放做进去基础功能跑通之后真正让这套东西在团队里站住脚的是审计能力。运维平台没有审计等于没有后悔药。我一般会在 Consumer 的receive里加一层记录用户每次发送input时把数据和时间戳写进数据库同时把服务端返回的output也存一份按会话 ID 关联。这样事后可以按人、按机器、按时间段查“谁在什么时候执行了什么”。# terminal/models.py 追加 class CommandLog(models.Model): session_id models.CharField(max_length64, db_indexTrue) user models.ForeignKey(User, on_deletemodels.CASCADE) host models.ForeignKey(Host, on_deletemodels.CASCADE) direction models.CharField(max_length6) # input / output content models.TextField() created_at models.DateTimeField(auto_now_addTrue, db_indexTrue) class Meta: indexes [ models.Index(fields[user, created_at]), models.Index(fields[host, created_at]), ]写入时用批量插入降低开销比如每 20 条或每 2 秒 flush 一次不要每条都写库否则高频输出会把数据库打满。会话回放就是按session_id和created_at排序把 input 和 output 按时间轴重放前端可以用一个简单的播放器按时间间隔逐条渲染。验证审计是否生效可以写一个管理命令模拟一次连接、执行几条命令、断开然后查库确认记录完整python manage.py shell -c from terminal.models import CommandLog logs CommandLog.objects.filter(session_idtest-session).order_by(created_at) for log in logs: print(log.direction, log.content[:50]) 参数说明session_id在 Consumer 的connect里生成用uuid4().hex即可贯穿整个会话。查询时加db_indexTrue的字段做过滤避免全表扫描。还有一个容易忽略的点命令审计要记录原始输入但用户可能输入密码比如mysql -p后回车输入密码这些敏感内容不应该明文存。常见做法是对 input 做正则匹配遇到password、passwd、-p等关键词后的内容做脱敏或者干脆只记录命令本身不记录交互式输入。这个取舍看团队的安全要求但至少要有意识。从那以后我每次做 Web 终端类的项目都会先把审计表建好、把脱敏规则定好再动手写连接逻辑。因为连接层可以慢慢调审计漏了就是真的漏了补不回来。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

WikiSkill:把Agent执行经验编译为可复用技能库的完整方案

WikiSkill:把Agent执行经验编译为可复用技能库的完整方案

之前开发 Agent 类应用时,我一直被一个问题困扰:Agent 在每次任务里都会产生大量轨迹、决策记录和试错过程,但任务一结束,这些经验就基本丢掉了。下一次遇到类似问题,模型还是从零开始推理,同样的坑还会再踩…

2026/10/12 14:42:22 阅读更多 →
红队评估和渗透测试的区别是什么?2026 年从目标、方法到交付物的对比

红队评估和渗透测试的区别是什么?2026 年从目标、方法到交付物的对比

本文为安全研究团队的技术实践总结,文中涉及环境均为自有系统或已获授权的测试目标。渗透测试的目标是"找漏洞",红队评估的目标是"测防守",两者在范围、隐蔽性要求与交付物上都有明显差异,不能互相替代。 一句…

2026/10/12 14:40:57 阅读更多 →
图书销售系统从需求到答辩:SpringBoot+Vue数据库设计全解

图书销售系统从需求到答辩:SpringBoot+Vue数据库设计全解

开头拿到“图书销售系统的设计与实现”这个毕设题目,很多同学的第一反应是搜源码、找现成模板,但真正动手后才发现,要么代码跑不起来,要么答辩时被老师问得说不出话。我这些年看过太多毕设项目,图书销售系统算是计算机…

2026/10/12 14:41:03 阅读更多 →

最新新闻

金字塔原理:职场沟通结构化表达的四个原则与实战技巧

金字塔原理:职场沟通结构化表达的四个原则与实战技巧

废话不多说,这可能是你在职场沟通中最值得补的一课。我见过太多人,明明业务能力很强,可一开口就败下阵来:汇报讲了十分钟,领导眉头越皱越紧;邮件写了几百字,对方回复“所以你想表达什么”&#…

2026/10/13 17:10:09 阅读更多 →
实时数据采集架构设计:从需求量化到全链路调优

实时数据采集架构设计:从需求量化到全链路调优

直接开讲。实时数据采集架构设计这件事,我前前后后做了三年多,从最早只会写脚本定时拉数据,到后来能独立设计一套覆盖采集、缓冲、计算、落库全链路的实时方案,中间踩过不少坑。很多人一听到“实时数据采集”这几个字,…

2026/10/13 17:10:09 阅读更多 →
专科生AI论文写作平台实测:8个靠谱工具全解析

专科生AI论文写作平台实测:8个靠谱工具全解析

实话实说,专科生写毕业论文,难度不比本科生低,甚至更难。难在哪?时间紧、任务重,还有相当一部分同学是第一次接触完整的学术写作流程。我自己当年在专科阶段写毕业设计报告时,光是“怎么开头”“怎么把一段…

2026/10/13 17:10:09 阅读更多 →
Docker部署Ceph实现RBD镜像创建与挂载全流程

Docker部署Ceph实现RBD镜像创建与挂载全流程

1. 为什么非得用 Docker 部署 Ceph?——从“三台物理机起步”到“单机快速验证”的真实动因很多人看到“Docker 部署 Ceph”第一反应是皱眉:“Ceph 不是分布式存储系统吗?跑在容器里靠谱?”这问题我当年在某高校实验室带学生做存储…

2026/10/13 17:10:09 阅读更多 →
Python机器学习入侵检测系统实战:从NSL-KDD到Flask部署

Python机器学习入侵检测系统实战:从NSL-KDD到Flask部署

简介:这是一份基于Python机器学习的入侵检测系统项目源码,面向希望入门或进阶网络安全与机器学习方向的学习者,可作为毕业设计、课程设计、大作业或工程实训的参考方案。项目围绕网络流量嗅探与异常检测展开,包含数据预处理、特征…

2026/10/13 17:10:09 阅读更多 →
荷兰耕地遥感语义分割:从数据标注到模型训练全流程实战

荷兰耕地遥感语义分割:从数据标注到模型训练全流程实战

简介:这份荷兰耕地语义分割遥感影像数据集面向遥感图像处理、计算机视觉与农业监测方向的研究者和开发者,用于训练和验证耕地识别与像素级分割模型。资源包共2000个文件,以xml标注文件为主要类型,对应每张遥感影像的像素级类别标签…

2026/10/13 17:09:08 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →