开发Chrome扩展实现YouTube视频描述含Licence关键词告警:检测失效、误扫旧页面问题排查
问题核心根因
你碰到的两个异常(持续检测到上一页内容、扩展偶发失效)根本不是YouTube API适配的问题,是三个实现细节没踩中单页应用(SPA)的特性:
- YouTube是纯单页应用,
webNavigation.onHistoryStateUpdated的触发时机远早于视频描述区DOM渲染完成,此时发消息给内容脚本,读到的全是上一个视频的残留DOM - 现有匹配逻辑是全页面扫XPath找「Licence」,没有限定到视频描述区域,很容易把其他位置的同关键词误判,碰到懒加载的描述区时还会因为节点没加载返回错误结果
- 消息通信的时序没有兜底,一旦内容脚本没注入完成、或者sendResponse时机不对,就会触发通信失败,表现为偶发不工作
你之前想靠YouTube API判断播放状态的思路完全没必要,纯靠原生DOM监听+合适的触发时机就能实现功能,不用调用任何外部接口。
优化落地方案
- 把核心检测逻辑从“background发消息等内容脚本返回结果”改成“内容脚本自主监听路由变化、DOM更新时机,触发后自主完成检测+告警”,彻底绕开消息通信的时序问题
- 检测范围从全页面缩小到YouTube固定的视频描述区容器,从根源上减少误判
- 加防抖、去重逻辑,避免DOM频繁更新时重复检测、同一个视频反复弹告警
- 注入时机调整为页面空闲阶段再加载内容脚本,减少注入太早导致的DOM读取错误
改完的可运行代码
contentscript.js
// 存储已告警过的视频ID,避免重复弹窗 let lastAlertedVideoId = null; // 防抖定时器 let checkDebounceTimer = null; // 核心检测逻辑 function runKeywordCheck() { // 锁定YouTube视频描述区的固定容器 const descArea = document.querySelector('#description-inline-expander, ytd-text-inline-expander'); if (!descArea) return; // 提取当前视频ID const currentVideoId = new URL(location.href).searchParams.get('v'); // 已经检测告警过的视频直接跳过 if (currentVideoId === lastAlertedVideoId) return; // 在描述区范围内匹配关键词 const isLicenceExist = descArea.textContent.includes('Licence'); if (isLicenceExist) { lastAlertedVideoId = currentVideoId; alert("检测到视频描述区包含「Licence」关键词"); } } // 防抖处理:避免DOM频繁变动时重复触发检测 function debouncedRunCheck() { clearTimeout(checkDebounceTimer); checkDebounceTimer = setTimeout(runKeywordCheck, 300); } // 监听页面初次加载 window.addEventListener('load', debouncedRunCheck); // 监听YouTube自身的导航完成事件(比原生popstate触发时机更准) window.addEventListener('yt-navigate-finish', debouncedRunCheck); // 监听DOM变化:等懒加载的描述区内容渲染完成再触发检测 const domObserver = new MutationObserver(debouncedRunCheck); domObserver.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false });
background.js
简化成只做路由变更的通知,不再处理检测的返回值,避免alert脱离页面上下文:
// 监听YouTube站点的历史状态变更 chrome.webNavigation.onHistoryStateUpdated.addListener(function(details) { // 只给YouTube视频页发通知,其他页面不处理 if (details.url.includes('youtube.com/watch')) { chrome.tabs.sendMessage(details.tabId, {message: "pageChange"}); } });
manifest.json
缩小内容脚本的匹配范围,调整注入时机,减少性能消耗:
{ "manifest_version": 2, "name": "Licence关键词告警", "version": "1.0", "permissions": [ "tabs", "webNavigation", "activeTab" ], "content_scripts": [{ "matches": ["*://*.youtube.com/*"], "js": ["contentscript.js"], "run_at": "document_idle" }], "background": { "scripts": ["background.js"] } }
调整后的效果说明
- 原来的时序问题:把检测逻辑放到内容脚本里,结合YouTube官方的
yt-navigate-finish导航完成事件、MutationObserver监听DOM更新,等描述区真正渲染出内容之后再读,完全不会拿到上一页的残留DOM - 原来的偶发失效:不再依赖跨进程的消息返回做检测,内容脚本自主判断DOM状态,不会出现消息丢失、sendResponse时机不对的问题,检测稳定性会高很多
- 原来的误判问题:只在描述区容器里找关键词,不会把页面其他位置的同关键词当成触发条件,检测精度足够
- 原来的重复告警:加了视频ID的状态记录,同个视频只弹一次告警,切到新视频再重新判断
内容的提问来源于stack exchange,提问作者Vojtěch Fíla
相关产品推荐
相关产品推荐

