• 使用Chrome插件拦截广告的原理


    使用 Chrome 插件拦截广告的原理

    项目地址:chrome_plugin_zhihu_adblock

    本文阅读起来可能需要先了解一些 Chrome 插件基础知识,通过本文您可以学到什么?

    1. chrome 插件拦截广告思考方法和一般原理
    2. 浏览器拦截 fetch 和 xhr 请求的方法

    思路

    网页上的广告可以分以下三种情况:

    1. 网页上的广告都是由 html 构成的,所以只要用 chrome 插件删除这些 html即可。
    2. 有些广告是在正常加载后进行动态加载的,它们会和一般的代码块混在一起,这里我们可以拦截代码块的 http 请求,然后在请求成功之后,把广告从 html 上面删去
    3. 有些广告是通过专门的 http 请求加载的,这里我们拦截这些 http 请求,让他们发不出去即可。

    删除 html

    我们只需要知道广告 html 的 selector,然后直接删除即可,这里我们写一个比较通用的函数:

    // fuckAd
    const createFuckAd = (adSelector, textSelector) => () => {
        // 广告
        const ads = document.querySelectorAll(adSelector);
    
        if (ads.length > 0) {
            const cardBrand = document.querySelector(textSelector);
    
            if (cardBrand) {
                console.log(`已屏蔽广告:${cardBrand.innerText}`);
            }
    
            // 删掉广告
            [...ads].forEach(item => item.parentNode.removeChild(item));
        }
    }
    

    然后我们在 onload 事件里面删除广告即可。我们以知乎为例,代码如下:

    window.onload = () => {
        createFuckAd('.Pc-card')();
        createFuckAd('.Pc-feedAd-container', '.Pc-feedAd-card-brand--bold')();
        createFuckAd('.Pc-word-card', '.Pc-word-card-brand-wrapper > span')();
    }
    

    拦截请求

    对于动态加载的广告,我们可以拦截请求,判断是否有加载广告的请求进来了,然后对于混有正常请求的代码,我们在请求成功之后删掉广告;对于只有广告的请求,我们直接拦截这些请求。这里我们以 fetch 为例(对于 xhr 请求,可以通过这个库Ajax-hook),代码如下:

    // hook fetch
    const fetch_helper = {
        originalFetch: window.fetch.bind(window),
        myFetch: function (...args) {
            // 拦截只有广告的 http 请求
            if (args[0].includes('https://www.zhihu.com/commercial_api/')) {
                return Promise.reject(1);
            }
    
            return fetch_helper.originalFetch(...args).then((response) => {
                // 对于有正常代码的 http 请求,在请求完成之后,删除广告
                if (response.url.startsWith('https://www.zhihu.com/api/v3/feed/topstory/recommend?')) {
                    setTimeout(createFuckAd('.Pc-feedAd-container', '.Pc-feedAd-card-brand--bold'), 188);
                }
                return response;
            });
        },
    }
    
    window.fetch = fetch_helper.myFetch;
    

    其它

    核心代码我们已经写完了,然后只需要在document_start的时候引入代码即可。这里需要注意的是,我们不能直接在 contentScript 里面运行上面的代码,因为虽然 contentScript 能够操作页面上的 dom 元素,但是它运行在一个独立的环境里面,这里我们需要使用chrome.runtime.getURL获取 url,然后动态加载:

    const s = document.createElement("script");
    s.src = chrome.runtime.getURL("main.js");
    s.onload = function () {
        s.parentNode.removeChild(s);
    };
    (document.head || document.documentElement).appendChild(s);
    

    思考

    其实上面只是拦截广告的方法,但是对于一个好的广告拦截插件,怎么判断 html 和 js 是一个广告,才真正是一个非常大的挑战,我们通过查看adblock plus的源码可以发现它是这么判断的:

    // popupBlocker.js
    function checkPotentialPopup(tabId, popup)
    {
      let url = popup.url || "about:blank";
      let documentHost = extractHostFromFrame(popup.sourceFrame);
    
      let specificOnly = !!checkWhitelisted(
        popup.sourcePage, popup.sourceFrame, null,
        contentTypes.GENERICBLOCK
      );
    
      let filter = defaultMatcher.matchesAny(
        parseURL(url), contentTypes.POPUP,
        documentHost, null, specificOnly
      );
    
      if (filter instanceof BlockingFilter)
        browser.tabs.remove(tabId);
    
      logRequest(
        [popup.sourcePage.id],
        {url, type: "POPUP", docDomain: documentHost, specificOnly},
        filter
      );
    }
    

    它首先获取可能的跳转弹窗的 url,然后判断 url 的 host 和当前页面的 host是否一样,再判断白名单里面有没有。当然还有使用tensorflow通过机器学习收集可能的广告 url:

    // ml.js
    const tfCore = require("@tensorflow/tfjs-core");
    const tfConverter = require("@tensorflow/tfjs-converter");
    
    for (let object of [tfCore, tfConverter])
    {
      for (let property in object)
      {
        if (!Object.prototype.hasOwnProperty.call(tf, property))
          tf[property] = object[property];
      }
    }
    
  • 相关阅读:
    问题及解决:使用dotnet publish发布时Visual Stuido创建的配置文件中的路径失效
    模式的定义
    Identity Server 4 从入门到落地(三)—— 创建Web客户端
    信息系统的不能和能
    虚拟机中CentOS 6.8 Linux搭建GitLab服务器(安装篇)
    Eclipse快捷键大全
    冒泡排序实现
    JAVA的数据类型
    IDEA快捷键大全(翻译自官方手册)
    IntelliJ IDEA入门设置指南
  • 原文地址:https://www.cnblogs.com/yangzhou33/p/13835409.html
Copyright © 2020-2023  润新知