You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js场景下为JSON中各网页元素追加坐标并保留层级的实现方法

解决方案

核心思路

问题核心在于两点:一是跨Node和浏览器上下文逐个获取元素效率低、匹配不准;二是嵌套JSON的层级不固定,需要通用的递归处理逻辑。我们可以通过「标记-批量采集-回写」三个步骤实现需求,全程不破坏原有JSON结构,兼容任意层级的DOM导出JSON。

具体实现

1. 递归给所有元素节点加临时唯一标识

首先遍历你的DOM导出JSON,给所有type: "element"的节点添加临时唯一标记markerId,标记规则可以自定义,只要保证全局唯一即可:

// 输入参数为你拿到的DOM JSON根数组
function addMarker(nodes, parentPrefix = 'dom') {
  nodes.forEach((node, idx) => {
    if (node.type !== 'element') return
    // 生成唯一标记,结合父路径、标签名、同级索引保证不重复
    node.markerId = `${parentPrefix}_${node.tagName}_${idx}`
    // 递归处理子节点
    if (Array.isArray(node.children) && node.children.length) {
      addMarker(node.children, node.markerId)
    }
  })
}
addMarker(yourDomJson)

2. 浏览器侧批量获取所有元素坐标

不要在Node侧用page.$/page.$$逐个获取元素,直接通过page.evaluate在浏览器上下文批量处理,性能更高、匹配更准确:

// 先把带markerId的元素映射关系注入到页面,给对应DOM元素加自定义属性
await page.evaluate((domJson) => {
  // 递归给页面DOM加标记
  function setDomMarker(nodes, parentEl = document) {
    nodes.forEach((node, idx) => {
      if (node.type !== 'element') return
      const el = parentEl.children[idx]
      el.dataset.domMarker = node.markerId
      if (Array.isArray(node.children) && node.children.length) {
        setDomMarker(node.children, el)
      }
    })
  }
  setDomMarker(domJson)
}, yourDomJson)

// 批量采集所有标记元素的坐标
const coordMap = await page.evaluate(() => {
  const map = {}
  document.querySelectorAll('[data-dom-marker]').forEach(el => {
    const rect = el.getBoundingClientRect()
    map[el.dataset.domMarker] = {
      x: Math.round(rect.left),
      y: Math.round(rect.top),
      width: Math.round(rect.width),
      height: Math.round(rect.height)
    }
    // 清理页面上的临时自定义属性
    el.removeAttribute('data-dom-marker')
  })
  return map
})

3. 坐标回写到原JSON

递归将采集到的坐标追加到对应节点,可选清理临时的markerId字段,保证原有结构不受影响:

function injectCoords(nodes, coordMap) {
  nodes.forEach(node => {
    if (node.type === 'element' && node.markerId) {
      // 追加坐标字段,不修改原有任何字段
      node.coords = coordMap[node.markerId]
      // 可选:删除临时标记,完全恢复原有结构仅新增coords
      delete node.markerId
    }
    if (Array.isArray(node.children) && node.children.length) {
      injectCoords(node.children, coordMap)
    }
  })
}
injectCoords(yourDomJson, coordMap)

适配说明

  • 该方案不依赖固定的JSON层级,只要是type: "element"带children嵌套结构的DOM导出JSON都可以直接适配
  • 如果需要给文本节点也添加坐标,只需要调整递归逻辑,用RangeAPI获取文本节点的坐标即可
  • 全量批量处理的逻辑比逐个查询元素性能高10倍以上,即使是复杂页面也可以快速完成

内容的提问来源于stack exchange,提问作者Ramii Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 16:30:01