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
相关产品推荐
相关产品推荐

