Go监听MySQL binlog实战:轻量级数据变更捕获与生产避坑指南
1. 这不是“监听MySQL”而是重建一套轻量级数据变更管道很多人第一次看到“用Go监听binlog”这个说法第一反应是“MySQL自己不就有mysqlbinlog命令吗再不济写个Python脚本调用一下不就完了”——我试过也踩过。那套方案在测试环境跑得飞快一上生产三小时崩两次连接池耗尽、位点丢失、主从切换后直接断连重放失败。后来才明白问题根本不在于“能不能读到日志”而在于如何把binlog这种强时序、高吞吐、带状态的底层协议稳稳地接进现代应用架构里。canal这个名字常被误认为是阿里开源的那个Java服务。但这里说的“基于canal的go库”指的是社区中一批深度借鉴canal协议设计、专为Go生态重构的轻量级客户端实现比如go-mysql现名mydump、canal-go、以及更贴近原生协议的go-binlog。它们不部署独立服务不依赖ZooKeeper不管理元数据存储只做一件事以MySQL slave身份接入主库持续拉取并解析binlog event将原始字节流转化为结构化Go struct。关键词不是“canal”而是“协议兼容性”“event粒度控制”“位点精准回溯”——这决定了你后续能做什么是仅做简单告警还是支撑实时数仓入湖是同步几张核心表还是全库DDLDML无损捕获。这类库解决的是传统ETL链路里最脆弱的一环数据源侧的变更捕获。它绕开了应用层埋点的侵入性、避免了轮询查询的资源浪费、也跳过了触发器对主库性能的隐式拖累。一个真实场景某电商后台系统需要将订单状态变更实时推送到风控引擎。如果用定时任务查updated_at ?延迟至少30秒且无法区分是人工修改还是系统自动更新而用基于binlog的监听从UPDATE order SET statuspaid WHERE id123执行完成到风控服务收到结构化事件实测端到端延迟稳定在800ms以内且附带完整的事务上下文如GTID、server_id、timestamp。提示这不是“又一种数据库监控工具”。它的价值锚点不在“看”而在“用”——把数据库内部的状态流转变成应用可编程的数据流。你拿到的不是一个日志文件而是一条条带schema、带操作类型INSERT/UPDATE/DELETE/ALTER TABLE、带前后镜像row-based模式下的事件消息。这意味着你可以用标准Go channel做缓冲用context控制超时用zap打结构化日志甚至直接序列化为Protobuf发往Kafka。整条链路完全运行在你的应用进程内没有额外服务依赖部署和调试成本极低。2. 协议握手与连接生命周期为什么90%的连接失败都卡在这三步很多开发者在go run main.go后看到的第一行错误是ERROR 1045 (28000): Access denied for user canal%然后立刻去MySQL里狂加权限。其实真正的拦路虎往往藏在更底层的协议握手阶段。MySQL的复制协议不是简单的TCP连接SQL查询它是一套多轮交互的状态机。我们以go-mysql库为例拆解一次成功连接背后必须走完的硬性步骤2.1 第一步伪装成合法Slave的认证请求MySQL要求所有复制客户端必须提供有效的复制账号并在握手初期声明自身能力如是否支持GTID、是否需要压缩。go-mysql在NewBinlogSyncer时传入的BinlogSyncerConfig结构体关键字段如下cfg : mysql.BinlogSyncerConfig{ ServerID: 1001, // 必须全局唯一同一集群内不能重复否则主库会拒绝 Flavor: mysql, // 指定MySQL方言非MariaDB Host: 192.168.1.100, Port: 3306, User: repl_user, Password: repl_pass, SemiSyncEnabled: false, // 是否启用半同步生产建议false避免阻塞 }ServerID是生死线。我曾在一个K8s环境中部署多个监听实例因未显式配置全部使用默认值1结果主库日志里疯狂刷出[Warning] Slave I/O thread killed while connecting to master。原因很直白MySQL认为这是同一个slave在反复重连直接丢弃后续请求。2.2 第二步获取初始位点Position的精确计算StartSync方法需要指定起始位置常见写法是pos, _ : syncer.GetMasterPos() // 获取当前主库最新位点 // 或指定GTID pos : mysql.Position{GTIDSet: mysql.MariadbGTIDSet{0-1-100}}但这里有个致命陷阱GetMasterPos()返回的是执行该命令瞬间的位点而你真正开始监听时主库可能已写入新event。这就导致“漏数据”。正确做法是结合FLUSH TABLES WITH READ LOCK需SUPER权限生产慎用或更安全的GTID模式。GTID模式下你只需记住上次消费成功的GTID如aaa-bbb-ccc:1-100下次启动时直接从101开始拉取MySQL自动定位无需手动计算文件名和偏移量。2.3 第三步心跳保活与网络中断的优雅恢复binlog流是长连接TCP空闲超时Linux默认7200秒会导致连接静默断开。go-mysql通过HeartbeatPeriod参数主动发送心跳包cfg.HeartbeatPeriod 30 * time.Second // 每30秒发一次心跳但光有心跳不够。当网络抖动发生时库必须能检测到连接失效并自动重连。go-mysql的Syncer结构体提供了Close()和StartSync()组合但重连逻辑必须由你亲手编写。一个健壮的循环示例for { s, err : syncer.StartSync(pos) if err ! nil { log.Error(start sync failed, zap.Error(err)) time.Sleep(5 * time.Second) // 避免密集重试 continue } // 启动event处理goroutine go func() { for { ev, err : s.GetEvent(context.Background()) if err ! nil { log.Warn(get event error, zap.Error(err)) return // 触发外层重连 } handleEvent(ev) // 你的业务逻辑 } }() // 等待event goroutine退出 time.Sleep(1 * time.Second) // 短暂等待确保goroutine已启动 }注意GetEvent是阻塞调用一旦连接断开它会立即返回error。你绝不能把它放在for-select里用default分支做非阻塞轮询——这会导致CPU 100%且错过事件。真正的“优雅”在于让GetEvent自然阻塞靠error退出再由外层循环接管重连。3. Event解析的深层陷阱RowImage、Update事件的双镜像与DDL的不可逆性拿到*replication.BinlogEvent后你以为解析就结束了恰恰相反这才是真正考验细节的地方。binlog格式STATEMENT/ROW/MIXED和binlog_row_image参数设置会彻底改变event内容的丰富度和解读方式。我们以最常见的ROW模式为例深挖三个高频翻车点3.1 RowImage配置决定你能看到什么MySQL 5.6引入binlog_row_image参数控制ROW模式下记录的列数据范围值记录内容典型风险FULL默认UPDATE前后的所有列值日志体积大但最安全MINIMAL仅记录WHERE条件列旧值和被修改列新值若业务代码用UPDATE t SET aa1 WHERE id1旧值a可能为空导致无法还原完整行NOBLOB排除BLOB/TEXT列处理富文本时数据不全go-mysql解析时ev.Header.EventType为UPDATE_ROWS_EVENT_V2其RowsEvent结构体包含Table、Schema、Rows等字段。但关键在于Rows切片的长度和内容Rows[0]是旧值before imageRows[1]是新值after image当binlog_row_imageMINIMAL时Rows[0]可能只有id列Rows[1]只有status列其他字段为nil我曾在一个用户行为分析项目中因DBA将binlog_row_image设为MINIMAL导致监听程序无法还原用户注册时的完整手机号和邮箱最终只能回滚配置并重新初始化位点。3.2 UPDATE事件的“伪删除”与主键变更识别UPDATE在ROW模式下本质是“标记旧行删除 插入新行”。go-mysql将其统一为UPDATE_ROWS_EVENT但你需要自己判断是否发生了主键变更如UPDATE user SET id1001 WHERE id1000。方法是比对Rows[0][0]旧主键和Rows[1][0]新主键if len(ev.Rows) 2 len(ev.Rows[0]) 0 len(ev.Rows[1]) 0 { oldPK : ev.Rows[0][0] newPK : ev.Rows[1][0] if oldPK ! newPK { log.Info(primary key changed, zap.Any(old, oldPK), zap.Any(new, newPK)) // 此时需在下游执行DELETE INSERT而非UPDATE } }忽略这点下游数据库会出现“幻影更新”旧ID行还在新ID行又插入数据不一致。3.3 DDL事件的解析盲区与元数据同步难题CREATE TABLE、ALTER TABLE等DDL语句在binlog中以QUERY_EVENT形式存在ev.Event.(*replication.QueryEvent)的Query字段是原始SQL字符串。问题来了go-mysql不会自动解析SQL语法树。你想知道“新增了哪个字段”或“修改了哪个索引”必须自己做SQL解析。轻量级方案是用正则粗略提取if strings.HasPrefix(ev.Query, ALTER TABLE) strings.Contains(ev.Query, ADD COLUMN) { // 提取ADD COLUMN后的字段定义 re : regexp.MustCompile(ADD COLUMN (\w) (\w)) match : re.FindStringSubmatch(ev.Query) if len(match) 0 { log.Info(detected add column, zap.String(sql, string(match))) } }但这极其脆弱。更可靠的做法是监听库不处理DDL而是将QUERY_EVENT原样转发给一个独立的元数据服务由该服务用sqlparser等库做深度解析并更新下游表结构缓存。我们在某金融系统中采用此方案将DDL处理延迟从平均12秒降至200ms以内。提示永远不要假设RowsEvent的Rows切片长度是固定的。INSERT事件只有Rows[1]新值DELETE事件只有Rows[0]旧值UPDATE才有两个。遍历前务必用len(ev.Rows)校验否则panic。4. 生产级位点管理从内存变量到分布式持久化的演进路径开发阶段你可能这样写var currentPos mysql.Position // 在handleEvent里更新 currentPos ev.Header.LogPos这在单机、短时运行下没问题。但生产环境要求进程崩溃后重启必须从上一次成功处理的event位点继续而非从连接时的位点。这就引出了位点Position管理的核心矛盾它既是应用状态又是数据一致性边界。我们按演进顺序拆解三种位点管理方案的实操细节与取舍逻辑。4.1 方案一本地文件存储适合单机、低QPS场景最简方案是将位点写入本地文件如/data/canal/position.json{ filename: mysql-bin.000005, position: 123456789, gtid_set: aaa-bbb-ccc:1-100 }go-mysql提供syncer.SetBinlogDumpPos(pos)用于设置起始位点。关键在于写入时机必须在event业务逻辑处理成功后、且位点更新前原子化写入文件。Golang标准库无原子写入需用临时文件renamefunc savePosition(pos mysql.Position) error { tmpFile : /data/canal/position.json.tmp data, _ : json.Marshal(pos) ioutil.WriteFile(tmpFile, data, 0644) return os.Rename(tmpFile, /data/canal/position.json) }优点零依赖启动快。缺点文件I/O可能成为瓶颈多实例部署时位点冲突机器宕机可能导致最后几条event重复处理at-least-once语义。4.2 方案二数据库表存储平衡可靠性与复杂度在MySQL中建一张binlog_checkpoint表CREATE TABLE binlog_checkpoint ( id INT PRIMARY KEY DEFAULT 1, filename VARCHAR(64), position BIGINT, gtid_set TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_id (id) );每次处理完一个event执行INSERT INTO binlog_checkpoint (id, filename, position, gtid_set) VALUES (1, mysql-bin.000005, 123456789, aaa-bbb-ccc:1-100) ON DUPLICATE KEY UPDATE filenameVALUES(filename), positionVALUES(position), gtid_setVALUES(gtid_set), updated_atNOW();优势明显利用MySQL事务保证位点更新与业务逻辑的强一致性若业务逻辑在同一个事务中更新业务表则可实现exactly-once天然支持高可用主从切换后从新主库读取位点。但代价是增加主库压力需确保checkpoint表与业务库同实例否则跨库事务难保证。4.3 方案三Redis Stream高吞吐、分布式首选Redis 5.0的Stream数据结构天生适配event sourcing场景。我们将位点作为一条消息写入XADD canal:checkpoint * filename mysql-bin.000005 position 123456789 gtid_set aaa-bbb-ccc:1-100启动时用XREVRANGE canal:checkpoint - COUNT 1获取最新位点。优势在于写入毫秒级无锁竞争天然支持多消费者组Consumer Group不同业务系统可各自维护独立位点Redis Cluster可水平扩展我们实测单节点Redis QPS达8万远超binlog产生速率。唯一注意点是XADD的*生成ID是时间戳序列号需确保Redis服务器时间准确否则位点ID乱序影响XRANGE查询。经验总结位点管理不是“选一个就行”而是要匹配你的SLA。若要求“绝对不丢数据”选方案二DB表若要求“极致性能多实例”选方案三Redis Stream若只是内部工具、允许少量重复方案一文件足够。切忌在方案一上强行加锁做分布式那只会制造更复杂的bug。5. 实战避坑指南从连接超时到事务拆分的12个血泪教训纸上得来终觉浅。我把过去三年在五个不同规模项目中踩过的坑浓缩为12条可直接抄作业的硬核经验。每一条都对应一个真实报错、一次线上故障或一段深夜debug的抓狂时刻。5.1 连接池爆炸别用syncer自带的MaxReconnect自己控流go-mysql的BinlogSyncerConfig有MaxReconnect参数默认0无限重试。当MySQL主库因OOM被K8s OOMKilled时监听程序会在1秒内发起数百次重连瞬间打满连接数。正确姿势禁用自动重连用指数退避手动控制reconnectDelay : 1 * time.Second for i : 0; i 5; i { // 最多重试5次 s, err : syncer.StartSync(pos) if err nil { return s } log.Warn(reconnect attempt failed, zap.Int(attempt, i), zap.Error(err)) time.Sleep(reconnectDelay) reconnectDelay * 2 // 指数增长1s, 2s, 4s, 8s, 16s } return nil5.2 位点漂移LogPos不是event结束位置而是下一个event起始位置这是最反直觉的点。ev.Header.LogPos指向的是当前event之后的下一个event的起始偏移量。因此若你处理完event A后立即将LogPos存为checkpoint那么重启后会从event B开始event A被跳过。修复逻辑位点应存为ev.Header.LogPos - ev.Header.EventSize即当前event的起始位置。但EventSize在header里不直接暴露需用ev.Header.EventSize()方法计算。5.3 GTID间隙主库gtid_purged清理后从库无法追上当MySQL主库执行RESET MASTER或SET GLOBAL gtid_purged后GTID集合被清空。此时监听程序若用旧GTID启动会报Could not find first log file name in binary log index file。根治方案监听程序启动时先调用SHOW MASTER STATUS对比本地GTID与主库Executed_Gtid_Set若发现本地GTID已被purge则强制降级为fileposition模式并从File字段指定的binlog文件开头读取。5.4 大事务阻塞单个binlog event超1GGetEvent卡死MySQL在写入超大事务如全表UPDATE时会将整个事务打包为一个XID_EVENT其LogPos跨度极大。go-mysql的GetEvent会尝试一次性读取整个event导致内存爆满、goroutine挂起。应对策略在BinlogSyncerConfig中设置ReadTimeout和WriteTimeout并在GetEvent外层加context控制ctx, cancel : context.WithTimeout(context.Background(), 30*time.Second) defer cancel() ev, err : s.GetEvent(ctx) // 超时自动返回5.5 字符集错乱utf8mb4字段在event中显示为乱码MySQL binlog协议本身不携带字符集信息。go-mysql默认用utf8解码但若表字段是utf8mb4支持emoji解码会失败。解决方案在BinlogSyncerConfig中显式指定Charsetcfg.Charset utf8mb4同时确保MySQL主库的character_set_server和collation_server也设为utf8mb4_unicode_ci。5.6 时间戳失真ev.Header.Timestamp比系统时间早8小时这是因为MySQL的timestamp类型受time_zone变量影响。go-mysql读取的是MySQL server的time_zone设置值如SYSTEM而非UTC。正确做法永远不要信任ev.Header.Timestamp改用ev.Header.EventTime()方法它会根据MySQL的time_zone自动转换为本地时间若需UTC则用ev.Header.EventTime().UTC()。5.7 表名大小写Linux下MySQL默认小写但go-mysql解析的Schema和Table字段保留原始大小写若应用层用strings.EqualFold比较表名可能因大小写不一致导致路由失败。统一规范在handleEvent开头强制转为小写schema : strings.ToLower(ev.Header.Schema) table : strings.ToLower(ev.Header.Table)5.8 DDL阻塞ALTER TABLE执行期间后续DML event被积压MySQL在执行DDL时会阻塞binlog写入直到DDL完成。go-mysql的GetEvent会一直阻塞。破局点监听程序需具备“DDL感知”能力。当检测到QUERY_EVENT且Query包含ALTER时立即记录当前位点并向监控系统发告警提示“DDL执行中DML延迟升高”。5.9 主从切换VIP漂移后监听程序连到旧主库已变为从库K8s Service或LVS VIP切换有延迟。监听程序可能连上已降级的旧主库而该库binlog已停止写入。防御机制每次GetEvent前先执行SELECT read_only若返回ON说明已变从库立即关闭连接并重连新主库。5.10 内存泄漏未释放RowsEvent引用导致GC无法回收RowsEvent.Rows是[][]interface{}切片若你在handleEvent中将某一行存入全局map而该行包含大text字段内存永不释放。黄金法则所有event处理完后显式置空ev nil // 帮助GC5.11 事务拆分一个XID_EVENT包含数千条DML下游无法批量处理go-mysql将整个事务视为一个event但下游Kafka或ES可能有单条消息大小限制。拆分策略在handleEvent中对XID_EVENT内的RowsEvent进行二次遍历每100条DML打包为一个子事件单独发送。5.12 日志爆炸go-mysql默认开启DEBUG日志单日志文件超10Ggo-mysql的log包默认输出所有协议交互细节。生产必须关闭import github.com/siddontang/go-log/log log.SetLevel(log.ERROR) // 只留ERROR最后分享一个私藏技巧在handleEvent函数开头加一行log.Debug(event received, zap.String(type, ev.Header.EventType.String()), zap.Uint32(log_pos, ev.Header.LogPos))。这行日志看似普通但在排查“为什么某个表没监听到更新”时它是唯一能证明“event确实到达了你的程序”的铁证。没有它你永远在猜网络、猜MySQL、猜权限——有了它问题域瞬间缩小到你的业务逻辑层。

相关新闻

生成高质量技术博客:标题、关键词与摘要素材准备指南

生成高质量技术博客:标题、关键词与摘要素材准备指南

我注意到您提供的输入内容中,【项目标题】、【项目正文】、【关键词】和【摘要描述】均为空,暂时无法开始创作。 请按以下格式补齐输入信息,我会立即为您生成一篇独立、完整、可直接发布的博客文章: 项目标题: (例如…

2026/10/13 19:13:29 阅读更多 →
STM32 3D打印机固件:从寄存器级运动控制到S曲线实时规划

STM32 3D打印机固件:从寄存器级运动控制到S曲线实时规划

简介:这是一份面向嵌入式开发者与3D打印技术爱好者的STM32平台开源固件资源,聚焦于解决3D打印机底层运动控制、G代码实时解析与脱机打印等核心问题,适用于希望深入理解固件架构、定制打印逻辑或开展硬件协同开发的学习者与工程师。压缩包共19…

2026/10/13 19:13:29 阅读更多 →
单片机硬件电路设计实战:从最小系统到PCB布局的避坑指南

单片机硬件电路设计实战:从最小系统到PCB布局的避坑指南

1. 从一块“点不亮”的板子说起:单片机硬件电路设计的核心逻辑搞单片机开发的人,几乎都经历过这样一个场景:代码在仿真器里跑得飞起,逻辑时序看着都对,可一旦把程序烧进真实的PCB板子,要么上电没反应&#…

2026/10/13 19:13:29 阅读更多 →

最新新闻

Steinberg VST插件开发包入门:从零构建VST3音频插件

Steinberg VST插件开发包入门:从零构建VST3音频插件

简介:Steinberg VST插件开发包(VST3 SDK 3.5版)面向音频插件开发者、音乐软件工程师及数字音频工作站扩展爱好者,提供创建虚拟乐器与音频效果处理器所需的完整开发环境。包内共5764个文件,以html文档、png界面素材、h头…

2026/10/13 20:08:06 阅读更多 →
STM32驱动舵机:PWM时基、定时器映射与舵机协议三重校准

STM32驱动舵机:PWM时基、定时器映射与舵机协议三重校准

简介:本资源是一套基于STM32F10x系列的伺服电机数字控制完整工程源码,面向嵌入式初学者、自动化控制课程实践者及机器人/精密设备开发工程师,解决单片机驱动高精度执行机构的核心问题——即通过PWM生成、PID闭环调节与硬件外设协同实现伺服电…

2026/10/13 20:08:06 阅读更多 →
RAG项目自建AI基础设施的陷阱与务实落地路线图

RAG项目自建AI基础设施的陷阱与务实落地路线图

1. 被“自建平台”绑架的RAG项目过去一年多,我参与过好几个企业级RAG(检索增强生成)项目的评审和落地咨询。一个非常普遍的现象是:项目刚立项,技术团队的第一反应往往是“我们要自建一套AI基础设施平台”。理由听起来也…

2026/10/13 20:08:06 阅读更多 →
知识工作者的数字外挂:插件选型与实践复盘

知识工作者的数字外挂:插件选型与实践复盘

知识工作者的数字外挂:一套插件的选型思路与实践复盘这两年,我越来越觉得“知识工作”这个词被低估了。很多人以为写文档、做分析、查资料就是知识工作,但真正每天泡在这些流程里的人才会懂——最耗时间的从来不是思考本身,而是思…

2026/10/13 20:08:06 阅读更多 →
LiDAR360点云数据处理全流程:从格式转换到工程落地

LiDAR360点云数据处理全流程:从格式转换到工程落地

简介:《LiDAR360激光雷达点云数据处理软件用户手册》是一份面向测绘、林业、电力等行业从业者的官方技术文档,系统介绍 LiDAR360 V2.2 的点云数据整理、分析与成果输出方法。手册围绕航带拼接、数据管理、统计、分类、地形、机载林业、地基林业、电力线及…

2026/10/13 20:08:06 阅读更多 →
二维正态分布中ρ为何等于相关系数?数学证明与工程意义

二维正态分布中ρ为何等于相关系数?数学证明与工程意义

1. 这个证明到底在解决什么问题?——从直觉困惑到数学确认你翻开任何一本概率论教材,看到二维正态分布的密度函数时,大概率会立刻注意到那个希腊字母ρ(rho)——它被明确写在指数项里,和两个变量的均值、标…

2026/10/13 20:07:05 阅读更多 →

日新闻

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

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

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

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 阅读更多 →