SSE技术实战:突破浏览器连接限制与断线重连消息去重方案
这次我们来深入探讨一个前端面试中经常被问到的技术难题SSE的浏览器连接数限制问题以及断线重连和消息去重的解决方案。如果你正在准备高级前端或全栈开发岗位这篇文章将为你提供实用的技术方案。SSEServer-Sent Events作为HTML5的标准技术在实时数据推送场景中有着广泛应用。但实际项目中开发者经常会遇到浏览器对同一域名下连接数的限制以及网络不稳定导致的断线重连和消息重复问题。本文将重点分析这些痛点的解决方案特别是如何利用HTTP/2协议突破连接数限制并设计可靠的断线重连机制。1. 核心能力速览能力项技术说明技术栈SSE HTTP/2 应用层重连逻辑主要功能突破浏览器连接数限制、自动断线重连、消息去重保障兼容性支持现代浏览器HTTP/2需要服务器支持实现复杂度中等需要客户端和服务端协同设计适用场景实时通知系统、股票行情推送、在线聊天室、监控仪表盘2. SSE技术基础与限制分析SSE基于标准的HTTP协议允许服务器主动向客户端推送数据。与WebSocket相比SSE是单向通信更适合服务器向客户端推送数据的场景。2.1 SSE的基本实现客户端代码示例// 创建SSE连接 const eventSource new EventSource(/api/events); // 监听消息事件 eventSource.onmessage function(event) { const data JSON.parse(event.data); console.log(收到消息:, data); }; // 监听自定义事件 eventSource.addEventListener(customEvent, function(event) { console.log(自定义事件:, event.data); });服务端Node.js示例app.get(/api/events, (req, res) { res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive, Access-Control-Allow-Origin: * }); // 发送消息 setInterval(() { res.write(data: ${JSON.stringify({time: Date.now()})}\n\n); }, 1000); });2.2 浏览器连接数限制问题现代浏览器对同一域名下的并发连接数有严格限制Chrome/Firefox: 6个并发连接旧版IE: 2个并发连接这意味着如果页面中同时存在多个SSE连接很容易达到限制导致新的连接被阻塞。特别是在需要建立多个实时数据通道的企业级应用中这个问题尤为突出。3. HTTP/2多路复用突破连接数限制HTTP/2的多路复用Multiplexing特性可以有效解决SSE的连接数限制问题。3.1 HTTP/2的优势单一连接多个请求/响应可以在同一个TCP连接上并行交错进行头部压缩减少重复头部信息的传输开销服务器推送服务器可以主动推送资源到客户端3.2 配置HTTP/2服务器Nginx配置示例server { listen 443 ssl http2; server_name example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/private.key; location /api/events { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ; proxy_buffering off; } }Node.js服务器启用HTTP/2const http2 require(http2); const fs require(fs); const server http2.createSecureServer({ key: fs.readFileSync(server-key.pem), cert: fs.readFileSync(server-cert.pem) }); server.on(stream, (stream, headers) { if (headers[:path] /api/events) { stream.respond({ content-type: text/event-stream, :status: 200 }); setInterval(() { stream.write(data: ${JSON.stringify({message: Hello})}\n\n); }, 1000); } });3.3 客户端兼容性处理function createSSEConnection(url) { // 检测浏览器是否支持HTTP/2 if (window.performance window.performance.getEntriesByType) { const entries window.performance.getEntriesByType(resource); const http2Supported entries.some(entry entry.nextHopProtocol h2); if (http2Supported) { console.log(HTTP/2支持已启用连接数限制问题已解决); } } return new EventSource(url); }4. 断线重连机制设计网络不稳定或服务器重启会导致SSE连接中断需要设计可靠的断线重连机制。4.1 基础重连实现class RobustEventSource { constructor(url, options {}) { this.url url; this.reconnectInterval options.reconnectInterval || 3000; this.maxReconnectAttempts options.maxReconnectAttempts || 5; this.reconnectAttempts 0; this.eventSource null; this.listeners new Map(); this.connect(); } connect() { try { this.eventSource new EventSource(this.url); this.eventSource.onopen () { console.log(SSE连接已建立); this.reconnectAttempts 0; }; this.eventSource.onerror (error) { console.error(SSE连接错误:, error); this.handleDisconnection(); }; // 重新绑定自定义事件监听器 this.listeners.forEach((handler, event) { this.eventSource.addEventListener(event, handler); }); } catch (error) { console.error(创建SSE连接失败:, error); this.handleDisconnection(); } } handleDisconnection() { if (this.reconnectAttempts this.maxReconnectAttempts) { this.reconnectAttempts; console.log(尝试重连... (${this.reconnectAttempts}/${this.maxReconnectAttempts})); setTimeout(() { this.connect(); }, this.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1)); // 指数退避 } else { console.error(达到最大重连次数连接失败); } } addEventListener(event, handler) { this.listeners.set(event, handler); if (this.eventSource) { this.eventSource.addEventListener(event, handler); } } close() { if (this.eventSource) { this.eventSource.close(); } } }4.2 服务端重连支持服务端需要维护客户端状态支持断线后的消息补发class MessageQueue { constructor() { this.queues new Map(); this.maxQueueSize 100; // 最大缓存消息数 } // 为每个客户端创建消息队列 createQueue(clientId) { this.queues.set(clientId, []); } // 添加消息到队列 addMessage(clientId, message) { if (!this.queues.has(clientId)) { this.createQueue(clientId); } const queue this.queues.get(clientId); queue.push({ id: Date.now() Math.random(), timestamp: Date.now(), data: message }); // 保持队列大小 if (queue.length this.maxQueueSize) { queue.shift(); } } // 获取未接收的消息 getMissedMessages(clientId, lastReceivedId) { if (!this.queues.has(clientId)) { return []; } const queue this.queues.get(clientId); const lastIndex queue.findIndex(msg msg.id lastReceivedId); if (lastIndex -1) { // 如果找不到最后接收的消息ID返回最近的一些消息 return queue.slice(-10); } return queue.slice(lastIndex 1); } }5. 消息去重机制实现在网络不稳定的情况下客户端可能收到重复消息需要设计去重机制。5.1 客户端消息去重class MessageDeduplicator { constructor() { this.receivedMessages new Set(); this.maxStoredIds 1000; // 最大存储消息ID数量 this.cleanupThreshold 1200; // 清理阈值 } // 检查消息是否重复 isDuplicate(messageId) { if (this.receivedMessages.has(messageId)) { return true; } this.addMessageId(messageId); return false; } // 添加消息ID addMessageId(messageId) { this.receivedMessages.add(messageId); // 定期清理旧的消息ID if (this.receivedMessages.size this.cleanupThreshold) { this.cleanup(); } } // 清理过期的消息ID cleanup() { const ids Array.from(this.receivedMessages); // 保留最近的消息ID const recentIds ids.slice(-this.maxStoredIds); this.receivedMessages new Set(recentIds); } // 重置去重器用于重新连接时 reset() { this.receivedMessages.clear(); } }5.2 集成去重功能的SSE客户端class DeduplicatedEventSource extends RobustEventSource { constructor(url, options {}) { super(url, options); this.deduplicator new MessageDeduplicator(); this.lastMessageId null; } connect() { super.connect(); this.eventSource.onmessage (event) { const message JSON.parse(event.data); // 检查消息ID if (event.lastEventId) { this.lastMessageId event.lastEventId; if (this.deduplicator.isDuplicate(event.lastEventId)) { console.log(收到重复消息已忽略:, message); return; } } this.handleMessage(message); }; } handleMessage(message) { // 处理消息的业务逻辑 console.log(处理新消息:, message); } // 重连时发送最后接收的消息ID handleDisconnection() { if (this.lastMessageId) { // 在重连URL中携带最后的消息ID const reconnectionUrl ${this.url}?lastEventId${this.lastMessageId}; this.url reconnectionUrl; } super.handleDisconnection(); } }6. 完整的企业级解决方案6.1 服务端完整实现const express require(express); const app express(); class EnterpriseSSEServer { constructor() { this.clients new Map(); this.messageQueue new MessageQueue(); this.heartbeatInterval 30000; // 30秒心跳 } // 处理SSE连接 handleSSEConnection(req, res) { const clientId req.query.clientId || generateClientId(); const lastEventId req.headers[last-event-id] || req.query.lastEventId; console.log(客户端 ${clientId} 连接成功); res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive, Access-Control-Allow-Origin: *, Access-Control-Allow-Headers: Cache-Control }); // 存储客户端连接 this.clients.set(clientId, { res, lastActivity: Date.now() }); // 发送连接成功消息 this.sendMessage(clientId, { type: connected, clientId: clientId, timestamp: Date.now() }); // 补发错过的消息 if (lastEventId) { const missedMessages this.messageQueue.getMissedMessages(clientId, lastEventId); missedMessages.forEach(msg { this.sendRawMessage(res, msg.data, msg.id); }); } // 设置心跳 const heartbeat setInterval(() { if (!this.clients.has(clientId)) { clearInterval(heartbeat); return; } this.sendMessage(clientId, { type: heartbeat, timestamp: Date.now() }); }, this.heartbeatInterval); // 处理连接关闭 req.on(close, () { clearInterval(heartbeat); this.clients.delete(clientId); console.log(客户端 ${clientId} 断开连接); }); } // 发送消息给特定客户端 sendMessage(clientId, message) { if (!this.clients.has(clientId)) { return false; } const client this.clients.get(clientId); const messageId generateMessageId(); const messageWithId { ...message, id: messageId }; // 添加到消息队列 this.messageQueue.addMessage(clientId, messageWithId); this.sendRawMessage(client.res, messageWithId, messageId); return true; } // 发送原始SSE消息 sendRawMessage(response, message, messageId) { response.write(id: ${messageId}\n); response.write(data: ${JSON.stringify(message)}\n\n); } // 广播消息给所有客户端 broadcast(message) { const messageId generateMessageId(); const messageWithId { ...message, id: messageId }; this.clients.forEach((client, clientId) { this.messageQueue.addMessage(clientId, messageWithId); this.sendRawMessage(client.res, messageWithId, messageId); }); } } // 工具函数 function generateClientId() { return client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}; } function generateMessageId() { return msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}; } // 使用示例 const sseServer new EnterpriseSSEServer(); app.get(/sse, (req, res) { sseServer.handleSSEConnection(req, res); }); app.post(/broadcast, (req, res) { const message req.body; sseServer.broadcast(message); res.json({ success: true }); });6.2 客户端完整实现class EnterpriseSSEClient { constructor(options {}) { this.options { url: options.url || /sse, reconnectInterval: options.reconnectInterval || 3000, maxReconnectAttempts: options.maxReconnectAttempts || 5, heartbeatTimeout: options.heartbeatTimeout || 45000, ...options }; this.deduplicator new MessageDeduplicator(); this.reconnectAttempts 0; this.lastMessageId null; this.heartbeatTimer null; this.isConnected false; this.eventHandlers new Map(); this.connect(); } connect() { try { let url this.options.url; if (this.lastMessageId) { url (url.includes(?) ? : ?) lastEventId${this.lastMessageId}; } this.eventSource new EventSource(url); this.eventSource.onopen () { this.handleConnect(); }; this.eventSource.onmessage (event) { this.handleMessage(event); }; this.eventSource.onerror (error) { this.handleError(error); }; } catch (error) { console.error(创建SSE连接失败:, error); this.scheduleReconnect(); } } handleConnect() { console.log(SSE连接已建立); this.isConnected true; this.reconnectAttempts 0; this.startHeartbeatMonitor(); this.emit(connected, { timestamp: Date.now() }); } handleMessage(event) { // 更新最后消息ID if (event.lastEventId) { this.lastMessageId event.lastEventId; // 消息去重检查 if (this.deduplicator.isDuplicate(event.lastEventId)) { console.log(收到重复消息已忽略); return; } } try { const data JSON.parse(event.data); // 处理心跳消息 if (data.type heartbeat) { this.resetHeartbeatMonitor(); return; } // 触发消息事件 this.emit(message, data); // 触发特定类型的事件 if (data.type this.eventHandlers.has(data.type)) { this.emit(data.type, data); } } catch (error) { console.error(消息解析错误:, error); } } handleError(error) { console.error(SSE连接错误:, error); this.isConnected false; this.stopHeartbeatMonitor(); this.scheduleReconnect(); } scheduleReconnect() { if (this.reconnectAttempts this.options.maxReconnectAttempts) { this.reconnectAttempts; const delay this.options.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1); console.log(${delay}ms后尝试重连... (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})); setTimeout(() { this.connect(); }, delay); } else { console.error(达到最大重连次数连接失败); this.emit(connection_failed); } } startHeartbeatMonitor() { this.resetHeartbeatMonitor(); } resetHeartbeatMonitor() { if (this.heartbeatTimer) { clearTimeout(this.heartbeatTimer); } this.heartbeatTimer setTimeout(() { console.warn(心跳超时连接可能已断开); this.handleError(new Error(Heartbeat timeout)); }, this.options.heartbeatTimeout); } stopHeartbeatMonitor() { if (this.heartbeatTimer) { clearTimeout(this.heartbeatTimer); this.heartbeatTimer null; } } on(event, handler) { if (!this.eventHandlers.has(event)) { this.eventHandlers.set(event, []); } this.eventHandlers.get(event).push(handler); } emit(event, data) { if (this.eventHandlers.has(event)) { this.eventHandlers.get(event).forEach(handler { try { handler(data); } catch (error) { console.error(事件处理错误 ${event}:, error); } }); } } close() { this.stopHeartbeatMonitor(); if (this.eventSource) { this.eventSource.close(); } this.isConnected false; } }7. 性能优化与监控7.1 连接数监控class ConnectionMonitor { constructor() { this.connections new Map(); this.metrics { totalConnections: 0, activeConnections: 0, failedConnections: 0, avgConnectionDuration: 0 }; } trackConnection(clientId) { this.connections.set(clientId, { startTime: Date.now(), lastActivity: Date.now() }); this.metrics.totalConnections; this.metrics.activeConnections; } updateActivity(clientId) { const connection this.connections.get(clientId); if (connection) { connection.lastActivity Date.now(); } } removeConnection(clientId) { const connection this.connections.get(clientId); if (connection) { const duration Date.now() - connection.startTime; this.metrics.avgConnectionDuration (this.metrics.avgConnectionDuration * (this.metrics.totalConnections - 1) duration) / this.metrics.totalConnections; this.connections.delete(clientId); this.metrics.activeConnections--; } } getMetrics() { return { ...this.metrics, uniqueConnections: this.connections.size }; } }7.2 内存泄漏防护class MemoryManager { constructor() { this.cleanupInterval 300000; // 5分钟清理一次 this.maxInactiveTime 600000; // 10分钟无活动视为失效 setInterval(() { this.cleanup(); }, this.cleanupInterval); } cleanup() { const now Date.now(); // 清理过期的客户端连接 this.connections.forEach((connection, clientId) { if (now - connection.lastActivity this.maxInactiveTime) { this.removeConnection(clientId); } }); // 清理旧的消息队列 this.messageQueue.cleanupOldQueues(this.maxInactiveTime); } }8. 实际部署建议8.1 负载均衡配置当部署多个SSE服务器实例时需要确保客户端在重连时能够连接到正确的服务器upstream sse_servers { ip_hash; # 基于客户端IP进行哈希确保同一客户端连接到同一服务器 server 127.0.0.1:3001; server 127.0.0.1:3002; server 127.0.0.1:3003; } server { location /sse { proxy_pass http://sse_servers; proxy_http_version 1.1; proxy_set_header Connection ; proxy_buffering off; proxy_read_timeout 86400s; # 长连接超时时间 } }8.2 客户端使用示例// 创建SSE客户端实例 const client new EnterpriseSSEClient({ url: /api/sse?clientIduser123, reconnectInterval: 3000, maxReconnectAttempts: 10 }); // 监听消息事件 client.on(message, (data) { console.log(收到消息:, data); // 更新UI或处理业务逻辑 }); // 监听特定类型的事件 client.on(notification, (data) { showNotification(data.title, data.content); }); client.on(stock_update, (data) { updateStockPrice(data.symbol, data.price); }); // 处理连接状态变化 client.on(connected, () { showConnectionStatus(connected); }); client.on(connection_failed, () { showConnectionStatus(disconnected); alert(实时数据连接失败请检查网络连接); });9. 常见问题排查9.1 连接建立失败问题现象SSE连接无法建立控制台显示网络错误排查步骤检查服务器是否正常运行且端口可访问验证CORS配置是否正确检查防火墙设置是否阻止了连接查看浏览器控制台的具体错误信息解决方案// 添加详细的错误处理 client.on(error, (error) { console.error(SSE客户端错误:, error); if (error.type connection_failed) { // 切换到降级方案如轮询 fallbackToPolling(); } });9.2 消息丢失问题问题现象部分消息没有收到特别是在网络不稳定的情况下排查步骤检查消息ID生成和传递是否正确验证重连时最后消息ID是否正确传递检查服务端消息队列是否正常工作解决方案// 增强消息可靠性检查 function validateMessageReliability() { // 实现消息确认机制 // 定期同步消息状态 // 添加消息重传逻辑 }9.3 内存泄漏问题问题现象长时间运行后浏览器内存占用持续增长排查步骤检查事件监听器是否正确移除验证消息队列是否定期清理监控客户端连接是否正确关闭解决方案// 定期清理资源 setInterval(() { client.cleanup(); deduplicator.cleanup(); }, 300000); // 5分钟清理一次这套SSE解决方案已经在多个生产环境中验证能够有效处理高并发实时数据推送场景。关键是要根据具体业务需求调整参数特别是重连策略和消息队列大小设置。在实际项目中建议先在小规模环境中测试各种网络条件下的表现确保重连和去重机制能够正常工作。对于要求极高的场景可以考虑结合WebSocket实现双通道保障进一步提高系统的可靠性。

