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

Chrome扩展后台脚本开发:获取网页全资源并复用DOM内容

Chrome扩展:抓取网页全量资源并渲染DOM

问题

需要编写Chrome扩展的background script,获取youtube.com这类网页的所有资源(HTML、图片、脚本、样式表等),存储到localStorage、缓存或内存中,之后用这些数据设置标签页的DOM内容。目前通过fetch仅能加载HTML文本,关联资源无法加载渲染。

现有代码

Background Script

console.log("Background script loaded.");

chrome.tabs.onActivated.addListener((tab) => {
  chrome.tabs.get(tab.tabId, (currentTabData) => {
    chrome.scripting.executeScript({
      target: { tabId: currentTabData.id },
      files: ['contentScript.js']
    });
  });
  console.log("Content script injected");
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {

    fetch('https://www.youtube.com')
      .then(response => response.text())
      .then(data => {
        chrome.tabs.sendMessage(tabId, { html: data });
        console.log("message sent")
      })
      .catch(error => console.log(error));

  });

});

Content Script

console.log("Content script loaded.");

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
  console.log("Message received: " + message);
  if (message.html) {
    console.log("Inside if");
    document.body.innerHTML = message.html;
  }
});

解决方案

核心问题分析

直接替换innerHTML仅能加载HTML文本,页面中的资源引用(如img.src、link.href)仍会指向原网站,而非本地存储的资源。需通过资源拦截+本地缓存实现全量资源加载。

实现步骤与代码

1. Manifest.json 权限配置

确保扩展拥有必要权限:

{
  "manifest_version": 3,
  "name": "网页资源缓存工具",
  "version": "1.0",
  "permissions": ["webRequest", "webRequestBlocking", "storage", "scripting", "tabs"],
  "host_permissions": ["*://*.youtube.com/*"],
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["*://*.youtube.com/*"],
      "js": ["content.js"]
    }
  ]
}

2. Background Script(background.js)

负责抓取全量资源、缓存、拦截请求:

console.log("Background script loaded.");

// 内存缓存(持久化可改用chrome.storage.local)
const resourceCache = new Map();

// 抓取页面及所有关联资源
async function fetchAndCachePage(url) {
  try {
    // 抓取HTML
    const htmlRes = await fetch(url);
    const htmlText = await htmlRes.text();
    resourceCache.set(url, { content: htmlText, type: 'text/html' });

    // 解析HTML提取资源URL
    const parser = new DOMParser();
    const doc = parser.parseFromString(htmlText, 'text/html');
    const resourceSelectors = [
      'link[rel="stylesheet"]', 'img[src]', 'script[src]',
      'video[src]', 'audio[src]', 'source[src]'
    ];
    const resourceUrls = new Set();

    resourceSelectors.forEach(selector => {
      doc.querySelectorAll(selector).forEach(el => {
        const src = el.src || el.href;
        src && !resourceUrls.has(src) && resourceUrls.add(src);
      });
    });

    // 批量缓存资源
    await Promise.all(Array.from(resourceUrls).map(async resUrl => {
      try {
        const res = await fetch(resUrl);
        const blob = await res.blob();
        resourceCache.set(resUrl, { content: blob, type: res.headers.get('Content-Type') });
      } catch (err) {
        console.error(`缓存资源失败: ${resUrl}`, err);
      }
    }));

    console.log(`页面资源缓存完成: ${url}`);
  } catch (err) {
    console.error(`抓取页面失败: ${url}`, err);
  }
}

// 拦截请求,返回缓存内容
chrome.webRequest.onBeforeRequest.addListener(
  async details => {
    const url = details.url;
    if (resourceCache.has(url)) {
      const res = resourceCache.get(url);
      const arrayBuffer = await res.content.arrayBuffer();
      // 转换为data URL返回
      return {
        redirectUrl: `data:${res.type};base64,${btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)))}`
      };
    }
    return { cancel: false };
  },
  { urls: ["*://*.youtube.com/*"] },
  ["blocking"]
);

// 监听标签页加载完成,触发缓存
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && tab.url.includes('youtube.com')) {
    await fetchAndCachePage(tab.url);
    chrome.tabs.sendMessage(tabId, { action: 'loadCachedPage', url: tab.url });
  }
});

3. Content Script(content.js)

负责更新标签页DOM:

console.log("Content script loaded.");

chrome.runtime.onMessage.addListener(async (message, sender) => {
  if (message.action === 'loadCachedPage' && message.url) {
    // 从内存缓存获取HTML(若用持久化存储则改为chrome.storage.local.get)
    const cachedHtml = resourceCache.get(message.url);
    if (cachedHtml) {
      document.documentElement.innerHTML = cachedHtml.content;
      console.log("缓存页面已渲染");
    } else {
      console.log("无缓存页面可用");
    }
  }
});

注意事项

  • Manifest V3 限制:Service Worker无法直接访问DOM,需用DOMParser解析HTML。
  • 缓存持久化:内存缓存会随Service Worker重启丢失,大资源建议用IndexedDB存储,避免chrome.storage.local的5MB限制。
  • 动态资源处理:对于懒加载资源,需监听DOM变化(如MutationObserver),实时抓取并缓存新资源。
  • 跨域权限:确保host_permissions覆盖目标网站,否则fetch会失败。

内容的提问来源于stack exchange,提问作者Harisanker k m

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 15:09:58