广告过滤算法实现及优化

一 广告过滤综述

互联网已无处不在的今天,各互联网公司通过各种方式都赚的盆满钵满,其中很重要的一项收入来源就是页面广告,横幅广告,弹窗广告以及视频广告等等,这些对大部分用户来说,已经造成一定烦恼。因此,广告过滤已成为浏览器的必备插件之一,最为人熟知的广告过滤插件就是AdBlockhttps://adblockplus.org/),AdBlock针对FireFoxChrome等都有相应的插件版本。但是在移动智能设备上,浏览器还不能很好地对AdBlock进行支持,其实这不是因为移动智能设备上浏览器的原因,而是市场和各浏览器厂商导致的,目前移动智能终端系统被AndroidIOS两家独占,IOS上的safari以及IOS对开发者来说都是闭源,开发者很难对safari进行开发,好在Android平台上的WebKit/Blink是完全开源的,很多开发者可以进行开发后发布自己的浏览器,就国内而言,QQUC,百度浏览器等就都是这样的,他们也都各自实现了广告过滤,下面将对广告过滤核心算法的实现以优化作一些介绍。

广告过滤最早的实现者是AdBlock,广告过滤的实现原理其实非常简单:

#1 针对各类广告的实现方法,比如大幅广告图片,弹窗广告,视频广告,它们在页面中都是存在资源下载链接的,在浏览器进行这些资源下载时,发现它们的URL存在一个黑名单中,那就告诉浏览器别去下载这些广告,从而达到广告过滤的目的;

#2 对于页面内镶嵌的文字广告,就不能通过以上方法过滤了,这时只要在页面显示时,通过设置文字广告的CSS属性,比如 display:none;就可以把这些广告隐藏起来,给用户的感觉就是这些广告也被过滤了。就如AdBlock官网所展示的那样,如图1所示。

 

图1 广告过滤

二 广告过滤算法实现

  通过上面介绍可以了解到,广告其本上可以分成两大类,一是有明确的资源下载链接的广告,AdBlock称之为blocking的;二是内嵌在页面内容中的文字类广告,AdBlock称之为Elem hide。针对这样的每一个广告,AdBlock都会有与之相对应的规则(filter),只要被filter所匹配的,都会被拦截或者被隐藏。比如

#1 这样一条blocking规则http://example.com/ads/banner*.gif就会拦截下面这个gif广告

http://example.com/ads/banner123.gif

#2 这样一条Elem hide规则example.com##DIV[class=”textad”]就会把domainexample.com下的页面中广告隐藏起来,

<div class="textad">

Cheapest tofu, only here and now!

</div>

<div id="sponsorad">

Really cheap tofu, click here!

</div>

<textad>

Only here you get the best tofu!

</textad>


对于上面这些规则,AdBlock有更详细的描述:https://adblockplus.org/en/filters

