我最近熟悉一个陌生网站时,最开始以为任务很简单:把网站的页面、功能和接口摸清楚,再整理进现有知识库。下游其实已经相当顺手了,知识一旦入库,就能查询、问答,也能通过记分卡判断哪些能力已经验证。
真正让我停下来的,是上游那一段。知识库能快速回答问题,不代表网站已经被真正熟悉。页面、控件、接口和能力证据,仍然要靠临时脚本一点点拼起来。换一个网站,我又要重新猜入口、重写解析器、手工收集页面,再想办法把结果喂给下游。
这次工作让我看到一个容易被忽略的事实:自动化能力的断点,往往不在最后的查询,而在第一次把陌生对象变成结构化知识。
查询能用,不等于接入完成
当时我已经能从知识库里查到页面和控件,甚至可以得到一组看起来完整的能力信息。要是只看查询结果,很容易宣布工作完成。毕竟,问一个问题有答案,页面也有记录,表面上的闭环已经形成了。
可是回头看整个过程,很多内容并不是由通用流程产出的,而是为了这一个网站临时写出来的。脚本把压缩后的前端代码拆开,找路由、接口、字典和控件;另一些脚本再把这些结果转成页面资料。它们确实解决了当下的问题,却没有成为下一次可以直接调用的入口。
如果不处理这层断点,后果不是“下次多花一点时间”这么简单。每接入一个新站,都可能重复几十次搜索和试错;临时脚本的命名、输出格式和证据位置稍有不同,后面的知识库就会出现漏项。更危险的是,最后仍然可能得到一个漂亮的覆盖率数字,却说不清这个数字究竟来自真实页面,还是来自前端代码里的推断。
所以我没有把“查询有结果”当成完成标准。我选择先追溯知识是怎样产生的,是因为只有把上游的证据链补齐,下游的稳定才有意义。
SPA 不能只靠手工点页面
陌生网站如果是 SPA,浏览器第一次拿到的 HTML 往往只是一个壳。真正的路由、接口地址、选项字典、计费字段和控件关系,藏在后续加载的前端 bundle 里。只靠手工点页面,能看到一部分能打开的界面,却很难知道还有哪些入口没有被点到。
这次我先做了一层静态测绘:把 bundle 当作待分析的材料,提取路由、接口面、字典和控件树,再将结果落成本地缓存。这个步骤的价值,不是声称“代码里出现过的东西都已经可用”,而是先建立一张候选地图。它告诉我应该去哪里验证,也告诉我哪些能力只是被前端声明过。
静态分析尤其适合发现那些不适合靠鼠标寻找的内容。一个页面可能有很多动态分支,某些选项只在特定状态出现,某些接口只在登录后才会被调用。手工浏览能证明“我看到了什么”,静态分析能帮助回答“还可能有什么”。两者互相补位,不能互相冒充。
这里最容易误判的是,把静态推导当成真实能力。前端代码里有一个接口,不等于当前账号能调用;控件树里有一个按钮,也不等于页面此刻能正常展开。静态层应该输出候选值,并明确标记来源,不能直接替 live 证据盖章。
先分层,再谈覆盖率
我后来把陌生站拆成4层。第1层是静态层,负责从前端材料中推导路由、接口、字典、扣费字段和控件候选。第2层是缓存层,保存原始材料和中间结果,让后续复核不必每次重新下载、重新解析。第3层是只读 live 层,验证接口是否真实响应、登录态页面有哪些控件、滚动和分页是否确实存在。第4层是证据层,把前3层的结果按能力归档,并记录每项结论来自哪里。
这样的分层看起来比“跑一个脚本”麻烦,实际上更省时间。每层都有自己的输出,失败时可以知道是材料没拿到、解析失败、接口不可用,还是页面验证没有完成。最重要的是,证据不会因为中间某一步不稳定而被悄悄混在一起。
覆盖率也要随之拆开。比如一个控件可以有“静态推导”“缓存保留”“页面实测”3种状态。静态推导说明它可能存在,页面实测才说明它在当前条件下出现过。若把两者相加后只报一个100%,就会把推测包装成事实。
覆盖率不是越高越可靠,只有来源被分开,数字才有解释力。
我还会给每个能力附上证据来源,而不是只保存一个名称。因为名称容易改,证据位置却决定了别人能不能复查。一次实际工作中,能力名称只差一个字,证据就没有匹配上,结果不是报错,而是静默丢失。这种问题比明显失败更难发现,正是因为它看起来一切正常。
一键入口解决的是记忆依赖
分层之后,下一步不是再写一份更长的说明,而是把已有步骤串成一个入口。这个入口应该能完成骨架创建、静态分析、缓存、只读探活、登录态控件枚举、知识编译和证据记分,并且每一步都留下具名产物。
我判断一键入口值得补,是因为当时最花时间的并非理解网站本身,而是确认“哪个现成脚本负责哪一层”。有的脚本已经存在,只是没有出现在标准入口里;有的输出可以复用,却没有清楚的文件名和索引。于是人只能靠搜索目录、猜参数、看旧记录,再手写胶水把它们接起来。
错误捷径是把这件事继续写在文档里,告诉下一个人“先做静态分析,再做页面验证”。文档能说明顺序,却不能保证每次使用相同的输入、输出和失败处理。只要执行者临场漏掉一步,最后的知识库仍然可能看起来完整。
一个流程成熟的标志,不是它能跑通一次,而是下一次少依赖临场记忆。
因此,一键入口不需要把所有事情都自动化。付费、写入、上传等有风险的动作仍然应该停在边界外;它要自动化的是重复的准备、串联和记分,把必须由人判断的地方明确标红。自动化不是替人作决定,而是让人不用反复寻找决定所需的材料。
失败也要成为证据的一部分
熟悉陌生网站时,失败是正常状态。接口可能返回未授权,页面可能因为登录态或服务波动打不开,某个按钮可能只显示提示而不真正展开。问题不在于有没有红项,而在于红项有没有被写清楚。
这次我把“未验证”和“不可用”分开。未验证表示还没有在允许范围内实测;不可用表示已经尝试,但当前条件下得到明确失败。前者不能算绿,后者也不能简单当成系统缺陷。比如匿名接口返回未授权,只能证明它需要更高权限,不能证明接口不存在。
同样,付费能力没有真实运行时,记分卡必须保留这个上限。静态代码、控件名称和价格说明可以证明“发现了这项能力”,却不能证明“这项能力已经可用”。如果不坚持这条边界,最容易出现的就是假绿:数字漂亮,真正操作时才发现没有走通。
这件事让我重新意识到,证据不是给报告增加重量的装饰,而是给结论加上边界。它告诉我哪些话可以说得肯定,哪些话只能说“目前推测”,也让下一次复验有明确起点。
下次我会怎么做
下次遇到陌生网站,我会先建立4层工作面:静态、缓存、只读 live 和证据。先从 bundle 画出候选地图,再用只读方式验证真实页面和接口;每层输出单独保存,静态值和实测值绝不混算。
然后我会检查是否存在一键入口。如果没有,就优先把现成脚本串起来,而不是先继续扩充知识库。入口至少要能创建工作骨架、保存原始材料、输出静态地图、执行探活和控件枚举,并在最后生成带来源的记分结果。
最后,我会把“没有验证”当作一种正式结果。对每个能力同时记录发现方式、实测状态和证据位置;遇到登录、付费或写入边界,就留下清楚的待办,不用乐观措辞把空白填成绿色。
表面上,我是在给陌生网站做测绘;其实我是在把一次性的熟悉过程,变成下一次可以依赖的工作记忆。真正可复用的方法,不是某个解析器或某个命令,而是这条判断链:先发现,再验证;先分来源,再记分;先把重复动作串起来,再让人处理真正需要判断的部分。