【JavaScript】读取商品页面中的结构化数据(JSON-LD),在不改动服务端情况下,实现一对一跳转

发布于:2025-09-04 ⋅ 阅读:(16) ⋅ 点赞:(0)

前端实践:从商品页面读取 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-xxxbuynow-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.onloadMutationObserver

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 反爬虫用 金融、电商防护 ⭐⭐⭐⭐

建议的思路是:

  1. 优先找 API(最干净的 JSON);
  2. 找不到 API → 看 JSON-LD / meta / data-*;
  3. 再不行 → 扫 window 全局变量;
  4. 实在没有 → MutationObserver、模拟渲染、甚至 OCR。


网站公告

今日签到

点亮在社区的每一天
去签到