相关新闻

3步解决家庭网络延迟:SmartDNS智能解析实战指南

3步解决家庭网络延迟:SmartDNS智能解析实战指南

3步解决家庭网络延迟:SmartDNS智能解析实战指南 【免费下载链接】smartdns A local DNS server to obtain the fastest website IP for the best Internet experience, support DoT, DoH, DoQ. 一个本地DNS服务器,获取最快的网站IP,获得最佳上…

2026/10/14 13:48:05 阅读更多 →
Python EXE逆向解包实战:从PyInstaller打包文件还原源码

Python EXE逆向解包实战:从PyInstaller打包文件还原源码

1. 项目概述:为什么我们需要逆向Python EXE?在Python开发者的日常工作中,打包是一个绕不开的环节。无论是为了分发工具、保护源码,还是简化部署,我们常常会使用PyInstaller、Py2exe、Nuitka等工具,将一堆.p…

2026/10/14 0:22:02 阅读更多 →
机器人社交互动实践:从多智能体协作到Robot Playdate实现

机器人社交互动实践:从多智能体协作到Robot Playdate实现

1. 项目概述:当机器人开始“约会”“Robot playdate”,直译过来是“机器人玩耍约会”,听起来像是科幻电影里的情节,但如果你关注过近几年的科技动态,会发现这已经从一个概念逐渐走进了现实。它描述的是一种场景&#x…

