前端侦探:我是如何挖掘出网站里 28 个"隐藏商品"的?
免责声明:本文仅供技术交流与学习,请勿利用文中技术手段对他人的服务器造成压力或进行恶意爬取。所有测试数据均来自公开接口。
🕵️♂️ 从一个好奇心开始
前几天逛一个数字产品合租平台(nf.video)时,我发现它首页只孤零零地挂着 6 个商品:Netflix、Disney+、Spotify 等常见的全家桶。
作为一个前端开发者,我的直觉告诉我:事情没这么简单。
通常这类平台为了 SEO 或者后台管理的统一性,数据库里往往躺着更多商品,只是因为库存、策略原因被前端"隐藏"了。今天就带大家通过浏览器控制台(Console),用几招前端调试技巧,扒出那些藏在代码背后的秘密。
🔍 第一层:摆在明面上的数据
首先,我们看看普通用户能看到什么。打开控制台,简单查一下 DOM:
javascript
// 获取首页所有商品卡片
const cards = document.querySelectorAll('.platFormItem');
console.log(`首页可见商品数: ${cards.length}`);
// 输出: 6
确实只有 6 个。这建立了我们的"基准线"。如果后面我们找到了多于 6 个的数据,那就说明有"隐藏款"。
🎣 第二层:Vue Router 拦截术
点击商品卡片会跳转到购买页。通常我们会看 Network 面板找链接,但这个网站是 SPA(单页应用),点击是路由跳转。
为了不真的跳走(跳走就得退回来,麻烦),我们可以利用 Vue Router 的全局前置守卫来做一个"钩子"。我们想知道点击卡片后,路由到底想带我们去哪?
我们可以直接在控制台注入这段代码:
javascript
// 假设挂载在 app 上的 router 实例(视具体项目而定,通常在 vueApp.config 或 __vue_app__ 中)
// 这里演示思路
const router = document.querySelector('#app').__vue_app__.config.globalProperties.$router;
// 👮♂️ 注册一个拦截守卫
router.beforeEach((to, from, next) => {
console.log(`🎯 捕获到目标路由: ${to.fullPath}`);
console.log(`📦 参数 ID: ${to.params.id}`);
// ✋ next(false) 阻止实际跳转,我们就停在当前页
next(false);
});
然后在页面上点击一个"苹果商店"的卡片:
Console 输出:
🎯 捕获到目标路由: /buy/31📦 参数 ID: 31
Bingo!我们摸清了路由规则:/buy/:id。这意味着商品是以 ID 为索引的。
🕵️ 第三层:Performance API 里的蛛丝马迹
页面加载完了,Network 面板里的请求都被冲掉了或者很难找。这时,浏览器原生的 Performance API 就像一个黑匣子,记录了所有发生过的资源请求。
我想看看前端到底请求了哪些 API 接口:
javascript
// 筛选所有 XMLHttpRequest 或 Fetch 请求
const apiRequests = performance.getEntriesByType('resource')
.filter(e => e.initiatorType === 'xmlhttprequest' || e.initiatorType === 'fetch')
.map(e => e.name);
console.table(apiRequests);
在一堆日志里,我发现了这几个有趣的接口:
/api/applets/goods/get/homeManage(首页数据,估计就那 6 个)/api/applets/goods/get/categoryGoods(分类商品?这个听起来有戏!)
我尝试手动调用了一下这个 categoryGoods 接口:
javascript
fetch('/api/applets/goods/get/categoryGoods')
.then(res => res.json())
.then(data => console.log(`拿到所有商品数: ${data.data.length}`));
// 输出: 27
27 个! 远超首页的 6 个。
通过分析返回的 JSON,我看到了大量首页没展示的商品:
ID 20: MagSafe 三合一无线充ID 96: 银河次时代智能 NAS (这啥黑科技?)ID 111: Typora 正版授权
到这里,如果是普通用户可能就满足了。但作为程序员,我注意到 ID 并不连续。最大的 ID 是 113,但中间缺了很多数字。
那些消失的 ID 去哪了?
🚀 第四层:ID 暴力枚举与深度挖掘
既然知道了 API 模式是 /api/applets/goods/get/:id,且 ID 是数字。那我能不能写个脚本,把 1 到 200 的 ID 全扫一遍?
这就像是在玩"扫雷"。
javascript
// 简单的并发探测脚本
async function scanHiddenGoods(maxId) {
const hiddenGoods = [];
console.log(`🚀 开始扫描 ID 1 - ${maxId}...`);
const promises = [];
for (let id = 1; id <= maxId; id++) {
const p = fetch(`/8081/api/applets/goods/get/${id}`)
.then(res => res.json())
.then(res => {
// 如果接口返回成功且有数据
if (res.code === 10000 && res.data) {
return { id, name: res.data.goodsName, price: res.data.price };
}
return null;
})
.catch(() => null);
promises.push(p);
}
const results = await Promise.all(promises);
return results.filter(Boolean); // 过滤掉 null
}
// 让我们跑一下
scanHiddenGoods(200).then(goods => {
console.table(goods);
console.log(`🎉 共发现商品: ${goods.length} 个`);
});
几秒钟后,控制台打出了一张长长的表格。
结果令人震惊:
除了刚才分类列表里的 27 个,我又挖出了 8 个"幽灵商品"。这些商品连分类 API 都不返回,完全是"隐形"的,只有通过 ID 直达才能看到:
| ID | 名称 | 这居然也有? |
|---|---|---|
| 18 | GPT Plus | 可能因为合规问题隐藏 |
| 26 | Midjourney | 只能直接访问购买 |
| 50 | Runway | 那个文生视频的 AI |
| 105 | Codex | 编程神器 |
这些商品很可能是测试下架的、或者是仅限内部/老客户通过链接购买的。
📝 总结
通过这次探索,我们发现了网站里共有 34 个有效商品,而首页只展示了 17%。
回顾一下我们的"作案工具":
- DOM 解析:看清表象。
- Vue Router 守卫:拦截路由,探知路径规则。
- Performance API:回溯历史请求,定位关键后端接口。
- Promise.all 并发探测:暴力枚举,发现离散数据。
前端开发不仅仅是画页面,善用浏览器提供的调试工具,我们可以对正在运行的应用有更深层的理解(或者单纯是为了满足好奇心 😉)。
如果你觉得这个分析过程有趣,欢迎点赞收藏!