前言
本文主要为技术分享,仅供学习。 前段时间开发了一个浏览器新标签页的插件(见上文),最近在插件里植入了屏蔽搜索引擎广告的功能,在此记录并分享一下。 具体实现的效果如下:
如何识别搜索引擎的广告
以度娘为例,观察度娘搜索结果返回,我们能够发现,度娘搜索返回的每一条结果都包裹在一个class为c-container元素中, 如果这条搜索结果为广告,则该元素的子节点必定包含一个类似下面这样的节点:
<span
class="ec-tuiguang ecfc-tuiguang m12mvnb"
data-tuiguang='{"content":"本搜索结果为 [linkLabel] 信息,请注意可能的风险。","linkLabel":"商业推广","a":{"url":"http://e.baidu.com/ebaidu/home?refer=XXX"}}'>
广告
</span>
那么接下来,识别出带广告的搜索结果就很简单了,大致操作如下:
// data-tuiguang 带广告的标识
// c-container 一条搜索结果
const ads = document.querySelectorAll("[data-tuiguang]") // 找到页面中所有带有推广表示的元素
ads.forEach((ad) => {
const searchItem = ad.closest(".c-container") // 顺着推广标识, 找到最近的搜索结果祖先元素
// do something...
})
至于如何处理广告元素,这里不多做赘述,无非就是隐藏、删除或者遮罩等手段。
如何将这个功能嵌入插件中
内容脚本是拓展自带的能力,它能够在独立的js环境中运行而不与其托管网页或其他扩展程序发生冲突, 实际操作也很简单, 将我们写好的js脚本申明在拓展程序的manifest.json中即可,如下:
{
"name": "OVOTab 新标签页",
"author": "waylonzheng",
"manifest_version": 3,
// ...
"permissions": [
// ...
"storage", // 存储状态
"activeTab" // 允许访问当前标签页
],
"content_scripts": [
{
"matches": [
"https://cn.bing.com/*",
"https://www.baidu.com/*",
"https://www.so.com/*",
"https://www.sogou.com/*"
],
"js": [
"content.js" // 核心代码
],
"css": [
"style.css" // 注入的样式
],
"run_at": "document_end" // 在 DOM 完成之后,在图片和框架等子资源加载之前立即注入脚本 参考: https://developer.chrome.com/docs/extensions/reference/api/extensionTypes?hl=zh-cn#type-RunAt
}
]
}
当然,光靠manifest.json的配置是无法满足我们的需求的,在搜索后,可能会存在翻页等场景,这些场景返回的广告如何找到并屏蔽呢?这里笔者使用了MutationObserver,在首屏屏蔽完以后监听body后代的变更,完整代码如下:
// content.js
// 首次加载隐藏广告
function hiddenAdsFirstLoad() {
const adsList = document.querySelectorAll("[data-tuiguang]")
adsList.forEach((adsItem) => {
handleHiddenAds(adsItem)
})
}
// 隐藏广告
function handleHiddenAds(ads) {
// 找到搜索结果容器
const searchItem = ads.closest(".c-container")
if (searchItem) {
const isHandled = searchItem.classList.contains("ovo-search-ads-filter") // 是否已经处理过
if (isHandled) {
return
}
searchItem.classList.add("ovo-search-ads-filter") // 为广告添加样式
}
}
// 监听 DOM 变化
function setupMutationObserver() {
const observer = new MutationObserver(mutationObserverCallback)
const config = { childList: true, subtree: true } // 监听子节点变化 和 所有后代节点变化
observer.observe(document.body, config)
}
// 监听 DOM 变化回调
function mutationObserverCallback(mutations) {
requestAnimationFrame(() => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
// 对新增的节点 筛选是否是广告
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1 && node.hasAttribute("data-tuiguang")) {
handleHiddenAds(node) // 隐藏广告
}
})
}
})
})
}
// 判断是否开启过滤
window.chrome.storage.sync.get(["enable"], (result) => {
if (result.enable !== false) {
hiddenAdsFirstLoad() // 首屏屏蔽
setupMutationObserver() // 屏蔽后续新增的广告
}
})
/* style.css */
.ovo-search-ads-filter {
pointer-events: none;
user-select: none;
position: relative;
margin-bottom: 8px !important;
}
.ovo-search-ads-filter > div {
filter: grayscale(100%) blur(5px);
}
.ovo-search-ads-filter::after {
content: "OVO 去除广告";
display: block;
color: #818c9d;
font-size: 12px;
position: absolute;
font-weight: 500;
bottom: 0px;
right: 8px;
background-color: rgba(255, 255, 255, 0.1);
z-index: 1;
}
至此大功告成,大致效果如图:
结语
最后,本文主要为技术分享,仅供学习,欢迎大家体验笔者独立开发的新标签页插件~