打开网易新闻 查看精彩图片

一个写了20年代码的老兵,把模板引擎倒过来用,让爬虫代码从80行缩到8行。

这不是什么新框架发布,是一个新西兰程序员在GitHub上默默维护了三年的工具。上周突然在Hacker News冲到首页,评论区里做数据抓取的工程师集体倒苦水——原来大家被CSS选择器折磨了这么多年,却没人想过换个思路。

从航空公司的CSV地狱开始

从航空公司的CSV地狱开始

2012年,作者还在给加拿大SITA公司做远程外包。航空业的票价数据以CSV形式涌来,几百个文件,每个布局略有不同,来源系统各异,最终要塞进同一张数据库表。

他们写了很多Java解析代码。不是复杂业务逻辑,就是结构化文本的形状识别。这件事的荒谬之处在于:人类看一眼就知道"这一列是价格,那一列是日期",但代码却要逐行字符匹配

后来他在新西兰最大的新闻平台Stuff工作,处理结构化内容的规模更大。银行和健康医疗领域的经历更离谱——有些系统比REST API还古老,2003年的内部工具把结构化文本打印到stdout,因为那就是当时的接口,二十年没人敢动。

这些问题有个共同内核:文本明显有规律,提取数据的工具却笨得要死。

CSS选择器是当代程序员的体力劳动

几年前的副业项目让他彻底爆发。目标网页明显是模板生成的,结构一致,只有数据不同。他打开DevTools,开始写那种每个爬虫工程师都熟悉的代码:

const name = $(".product-title h1").text().trim();

const price = $(".price-box .current-price").text().replace("$", "").trim();

const brand = $(".specs-table tr:nth-child(2) td:last-child").text().trim();

两个字段还能忍。但页面有嵌套列表,每个条目带子字段时,代码就开始变异:循环、索引访问、到处trim,最终产物和想要的数据结构毫无相似之处。

更隐蔽的伤是脆弱性。站点改个类名、挪个div,选择器默默返回空字符串。管道开始产出垃圾数据时,你才发现问题。这不是编程,是考古——用镊子从HTML废墟里夹东西

Puppeteer能渲染页面,但对提取数据毫无帮助。为了读一段纯文本,你要启动完整的无头Chromium,承受启动时间、内存占用、waitForSelector的飘忽不定。企业环境更惨:安全策略、沙箱构建代理、锁死的CI管道,经常让无头浏览器直接不可行。

作者在这些环境里待过。零依赖的纯文本方案不只是"更好",有时是"唯一可行"。

模板引擎倒过来长什么样

模板引擎倒过来长什么样

关键洞察来自一个类比:网页是模板引擎的输出。如果正向是"模板+数据=HTML",逆向不就是"HTML+模板=数据"?

模板引擎的工作方式是替换占位符。Handlebars里写{{name}},运行时注入数据。那如果给引擎一段真实HTML,让它反向匹配模板结构,不就能自动提取对应位置的值?

这个方向没人做。作者查了现有方案,全是正则、XPath、CSS选择器的老路。于是他开了个坑,名字很直白:reverse-template-engine。

核心设计出人意料地简单。模板用类似Handlebars的语法标记动态部分:

{{title}}

${{amount}}

输入真实HTML时,引擎做结构匹配而非字符匹配。它不关心具体文本内容,只关心骨架是否一致。匹配成功后,动态部分自动提取为键值对。

嵌套列表的处理是亮点。模板支持{{#each items}}循环,引擎能识别重复结构,自动收集数组。之前80行的cheerio代码,现在8行模板定义搞定。

作者的原话是:"我终于在写我想要的数据结构,而不是HTML的解剖笔记"

企业场景里的隐藏优势

企业场景里的隐藏优势

纯文本方案在监管环境里意外吃香。无头浏览器需要下载Chromium,经常触发安全扫描的警报。纯Node.js实现、零原生依赖的包,过审容易得多。

性能数据也偏袒这个方案。测试集里1000个产品页面,cheerio+puppeteer组合平均耗时4.2秒,内存峰值380MB。反向模板引擎0.3秒,内存12MB。差距不是优化能解释的,是架构本质不同:一个模拟浏览器,一个做字符串匹配。

容错设计是另一个被低估的点。CSS选择器失败时返回空字符串,你的代码继续跑,下游消费垃圾数据。模板引擎匹配失败时直接抛异常,位置精确到行。数据管道的失败模式从"静默污染"变成"立即告警",运维友好得多。

作者提到一个真实案例:某次目标站点改版,把

改成。旧选择器$(".price")仍然能命中,但提取逻辑错了,管道产出负数价格。模板引擎的匹配严格得多,结构偏差超过阈值就拒绝,避免了这类事故。

社区反应与未解难题

社区反应与未解难题

Hacker News的热帖里,最高赞评论来自一个做了八年爬虫的工程师:「我上周刚写了200行选择器代码,现在觉得自己像在用石头砸坚果的原始人」。

质疑声也有。有人指出模板引擎需要预先知道页面结构,而CSS选择器更"灵活"。作者回应很直接:灵活是幻觉。你不知道结构时,选择器也在瞎猜;你知道结构时,模板比选择器表达得更精确

另一个批评是维护成本。站点改版时,模板和选择器都要更新。但模板的更新是声明式的,改几行语法;选择器的更新是过程式的,要重新理解DOM操作链。作者提供了可视化diff工具,对比新旧页面的结构偏差,辅助模板迭代。

项目目前Star数刚过3000,作者还在全职工作,维护节奏是"周末+假期"。文档里有段自嘲:「不要问我什么时候支持Vue的模板语法,我的Vue知识停留在2019年」。

最有趣的反馈来自一位前同事。他们在Stuff共事时处理过同样的新闻内容管道,对方私信说:「如果2018年有这个,我们那套基于XPath的提取层能少写一半代码」。作者回复:「如果2018年我有这个觉悟,我们现在可能还在Stuff写它」。

工具的价值有时候不在技术本身,而在它重新定义了"简单问题"的边界。当整个行业习惯了用复杂方案解决简单问题,逆向思考本身就是稀缺能力。

这个项目的GitHub仓库描述只有一句话:"Extract structured data from templated text"。没有"革命性",没有"下一代",连"轻量级"都没写。但过去三周,它的issue区涌进了47个真实业务场景的求助帖——从电商价格监控到政府公开数据抓取,从日志解析到遗留系统接口适配。

作者上周合并了一个PR,支持从Chrome DevTools直接导出HTML结构生成模板草案。他在这条commit下的评论是:「现在你可以右键复制元素,粘贴进CLI,三分钟后开始提取数据」。没有感叹号。

如果模板引擎的正向流程塑造了现代Web的开发体验,它的逆向版本会不会重塑数据提取的实践?或者更直接点——你最后一次写爬虫时,有多少时间花在"理解页面结构"上,又有多少时间花在"和CSS选择器搏斗"上?