前端实践:从商品页面读取 mpn 并实现一对一跳转
在实际开发中,我们经常会遇到这样一种需求:
用户浏览 A 网站的商品页面后,点击按钮能够直接跳转到 B 网站的对应商品。
表面看似只是一个按钮跳转,但如果不同商品需要精确映射,就必须找到每个商品的唯一标识。这时,很多电商页面都会通过 结构化数据(JSON-LD) 提供关键信息,例如
mpn
(Manufacturer Part Number,制造商零件号)。
1. 问题背景
- 我需要在商品详情页中获取唯一标识 mpn,并基于它拼接目标商店的跳转链接。
- 页面本身是第三方的,不能直接改动服务端返回的数据。
- 目标效果:隐藏原始「加入购物车 / 立即购买」按钮,替换为一个自定义按钮,点击后跳转到另一个电商平台(例如 DHgate)的对应商品。
2. 数据获取的波折
一开始,我尝试直接在页面 DOM 中寻找 mpn
:
document.querySelector()
去找标签 → 失败- 在
meta
标签里查找 → 失败 - 在页面可见文本里搜索 → 失败
直到我注意到页面 <head>
区域存在大量 <script type="application/ld+json">
标签,这是用于 SEO 的结构化数据。
里面通常包含:
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "Luxury Custom Handmade Watch",
"mpn": "123456789",
"brand": {
"@type": "Brand",
"name": "Artisan Watches"
}
}
注解:
JSON-LD(JavaScript Object Notation for Linked Data)是 Google 推荐的结构化数据标注方式。
电商网站会用它向搜索引擎说明商品属性,比如:名称、价格、库存、SKU、MPN 等。
这意味着 最佳的获取方式是解析 JSON-LD,而不是从 DOM 树中“扒值”。
3. 解决方案:解析 JSON-LD 提取 mpn
代码实现如下:
// 获取所有 JSON-LD 脚本标签
const ldJsonScripts = document.querySelectorAll('script[type="application/ld+json"]');
let mpnValue = null;
// 遍历解析 JSON,找到 Product 类型的 mpn
ldJsonScripts.forEach(script => {
try {
const jsonData = JSON.parse(script.textContent);
if (jsonData["@type"] === "Product" && jsonData.mpn) {
mpnValue = jsonData.mpn;
}
} catch (e) {
console.error("解析 JSON-LD 出错:", e);
}
});
这样就能稳健地提取出 mpn
值。
4. 替换默认按钮并添加跳转逻辑
由于目标是 一对一跳转,我需要屏蔽原有的购物入口,改为跳转到目标链接。
4.1 隐藏原有按钮
原始按钮 ID 具有统一前缀(如 add-cart-xxx
、buynow-xxx
),所以通过前缀匹配批量隐藏:
function hideElementsWithPrefix(prefix) {
const allElements = document.getElementsByTagName('*');
for (let element of allElements) {
if (element.id && element.id.startsWith(prefix)) {
element.style.display = 'none';
}
}
}
并且定时执行,防止异步加载的按钮漏网:
setInterval(() => {
hideElementsWithPrefix('add-cart-');
hideElementsWithPrefix('buynow-');
}, 1000);
4.2 添加自定义按钮
const customButton = document.createElement('button');
customButton.textContent = 'Buy Now on DHgate';
customButton.style.cssText = `
background-color: #D4AF37;
color: white;
border: none;
padding: 12px 24px;
font-size: 16px;
font-weight: bold;
border-radius: 6px;
cursor: pointer;
margin: 10px 0;
transition: background-color 0.3s ease;
`;
// 悬停效果
customButton.addEventListener('mouseover', () => customButton.style.backgroundColor = '#C89A2E');
customButton.addEventListener('mouseout', () => customButton.style.backgroundColor = '#D4AF37');
// 点击跳转
customButton.addEventListener('click', () => {
const targetUrl = `https://www.dhgate.com/product/french-artisanal-luxury-exquisite-custom/${mpnValue}.html?`;
window.location.assign(targetUrl);
// 备份方案:防止 assign 失败
setTimeout(() => {
if (window.location.href !== targetUrl) {
window.location.href = targetUrl;
}
}, 100);
});
插入到页面:
const insertionPoint = document.querySelector('.product-cart-group') ||
document.querySelector('.main_btn').parentNode ||
document.body;
insertionPoint.appendChild(customButton);
5. 最终效果
- 页面原有的「加入购物车 / 立即购买」按钮被隐藏。
- 出现一个新的金色按钮 “Buy Now on DHgate”。
- 点击时,根据当前页面解析到的
mpn
值,自动拼接跳转链接并跳转。
例如:
https://www.dhgate.com/product/french-artisanal-luxury-exquisite-custom/123456789.html
非常好 👍
你提到的这一点非常关键:作为后端,很多人会觉得前端拿个值“应该很简单”,但实际落到细节上,经常会踩坑。
你经历 mpn
的抓取,就是一个典型的前端数据获取思路问题。
下面我以“从一个商品页面获取关键数据”为引,系统整理前端常见的数据抓取手段,带例子和注解。
前端数据抓取手段
前端里,想要拿到一个页面的数据,不一定总是
document.querySelector("#id")
那么直白。
1. 直接 DOM 查询
最直观的方法就是 通过选择器直接查找元素。
// 通过 ID
const price = document.querySelector("#product-price").textContent;
// 通过 class
const title = document.querySelector(".product-title").innerText;
// 通过标签层级
const stock = document.querySelector("div.product-info span.stock").innerText;
注解:
- 如果页面是 SSR(服务端渲染),DOM 一开始就有数据,这是最快捷的方式。
- 但对于 SPA(单页应用),很多内容是后续 JS 渲染的,需要等
window.onload
或MutationObserver
。
2. 结构化数据(JSON-LD、Microdata、RDFa)
之前遇到的 mpn
,就是嵌在 <script type="application/ld+json">
里的。
const scripts = document.querySelectorAll('script[type="application/ld+json"]');
scripts.forEach(s => {
try {
const json = JSON.parse(s.textContent);
if (json["@type"] === "Product") {
console.log(json.mpn, json.sku, json.brand?.name);
}
} catch (e) {}
});
注解:
- 这种方式对 SEO 友好,Google / Bing / 电商比价插件都会用。
- 如果在做 商品比对 / 跨站跳转 / 爬虫,这是最可靠的切入点。
3. 隐藏在 meta 标签 / 属性中
一些页面会把关键数据放在 <meta>
、data-*
属性里,给搜索引擎或前端脚本用。
例子:meta 标签
<meta property="product:price:amount" content="99.99">
<meta property="product:sku" content="A12345">
获取:
const price = document.querySelector('meta[property="product:price:amount"]').content;
const sku = document.querySelector('meta[property="product:sku"]').content;
例子:data-* 属性
<button id="buyBtn" data-sku="A12345" data-stock="20">Buy Now</button>
获取:
const btn = document.getElementById("buyBtn");
console.log(btn.dataset.sku); // "A12345"
console.log(btn.dataset.stock); // "20"
注解:
- data-* 是 HTML5 规范推荐的“私有数据通道”,比
id
/class
更稳定。- 在电商、新闻站、CMS 里很常见。
4. 页面内嵌 JSON 配置(window 变量 / inline script)
有些网站在 <script>
里会直接挂一个全局对象,供页面其他 JS 使用。
例子:
<script>
window.__INITIAL_STATE__ = {
product: {
id: "12345",
name: "Luxury Watch",
price: 299.99,
mpn: "XYZ987"
}
};
</script>
获取:
console.log(window.__INITIAL_STATE__.product.mpn);
注解:
- SPA 框架(React/Vue/Next.js)经常这么做,把后端数据注入到页面里。
- 在 f12 → Console 输入
window
,多翻几页,常能挖到完整的数据结构。
5. Ajax / Fetch 请求拦截
很多页面并不是直接渲染,而是前端在加载时通过 Ajax/FETCH 请求接口。
这类数据可以直接抓接口,而不是“扒 DOM”。
例子:
商品页打开时,浏览器可能会请求:
GET https://api.shop.com/product/12345
返回:
{
"id": "12345",
"title": "Luxury Watch",
"price": "299.99",
"mpn": "XYZ987"
}
在前端可以这样拦截:
// 重写 fetch
const originalFetch = window.fetch;
window.fetch = async (...args) => {
const response = await originalFetch(...args);
if (args[0].includes("/product/")) {
response.clone().json().then(data => {
console.log("抓到商品数据:", data);
});
}
return response;
};
注解:
- 这是 最稳定 的方案,因为你直接拿到原始 JSON 数据。
- 后端开发者更熟悉接口,所以只要能找到 API 请求,比 DOM 抓取更靠谱。
- 在 DevTools → Network 里找
XHR
/Fetch
,常能发现目标接口。
6. 监听 DOM 变化(MutationObserver)
如果页面异步渲染(比如 React/Vue 延迟加载),DOM 里可能一开始没有数据。
这时需要用 MutationObserver 来监听节点变化。
const observer = new MutationObserver(mutations => {
const priceNode = document.querySelector(".product-price");
if (priceNode) {
console.log("价格:", priceNode.textContent);
observer.disconnect(); // 拿到后停止监听
}
});
observer.observe(document.body, { childList: true, subtree: true });
注解:
- 适合异步加载的 SPA。
- 比
setInterval
更优雅,但逻辑复杂一些。
7. Canvas / Shadow DOM / 加密数据(特殊情况)
一些网站会反爬虫,把数据写在:
- Canvas 渲染出来(你只能截图识别)
- Shadow DOM(需要穿透
.shadowRoot
) - Base64 / 加密字符串(要解码/解密)
这种情况属于“反爬虫”范畴,日常不常见,但电商/金融站点有时会遇到。
总结
方法 | 特点 | 适用场景 | 难度 |
---|---|---|---|
DOM 选择器 | 直观,容易写 | 静态页面、SSR | ⭐ |
JSON-LD / Microdata | 结构化,标准化 | 商品详情、SEO 站点 | ⭐⭐ |
meta / data-* | 隐藏但容易读 | 电商、CMS | ⭐⭐ |
window 全局变量 | 完整数据,格式化 | React/Vue 注水页面 | ⭐⭐ |
Ajax / Fetch | 原始 JSON,最稳定 | SPA、电商 API | ⭐⭐⭐ |
MutationObserver | 处理异步渲染 | Vue/React 延迟加载 | ⭐⭐⭐ |
Canvas / Shadow DOM | 反爬虫用 | 金融、电商防护 | ⭐⭐⭐⭐ |
建议的思路是:
- 优先找 API(最干净的 JSON);
- 找不到 API → 看 JSON-LD / meta / data-*;
- 再不行 → 扫
window
全局变量; - 实在没有 → MutationObserver、模拟渲染、甚至 OCR。