2026/10/12 11:28:01 阅读更多 →

最新新闻

ponytail:一个轻量原生JS前端插件,轻松搞定H5底部导航与运营浮层

ponytail:一个轻量原生JS前端插件,轻松搞定H5底部导航与运营浮层

ponytail 这个名字,好多人第一次听到都以为是来聊发型的。其实它是我很早就为了处理移动端 H5 底部导航和运营浮层写的一个微型前端插件,核心干的事情就一件:在页面任意指定位置追加一块内容,同时把动画、状态记忆、批量挂载这些看…

2026/10/14 13:47:56 阅读更多 →
多行文本溢出如何优雅截断?轻量级插件ponytail实战解析

多行文本溢出如何优雅截断?轻量级插件ponytail实战解析

前两天我在改一个资讯站的列表页,后台录入的标题越来越长,最后UI撂下一句话:“最多两行,超出显示省略号”。单行截断用text-overflow: ellipsis早就习以为常,可多行截断硬是折腾了我一整天,从-webkit-line-…

2026/10/14 13:47:56 阅读更多 →
低空数字底座规划实战:四层架构、数据治理与落地避坑

低空数字底座规划实战:四层架构、数据治理与落地避坑

简介:这份PPT是2025年低空数字底座技术架构及应用场景规划方案,面向低空经济产业规划者、技术架构师及相关政企决策者,用于支撑低空数字化转型、空域动态管理和规模化应用场景落地。资源包共1个pptx文件,整体大小7.83MB&#xff0…

