用Java手写编译器:词法分析、语法分析与AST设计全解析
简介Java编译器实现是理解JVM生态与开发工具链的重要切入点。这份资源包以精简的代码示例和说明文档面向对编译原理感兴趣、想了解IDE如何集成编辑与编译功能的开发者也适合准备自制小型编译器的学生或工程师。包内包含Java源码与Markdown说明文档两个文件压缩包仅2KB却点出了词法分析、语法分析、语义分析、中间代码生成等关键阶段并配合说明文档梳理了从源码文本到字节码的整体流程。目前已有59人学习浏览读者可借助说明文件快速掌握编译器模块的职责划分、错误检测与报告思路并在此基础上扩展词法或语法规则尝试生成更高效的字节码进而为深入研读javac源码或参与编译工具开发打下基础。1. 拿到Java编译器的实现源码先定目标再拆模块做编译原理课程设计时最怕的是拿到一份Java编译器的实现源码却没有入手点一头扎进代码就开跑结果连入口在哪都找不到。这份资源把编译器从源码到可执行单元的全链路串起来了词法分析、语法分析、符号表与作用域、中间表示、目标代码生成都以模块化Java源码呈现。适合三类人准备编译原理课程答辩的学生、想复现经典编译流程的开发者、给现有编译器加语法特性的进阶玩家。文章按一条主线走——先看目录与模块边界再亲手跑通词法分析和语法分析接着处理符号表和语义最后落在代码生成与验证上。2. 词法分析器怎么读Token定义与有限状态机的落地词法分析是整个编译器里最容易先跑通、也最容易埋雷的模块。它负责把源码字符串切成Token流后续Parser全部工作在Token流上所以Token类型划分和扫描状态机的质量直接决定后面能走多远。2.1 先从目录结构看编译器分层测试用例就是最好的文档拿到一个Java编译器实现的zip包别先急着找main方法先把目录结构过一遍。典型的实现会按职责拆成词法、语法、语义、生成几个包目的大概是这样目录职责典型入口lexer/源码字符串转Token流识别关键字、操作符、字面量Lexer.javaparser/Token流转AST递归下降解析表达式和语句Parser.javaast/AST节点定义与Visitor接口Expr.java, Stmt.javasemantic/符号表、作用域链、类型检查SymbolTable.javacodegen/目标代码生成或AST解释器实现CodeGenerator.javabuild.gradle构建脚本依赖与编译入口无我拆这种项目有个习惯先打开测试目录。一个合格的编译器实现里单元测试会覆盖表达式优先级、变量遮蔽、字符串转义、错误恢复等典型场景。测试用例是最直观的文档比任何README都可靠。如果你拿到的资源里只有源码没有测试建议动手补一个——后面第5章会讲最小测试集怎么设计。提示如果压缩包里只有一个巨大的单文件先按上述目录拆开再读否则状态机、AST、符号表混在一起翻车概率翻倍。2.2 TokenType与Token数据类先给词法单元分类再谈识别词法分析的核心产物是Token流而TokenType枚举决定了这个编译器能识别哪些基本语言元素。一个覆盖教学编译器常用特征的TokenType大致长这样public enum TokenType { // 字面量与标识符 IDENTIFIER, INT_LITERAL, STRING_LITERAL, // 关键字单独用KEYWORD标记 KEYWORD, // 单字符运算符 PLUS, MINUS, STAR, SLASH, LPAREN, RPAREN, LBRACE, RBRACE, SEMICOLON, // 双字符运算符 EQ, EQEQ, BANG, BANGEQ, LT, GT, LE, GE, // 文件结束标记 EOF }这里的选型理由是关键字不单独建IF、ELSE、WHILE等枚举常量而是先把字母序列识别为IDENTIFIER再查关键字表判断它是不是关键字最后把Token的type改成KEYWORD。这么做的好处是新增关键字不用改枚举词法扫描器的主流程也不用动。坏处是Parser在匹配关键字时要比较token.lexeme而不是枚举常量所以要保证lexeme存的是原始源码文本。Token数据类要携带位置信息否则语义分析阶段报错时没法定位到行列public class Token { public final TokenType type; public final String lexeme; public final int line; public final int column; public Token(TokenType type, String lexeme, int line, int column) { this.type type; this.lexeme lexeme; this.line line; this.column column; } Override public String toString() { return type ( lexeme ) line : column; } }lexeme保存的是源码里的原始字符串比如就存不要在这里做任何语义转换line从1开始column从1开始初始化时每读一个字符column换行时line并重置column为1。这个设计直接决定后面报错信息是否可读。2.3 手写扫描状态机读字符、判类别、回退一步词法扫描器最常见的实现是用PushbackReader做一个带回退的单字符流。核心的nextToken方法按当前字符类别分流public Token nextToken() throws IOException { // 跳过空白、换行、块注释、行注释 skipWhitespaceAndComments(); int c reader.read(); if (c -1) { return new Token(TokenType.EOF, , line, column); } char ch (char) c; if (isDigit(ch)) { return readNumber(ch); } if (isAlpha(ch)) { return readIdentifier(ch); } if (ch ) { return readStringLiteral(); } return readOperator(ch); }逻辑说明每次先跳过不可见字符和注释再读一个有效字符。数字走readNumber字母和下划线走readIdentifier双引号走字符串其余走运算符。注意不要用Character.isLetterOrDigit直接判断它在某些locale下会把中文也判定为字母导致标识符规则不可控。readNumber要一次吞够整个数字字面量private Token readNumber(char first) throws IOException { StringBuilder sb new StringBuilder(); sb.append(first); while (isDigit(peek())) { sb.append((char) reader.read()); } // 支持带小数点的浮点字面量 if (peek() .) { sb.append((char) reader.read()); while (isDigit(peek())) { sb.append((char) reader.read()); } return new Token(TokenType.DOUBLE_LITERAL, sb.toString(), line, column); } return new Token(TokenType.INT_LITERAL, sb.toString(), line, column); }参数说明peek()是PushbackReader的常见封装返回下一个字符但并不消费相当于往前看了一眼。之所以要peek而不是直接read是因为遇到123.这种输入时如果小数点后面不是数字就得把小数点回退给下一轮处理。最大吞读原则在这里的体现是在当前字符能构成合法Token的前提下吞得越长越优先但吞到临界点该停就停。字符串字面量的扫描最容易翻车见下面的实现private Token readStringLiteral() throws IOException { StringBuilder sb new StringBuilder(); while (true) { int c reader.read(); if (c -1 || c \n) { throw new LexException(未终止的字符串字面量, line, column); } char ch (char) c; if (ch \\) { int next reader.read(); if (next -1) { throw new LexException(字符串内不能以反斜杠结尾, line, column); } // 把反斜杠和转义字符原样保存 sb.append(ch).append((char) next); continue; } if (ch ) { return new Token(TokenType.STRING_LITERAL, sb.toString(), line, column); } sb.append(ch); } }这里的关键选择是反斜杠只触发对下一个字符的消费但转义序列原样并入lexeme不在这里解释成真正的换行或制表符。原因很简单——词法阶段不知道当前上下文是赋值给字符串还是作为方法参数转义展开是语义分析阶段的事。如果你在词法器里就把\n变成真正的换行符后面代码生成阶段就丢失了“源码里到底写得是什么”这个信息调试时会非常痛苦。3. Parser与SymbolTable递归下降语法分析和作用域链的具体实现语法分析阶段的任务是把Token流变成AST。手工编写的递归下降Parser是最适合教学和课程设计的方案代码直观、优先级靠嵌套函数表达、错误定位准确。相比用ANTLR或JavaCC这类生成器手写Parser虽然样板代码多但每一步逻辑都是自己可控的。3.1 表达式优先级的实现靠递归层级而不是老师傅经验递归下降的核心技巧是每一层函数只处理自己这层运算符优先级越低的运算符越在外层。一个实用的层级顺序从低到高是assignment → equality → comparison → term → factor → unary → primary。private Expr expression() { return assignment(); } private Expr assignment() { Expr expr equality(); if (match(TokenType.EQ)) { Token equals previous(); Expr value assignment(); return new AssignExpr(equals, expr, value); } return expr; }逻辑说明assignment是表达式层级的最高层所以它能接收整个等式右边的值。match(TokenType.EQ)检查当前Token是否是赋值号是就继续递归调用assignment()解析右操作数。这里的右递归是有意的为了支持a b 3这样的链式赋值。加减和乘除分别处理private Expr term() { Expr expr factor(); while (match(TokenType.PLUS, TokenType.MINUS)) { Token op previous(); Expr right factor(); expr new BinaryExpr(expr, op, right); } return expr; } private Expr factor() { Expr expr unary(); while (match(TokenType.STAR, TokenType.SLASH)) { Token op previous(); Expr right unary(); expr new BinaryExpr(expr, op, right); } return expr; }这里的while循环是左结合的唯一正确解法。如果这里不用循环而用递归1 - 2 - 3会被解析成1 - (2 - 3)得到2而不是-4。左递归在递归下降Parser里无法直接处理但可以改写成迭代循环每次看到同层运算符就继续消费右操作数并叠加到左树上。参数说明match(TokenType.PLUS, TokenType.MINUS)是变参方法它先检查current位置的Token类型是否匹配参数列表中的任意一个匹配则推进指针并返回trueprevious()返回上一个Token也就是本轮消费的运算符。这套玩法是所有递归下降Parser共通的模式。3.2 AST节点体系表达式和语句分开设计方便写VisitorAST节点分成两大类表达式有值语句没有值但会改变程序状态或控制流。把两者分开定义语义分析和代码生成的Visitor就不需要混在一起处理。public abstract class Expr { public abstract T T accept(ExprVisitorT visitor); } public abstract class Stmt { public abstract T T accept(StmtVisitorT visitor); }public class BinaryExpr extends Expr { public final Expr left; public final Token op; public final Expr right; public BinaryExpr(Expr left, Token op, Expr right) { this.left left; this.op op; this.right right; } Override public T T accept(ExprVisitorT visitor) { return visitor.visitBinaryExpr(this); } } public class VarDeclStmt extends Stmt { public final Token type; public final Token name; public final Expr initializer; public VarDeclStmt(Token type, Token name, Expr initializer) { this.type type; this.name name; this.initializer initializer; } Override public T T accept(StmtVisitorT visitor) { return visitor.visitVarDeclStmt(this); } }这里的选择理由是通过Visitor模式把“遍历AST”和“对AST做什么”解耦。语义分析器实现一套ExprVisitor和StmtVisitor代码生成器再实现一套两套逻辑互不干扰。如果你拿到的资源里只有AST节点没有Visitor接口建议自己补上后面写代码生成和AST打印调试都会省很多事。调试AST最实用的技巧是写一个toString递归方法打印树结构。我一般会在Expr和Stmt里加一个void print(int indent)方法输出类似BinaryExpr(, Literal(1), BinaryExpr(*, Literal(2), Literal(3)))的文本语义分析跑不通时先看AST长得对不对排除Parser的问题再往下查。3.3 符号表的作用域链define写当前层resolve逐层向上找语义分析阶段最重要的数据结构就是符号表。一个只装MapString, VarSymbol的表处理不了嵌套作用域必须设计成链式结构每个作用域指向它外层的作用域形成一条链。public class SymbolTable { private final SymbolTable parent; private final MapString, VarSymbol symbols new HashMap(); public SymbolTable(SymbolTable parent) { this.parent parent; } public void define(String name, VarSymbol symbol) { symbols.put(name, symbol); } public VarSymbol resolve(String name) { VarSymbol symbol symbols.get(name); if (symbol ! null) { return symbol; } if (parent ! null) { return parent.resolve(name); } return null; } }逻辑说明define永远只在当前作用域写入不碰外层。resolve先查当前层找不到就沿parent链逐层向上查询查到最外层仍没有则返回null。这个设计的精妙之处在于同名遮蔽自动生效——内层函数参数或局部变量与外层变量同名时resolve返回内层定义当Parser退出内层块作用域时只需要把当前符号表丢弃外层表仍然保留遮蔽关系自然解除。我在实际使用中会在define时做一次重复声明检查如果当前层已经存在同名符号直接报告“变量重定义”这是避免变量遮蔽逻辑变得混乱最简单的防线。另一个容易被忽略的点是函数体、循环体、普通代码块都必须新建子符号表哪怕这个块里没有声明任何变量也要建。否则后续在这个块里解析到变量时作用域链就会漏掉该有的嵌套关系。4. 编译器实现避坑5条高频故障与排查路径这个章节是血泪经验汇总。以下五条故障我几乎在每一个编译器实现项目里都会遇到按踩中频率排序每条都给出排查路径和修复方案。4.1 字符串字面量里的转义被提前消费现象输入源码a\\nb词法器输出字符串文本变成anb反斜杠和转义字符n被合并成了普通字符。原因readStringLiteral在词法阶段遇到反斜杠就直接跳过了反斜杠本身把下一个字符n当作普通字符追加导致\n变成n。解决词法阶段必须把反斜杠和下一个字符一起原样存入lexeme。正确做法见2.3节的实现——遇到\时先读下一个字符把两者拼接后存入StringBuilder等到语义分析或代码生成阶段再做真正的转义展开。4.2 表达式优先级翻车1 2 * 3算出9现象输入1 2 * 3语法分析后AST是(1 2) * 3解释器求值结果是9而不是7。原因Parser里只有一个parseExpression函数没有按优先级分层。所有运算符都在同一层处理乘法和加法没有区分先后。解决按优先级拆成assignment → equality → comparison → term → factor至少五层。term负责加减、factor负责乘除factor内的操作数由unary提供。修完这层后如果出现1 - 2 - 3变成1 - (2 - 3)说明运算符用了右递归而没用while循环继续检查3.1节的写法。4.3 注释识别和字符串识别互相污染现象源码里写String url http://example.com;词法器把//当成注释开头整个字符串后面内容全部被吃掉。原因skipWhitespaceAndComments在检测//时没有判断当前是否处于字符串扫描状态。词法器把所有场景都当成普通代码区处理。解决注释扫描和字符串扫描不能共用同一个状态。一种干净的做法是nextToken主流程里遇到/时先peek下一个字符只有当下一个字符也是/时才走注释逻辑否则直接按除号运算符处理字符串内的//由readStringLiteral单独消费不会回流到注释识别逻辑。4.4 变量遮蔽失效循环体修改了外层同名变量现象函数开头声明int i 0;循环体内再声明int i 100;循环结束后外层i的值变成了100遮蔽没有生效。原因解析循环体时没有创建新的SymbolTable整个函数体只用一个符号表。内层define直接覆盖了外层同名变量。解决语法解析器遇到左大括号进入块语句时创建一个new SymbolTable(currentTable)块内所有声明都写进新表遇到右大括号时把currentTable还原为parent。排查时在语义分析器里打印每次define前后的符号表深度很容易定位是哪个块没有建新作用域。4.5 报错定位偏移所有错误都指向第0行第1列现象语义错误信息永远显示0:1或者首行代码报错在对不上的行列位置。原因行号从0开始计数或者读取源码文件时没有跳过UTF-8 BOM头导致第一个Token的column计算错位。解决统一约定行号和列号都从1开始词法器里换行时line并重置column为1读文件时用Reader包装并显式指定UTF-8遇到BOM头手动跳过。排查编译器的所有报错路径确保错误信息统一从Token的line和column字段取值不自己另外算位置。注意如果你在改的编译器是用Java 8以下版本写的Token类不要用Java 16的record语法按2.2节的普通类写否则CI构建阶段就过不了。5. 验证编译器最快路径AST解释器加四类最小测试在动手写代码生成器或字节码发射器之前先花一小时写一个AST解释器。解释器实现起来比代码生成简单得多它直接把AST节点求值成结果不需要考虑寄存器分配、栈帧布局或指令编码。它的价值在于验证每个语法特性的语义是否正确时解释器是语义的参考实现——如果解释器计算结果都不对生成代码肯定也是错的如果解释器对了而生成代码不对问题一定出在代码生成器而不是语义分析。public class EvalExprVisitor implements ExprVisitorInteger { private final SymbolTable symbols; public EvalExprVisitor(SymbolTable symbols) { this.symbols symbols; } Override public Integer visitLiteralExpr(LiteralExpr expr) { return Integer.parseInt(expr.value); } Override public Integer visitBinaryExpr(BinaryExpr expr) { int left expr.left.accept(this); int right expr.right.accept(this); switch (expr.op.type) { case PLUS: return left right; case MINUS: return left - right; case STAR: return left * right; case SLASH: return left / right; default: throw new RuntimeException(不支持的二元运算符: expr.op.lexeme); } } Override public Integer visitVarExpr(VarExpr expr) { VarSymbol symbol symbols.resolve(expr.name.lexeme); if (symbol null) { throw new RuntimeException(未定义的变量: expr.name.lexeme); } return (Integer) symbol.value; } }参数说明visitLiteralExpr处理数字字面量visitBinaryExpr按运算符分别求值visitVarExpr通过符号表resolve找出变量当前值。这里把符号表塞进Visitor里作为状态持有者语义分析和代码生成阶段都沿用同一个SymbolTable实例保证变量解析路径一致。配套的最小测试集我固定为四类每一类都对应一种最常见故障测试类别典型输入能暴露的问题常量表达式1 2 * 3;优先级、二元运算符求值变量声明与引用int a 5; int b a 1;符号表写入与查找嵌套块作用域int x 1; { int x 2; } return x;遮蔽与作用域出栈错误输入(1 2;或abc错误报错位置与恢复跑通这四类用例后再开始写代码生成器不管是生成JVM字节码还是生成Java源码都先用这四类用例做回归。从那以后我每次拆编译器源码都强制先用这个最小测试集过一遍再动手改词法器。遇到玄学问题先怀疑状态机没复位其次怀疑符号表作用域没有正确出栈——这两个位置占了九成故障。这份资源把编译器的各个模块都摆在明处照着这个路径拆一遍比自己从零敲一个Lexer加Parser省太多时间希望帮到你。本文还有配套的精品资源点击获取

相关新闻

为AI应用构建可扩展、智能体友好的API:从设计到落地

为AI应用构建可扩展、智能体友好的API:从设计到落地

1. 从单体接口到智能体协作:为什么传统API撑不住AI应用了 过去两年我经手过不少AI应用的后端改造项目,从智能客服到自动化数据分析流水线,几乎每一个都踩过同一个坑: 用传统RESTful接口去对接大模型和智能体,前期跑得…

2026/10/12 13:54:04 阅读更多 →
C语言变量、常量与指针深度解析:理解内存底层逻辑与编程实践

C语言变量、常量与指针深度解析:理解内存底层逻辑与编程实践

看到这个标题,估计不少刚入门的朋友会觉得“这不就是基础语法嘛”,也有工作几年的老哥们心里犯嘀咕:变量、常量谁不会?但指针这东西,真的是C语言里绕不过去的坎,也是面试时最喜欢深挖的点。我见过太多人能把…

2026/10/12 13:54:04 阅读更多 →
js-filesearch:一个纯Node.js命令行文件搜索与内容检索工具

js-filesearch:一个纯Node.js命令行文件搜索与内容检索工具

简介:这是一份基于Node.js(JavaScript)的文件夹文件搜索工具项目,主要面向需要快速定位文件的开发者,也适合初学者借助源码学习Node.js文件操作与异步编程。项目采用模块化结构,包含搜索、用户界面、文件系…

2026/10/12 13:54:04 阅读更多 →

最新新闻

二叉树进阶攻略:递归、迭代、层序与经典题型全解析

二叉树进阶攻略:递归、迭代、层序与经典题型全解析

算法训练营第十四天:二叉树(2) 到了第十三天,我们基本上把二叉树的"骨架"摸清了:节点结构、树的定义、递归遍历的套路。那十四天干嘛?两个字:深化。二叉树(2)…

2026/10/12 14:48:18 阅读更多 →
卫星通信入网三关:物理连通、协议接入与业务可用性验证

卫星通信入网三关:物理连通、协议接入与业务可用性验证

简介:本资源是一份面向通信工程专业学生、卫星地面站建设工程师及网络测试技术人员的实务型教学课件,系统讲解卫星通信网络从建站、入网验证到系统测试的全流程规范与关键技术。内容覆盖新地球站入网申请审批(国内经原邮电部电信总局、国际由…

2026/10/12 14:48:18 阅读更多 →
Agent记忆系统实战:上下文窗口限制与MCP统一工具记忆

Agent记忆系统实战:上下文窗口限制与MCP统一工具记忆

1. 先理解Agent为什么“记不住”:上下文窗口的边界与失真1.1 上下文窗口是工作台,不是仓库我最早做Agent原型时犯过一个很天真的错误:以为模型能记住对话里出现过的所有信息。后来被现实狠狠教育了一课——上下文窗口更像一张有限大小的办公桌…

2026/10/12 14:48:18 阅读更多 →
Keras复现YOLOv1:从零训练自己的数据集实战指南

Keras复现YOLOv1:从零训练自己的数据集实战指南

简介:这份资源面向希望上手目标检测的深度学习研究者与开发者,围绕Keras版YOLOv1算法,提供训练自有数据集的完整配套材料。YOLOv1将检测视为回归问题,速度优势明显,而Keras高层API降低了实现门槛,二者结合便…

2026/10/12 14:48:18 阅读更多 →
MATLAB读取RINEX观测文件:从解析到定位解算的完整指南

MATLAB读取RINEX观测文件:从解析到定位解算的完整指南

简介:这份资源面向从事GNSS数据处理、测绘与导航定位方向的科研人员和工程技术人员,提供了一套用MATLAB读取RINEX格式GPS观测文件的程序,并附带测试数据用于验证解析结果。RINEX作为接收机独立交换格式,广泛用于GPS、GLONASS、Gal…

2026/10/12 14:48:18 阅读更多 →
WorkBuddy低代码工作流实战:从单点任务到业务闭环

WorkBuddy低代码工作流实战:从单点任务到业务闭环

1. 这不是一场普通征稿:WorkBuddy 应用指南征集背后的“真实战场”你点开这个标题,第一反应可能是——又一个企业发的软广?领积分、换周边?听起来像行政部发的季度团建通知。但如果你真这么想,就错过了一个正在 quietl…

2026/10/12 14:47:16 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/12 0:16:43 阅读更多 →

月新闻

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