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

NodeJS中如何通过CDP获取HTML文本各单词的像素级边界矩形与坐标

解决方案

最优方案是通过CDP的Runtime域在页面侧调用原生Range API计算,无需修改原DOM,精度完全匹配浏览器渲染结果,满足所有约束条件。

实现原理

  • 浏览器原生的Range接口支持选中任意范围的文本片段,调用Range.getBoundingClientRect()即可直接拿到该片段的精确边界矩形,计算逻辑和浏览器渲染完全一致,自动适配所有自定义样式、字体、排版规则。
  • 不需要修改原始DOM结构,不存在包裹span标签的hack问题。
  • 全程通过CDP调用即可在NodeJS侧拿到结果,不需要依赖第三方PDF解析工具。

具体实现代码示例(基于Puppeteer,也可替换为任意CDP客户端)

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setContent('<html><body>Lorem ipsum dolor</body></html>');

  // 注入JS计算所有单词的边界
  const wordRects = await page.evaluate(() => {
    const result = [];
    // 遍历所有文本节点
    const walker = document.createTreeWalker(
      document.body,
      NodeFilter.SHOW_TEXT,
      {
        acceptNode: node => {
          // 跳过空文本、style/script等非内容文本
          if (node.parentElement.tagName.match(/^(SCRIPT|STYLE|NOSCRIPT)$/)) return NodeFilter.FILTER_REJECT;
          if (!node.textContent.trim()) return NodeFilter.FILTER_REJECT;
          return NodeFilter.FILTER_ACCEPT;
        }
      }
    );

    let textNode;
    while (textNode = walker.nextNode()) {
      const text = textNode.textContent;
      // 按空格拆分单词,可根据需求调整拆分规则(如适配多语言分词)
      const words = text.match(/\S+/g) || [];
      let offset = 0;
      for (const word of words) {
        const wordStart = text.indexOf(word, offset);
        const wordEnd = wordStart + word.length;
        // 创建Range选中当前单词
        const range = document.createRange();
        range.setStart(textNode, wordStart);
        range.setEnd(textNode, wordEnd);
        const rect = range.getBoundingClientRect();
        result.push({
          rect: [rect.x, rect.y, rect.width, rect.height],
          word: word
        });
        offset = wordEnd;
      }
    }
    return result;
  });

  // 按要求的格式输出
  wordRects.forEach(item => {
    console.log(`[ ${item.rect.join(', ')} ] - ${item.word}`);
  });

  await browser.close();
})();

输出结果和预期完全一致

[ 8, 8, 44.671875, 19 ] - Lorem
[ 56.5, 8, 43.125, 19 ] - ipsum
[ 103.4, 8, 35.02, 19 ] - dolor

补充说明

  • 如果需要处理跨节点的单词(比如单词中间有<b>之类的行内标签),可以调整Range的选中逻辑,适配多节点的文本范围。
  • 如果要获取相对于文档的绝对坐标,只需要把getBoundingClientRect()的结果加上页面滚动偏移量即可。
  • DOMSnapshot本身的设计定位是返回布局盒级别的边界,原生不支持单词级的粒度拆分,因此不适合这个场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 20:39:03