因此,对于AdBlock而言,最重要的是这个持续不断更新的filter列表,规则列表并不只有AdBlock这一份,其实还有很多,比如EasylistFanboy, EasyList china等等。

  以上简要说了一下AdBlock的规则,那么接下来最重要的事就是如何进行这些规则的匹配,这里就是纯算法问题了。AdBlock也提供了一些实现机制方面的文章,请看这里(https://adblockplus.org/blog/investigating-filter-matching-algorithms)。先扮演一下搬运工,把AdBlock的算法简要说一下,这里我们大概想一下,要做的事情是啥:有一大堆filter,而每个filter都是字符串,给出一个URL需要判断是否与这堆规则中的某个filter匹配上。

  于是一个非常直观的算法,肯定是这样的,如图2所示,用脚趾头想了一下,这个算法需要遍历一下这堆filter,拿URL与每一个filter进行字符串比较,即使我们使用最快的字符串匹配算法,它的时间复杂度也是O(n*f),当规则表非常大时,这个算法的效率是不能接受的。

 

图2 算法1(图片截图自AdBlock官网)

接着,AdBlock对这个算法进行了优化,同时也给出了算法思路,如图3所示,优化后的算法是这样的,先对每一个filter中找到一个keyword,尽量保证这个keyword与其他filterkeyword不同,这样一来keywordfilter,就存在一对多的关系,理论上似乎可以做到一一对应,但是也没有必要的。这样就类似于哈希表一样,在匹配的时候,按照相同的方法将URL,分成一些keyword,然后通过keyword去找对应的filter,最后再匹配找到的filter。这样就不需要遍历规则表,大大地提高了匹配时间,文章也给出了keyword的经验长度8个字符,字符匹配使用了BM的非回溯算法,来尽量提高算法的匹配时间,在本人的实现中,大约有2300条规则,其中约95%都是无冲突的,最多重复keyword4次,两次及两次以上冲突只占到0.0072%,可见这个算法是非常高效的,可以认为是O(f),即URLfilter匹配所用时间。

 

图3 算法3(图片截图自AdBlock官网)

三 广告过滤算法的优化

   通过前面了解到过滤规则分为两类,blockingelem hide,如果都使用前面算法很显然对elem hide是不太适用的。首先需要分别来考虑blockingelem hide这两类规则。

   针对算法2的优化,算法2中在字符串匹配时,使用了的BMBoyer-Moore algorithmhttps://adblockplus.org/blog/investigating-filter-matching-algorithms),但是我们发现filter并不是简单的字符串,它也可以是正则表达式,看到正则表达式,我们就想到了Ken实现的DFA。所以,在不支持正则表达式,那就搞一个NFA,再把它变成DFA吧,尽管可能会有点难度,^_^ 

   我们来看看如何优雅地处理elem hide规则。Elem hide规则是形如这样的:example.com##DIV[class=”textad”] or example.com#@#DIV[class=”textad”],简单分析一下,它是由##或者#@#分割,前面都是一个或多个domain,后面是CSS选择器。对Elem hide规则要做的就是匹配到当前页面URLdomain是被elem hide规则中的domain包含的。下图4AdBlockElem hide规则的解析说明:(https://adblockplus.org/en/filter-cheatsheet)

 

图4 Elem hiding(图片截图自AdBlock官网)

#1 优化之hash_map

通过Elem hide规则特点是domain<-->CSS selectors,是一对多的映射广西,因此,可以使用数据结构map,将domain作为keyselectors作为value。这个在实现上是非常简单易懂的,另外,hash_map使用了hash table的特点在存储和查询都做到了线性时间内完成,缺点也是非常明显的,使用了更多的内存空间;还有比如有这样一个domainsubdomain.example.com,在hash_map中其实这个domainexample.com所对应的selectors也是适用于subdomain.example.com的,同时对~example.com这样的exclude规则处理会更加复杂。

#2 优化之Trie

在优化#1中,如何在保证hash_map的查询效率下,进一步节省内存空间呢?通过AdBlockElemhide规则的说明,一条example.com的规则,对subdomain.example.com也是生效的,这样仅对domain进行字符串匹配是不够,还需要做更多其他处理。那么如何做优化呢,其实只要把domain倒过来看,moc.elpmaxamoc.elpmaxe.niamodbus,看到这里,是不是豁然明朗了,这就是前缀匹配啊,立即想到Trie(字典树)就可以很轻松地搞定。Trie树如图5所示。


5 Trie

   由Trie树结构非常适合做前缀匹配,现在就可以使用Trie树来对处理所有Elem hide规则了,可以把所有Elem hide都放入同一颗Trie树中,匹配的时候,只需要进行Trie树查询,Trie树的查询时间只跟树的高度相关,时间效率是非常高的O(lgn)。通过对2000Elem hide规则建立Trie树,平均一次匹配时间是0.059ms,同时内存消耗比AdBlock的实现节省约60%,效果非常明显,当然在实现过程还有非常多的细节需要考虑和特别设计。

#3 优化之Double-array Trie

其实,在优化#2已经达到一个比较理想的优化结果,但是还有没有更进一步的优化空间呢,答案是肯定,那就是使用更高效的数据结构:Double-array TrieDouble-array Trie可以保证与Trie相同效率下,更进一步节省内存空间消耗,Double-array Trie如图6所示。


6 Double-array Trie

 

至此,广告过滤相关算法及其优化就介绍到这里,如果有更好的想法欢迎随时交流!

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

 

参考文献:

https://swtch.com/~rsc/regexp/regexp1.html

http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html

https://msdn.microsoft.com/zh-SG/library/525kffzd(v=vs.71)

https://adblockplus.org/en/filters

https://adblockplus.org/en/filter-cheatsheet

https://en.wikipedia.org/wiki/Trie

https://www.cs.bu.edu/teaching/c/tree/trie/

http://linux.thai.net/~thep/datrie/datrie.html

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页