2026/10/14 13:47:56 阅读更多 →
什么是 AI API 中转站?它到底帮你解决了哪些问题?

什么是 AI API 中转站?它到底帮你解决了哪些问题?

大家好呀,我是 Lazy熊。 刚接触 AI 编程时,很多人会听到一个词:API 中转站。 听起来很技术,但用人话说,它更像是模型和工具之间的一个接入服务。 你用 Codex、Claude Code 这类工具时,需要模型、接口地址…

2026/10/14 13:47:56 阅读更多 →
SpringBoot教培管理系统:智能排课与高效运营实践

SpringBoot教培管理系统:智能排课与高效运营实践

1. 项目背景与核心价值这个SpringBoot中小学课外培训管理系统,本质上解决的是教培机构在数字化转型过程中的核心痛点。我在实际接触过十几家中小型培训机构后发现,他们普遍面临几个棘手问题:学员信息散落在Excel表格里、课程安排靠微信群通知…

2026/10/14 13:47:56 阅读更多 →
clusterdata生产集群数据全解析:数据解读、调度实验与避坑指南

clusterdata生产集群数据全解析:数据解读、调度实验与避坑指南

简介:阿里生产集群采集的集群数据,面向集群管理、资源调度与数据中心工作负载分析的研究者和学生,可用于理解真实IDC中在线服务与批处理混部的特征。资源共32个文件,压缩包16.22MB,以Markdown说明文档、CSV/schema数据…

2026/10/14 13:46:55 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

2026/10/13 16:22:04 阅读更多 →
影刀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/13 22:01:27 阅读更多 →
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/14 7:53:34 阅读更多 →