広告削除方法模索(CSSで攻める)

追記以下のエントリーはこれを知らなかった。。

GM_addStyle(<><![CDATA[
#sub {display:none !important;}
td[class~="turnup"]:hover {background:pink !important;}
]]></>);


これまで個別のページごとに広告を削除するスクリプトは数多くあったが、
ここではテンプレート化して汎用性を高めたものを考える。

脱マシンガン方式

効率よく広告を消すgreasemonkeyスクリプトを考える (cherenkov.vox.com)
今までは削除したいやつをdocument.evaluateで大まかに拾ってきて、
一個一個リスト照会して削除をしていた。
今回は気持ちを切り替えてCSSで攻めてみた。

どういうもの?

最近のブログはタイトルと記事の間に広告が挟まれたりして、ユーザにとって不愉快なスクロールを強いることに疑問を感じた。近頃の広告はid要素やclass要素で丁寧にマーキングされている。そこで、そのマーキングを利用して効率よく広告を削除できるのではないかと考えた。
ユーザは消したい広告にマークされているid要素、class要素、name要素のいずれかをリストに追加すればいい。

Adblockよりも優れている点

  • Adblockは付属のブロック要素一覧が使いにくい。

 →うちの方法ではユーザがfirebugを使いながらリストに加える情報を集める。

  • Adblockは削除対象を画像やフレーム、Flashオブジェクトなどに限定されている。(正規表現を使えばいいが複雑で管理しにくい)

 →うちの方法ではテキストベースの広告などもidかclassかname要素が設定されていれば簡単に削除対象になる。

usercontent.cssよりも優れている点

  • 設定を反映するのにfirefoxをいちいち再起動をしなくてもよい。(Stylishはいらない)

さっそくデモ

ブックマークレットを用意しました。
gigazineで試してみてください トップページでも個別の記事でも広告を削除します。
今回のデモで消したい広告は以下の三種類。

  • id属性が 'af8b0702'
  • class属性が 'jobboard_blog_parts'
  • name属性が 'google_ads_frame'


gigazine専用ブックマークレット

javascript:(function(){function stringList(idList,classList,nameList){for(var i=0;i<idList.length;i++){idList[i]='#'+idList[i]+'{display:none!important;}';}for(var i=0;i<classList.length;i++){classList[i]='.'+classList[i]+'{display:none!important;}';}for(var i=0;i<nameList.length;i++){nameList[i]='[name="'+nameList[i]+'"]{display:none!important;}';}var removeList=idList.concat(classList,nameList).join('').toString();return removeList;}function addGlobalStyle(css){var head=document.getElementsByTagName('head')[0];if(!head){return;}var style=document.createElement('style');style.type='text/css';style.innerHTML=css;head.appendChild(style);}var idList=['af8b0702'];var classList=['jobboard_blog_parts'];var nameList=['google_ads_frame'];var removeList=stringList(idList,classList,nameList);addGlobalStyle(removeList);})();

主なソースコード(ここはまだ見るだけ)

(function(){

function stringList(idList,classList,nameList){

    for(var i=0;i<idList.length;i++){
        idList[i] = '#'+idList[i]+'{display:none!important;}';
    }

    for(var i=0;i<classList.length;i++){
        classList[i] = '.'+classList[i]+'{display:none!important;}';
    }

    for(var i=0;i<nameList.length;i++){
        nameList[i] = '[name="'+nameList[i]+'"]{display:none!important;}';
    }

    var removeList = idList.concat(classList,nameList).join('').toString();

    return removeList;

}


function addGlobalStyle(css){
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if(!head){
     return;
    }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}



    var idList = ['af8b0702'];
    var classList = ['jobboard_blog_parts'];
    var nameList = ['google_ads_frame'];

    var removeList = stringList(idList,classList,nameList);
    addGlobalStyle(removeList);

})();

仕組み

  1. stringList関数は引数の配列をCSS文字列にする機能。
  2. addGlobalStyle関数は引数の文字列をCSSに追加する機能。
  3. ユーザは広告にマークされているid属性、class属性、name属性をそれぞれidList、classList、nameListに追加する。
  4. stringList関数でリストに入力したものをCSSで見えなくする為にdisplay:noneと設定する。
  5. addGlobalStyle関数によってCSSを追加する。

効果

flashの広告はビデオメモリ容量の少ないノートPCのCPUに大きな負担を掛けるが、
このスクリプトによって、ページ読み込み完了毎に広告が削除されて一気に負荷が減った。

注意

    var idList = ['ad_01','ad_02'];
    var classList = ['']; ←
    var nameList = ['google_ads_frame'];


    var removeList = stringList(idList,classList,nameList);
    addGlobalStyle(removeList);

リストに入れるものがなくても上記のように、空でも記述しておかないといけない仕様。

日々の管理

  • たまに広告のマーキングが変ってないかチェック。リストのものが存在してなくても問題ない。
  • リストは以下のように空になっていても、空のCSSを設定し空振りするだけなのであまり気にしなくてもいい。(リスト追加するときにコンマ打つのがめんどうで書き溜めてもよい)

var idList = new Array('','','','','resources','bp4');

ついでに広めたいこと(メイン)

greasemonkeyユーザなら知っていて当然かもしれないけど、一応紹介したいことがある。
便利なスクリプトをどんどん追加していって数が多くなり、
管理が面倒になってないだろうか。
数行のコードのためにわざわざ新規追加するのに疑問を感じたはず。
それを是非まとめてほしい。というかまとめてスッキリした。

どうやってまとめるの?

// @include        http://japanese.engadget.com/*
// @include        http://www.nicovideo.jp/watch/*
// @include        http://dictionary.goo.ne.jp/

のようにドメインでフィルターをして

if (location.href.match(/japanese.engadget.com/))       {url = 'engadget'}
else if (location.href.match(/nicovideo.jp\/watch/))         {url = 'nicovideo'}
else if (location.href.match(/dictionary.goo.ne.jp/))        {url = 'goo_dictionary'}

switch (url){
    case 'engadget':

break;

のようにswitchして、ページごとに管理する。


注意点

@includeに http://* とか https://*を設定しないのは、セキュリティ面と管理しやすさを考えた。
ページを追加するときは、毎回ユーザスクリプトの管理画面で実行させたいページを追加する必要がある。
スクリプト中の@includeに書き忘れても、動作に影響はないが忘れずに追加することを勧める。


お試しインストール

life_script.user.js

なにこれ?

greasemonkeyスクリプト
個人的によくつかうページで実際に使っているサンプルを用意した。
上の方で作った広告削除機能を盛り込みつつ、入門者のテンプレートにもなるようにしたつもり。
見やすくインデントしたつもり。
公開しても恥ずかしくないようにしたつもり。

で結局どう使うの?

お試しインストールのソースコードを参考に説明します。

  1. firefox+greasemonkeyを導入しお試しインストールを済ます。
  2. スクリプト(例えば広告を削除したい)を実行したいページを見つける。
  3. firefoxの右下にある猿のアイコンを右クリックなりしてメニューを表示してユーザスクリプトの管理画面を開き、このスクリプトを選択しユーザスクリプトを実行するページに対象のURLを登録する。
  4. スクリプト上の// @includeにも登録しておく。
  5. 対象のURLに含まれるユニークな文字列を以下の部分に登録する。

else if (location.href.match(/ココ/)) {url = 'ココ'}

  1. スクリプトの一番したにテンプレートが用意してあるので、そこまでスクロール。テンプレートのコメントアウトをはずして、上で入力したurlと同じ文字列をcaseに入力する。
  2. そのテンプレートにidかclassかname属性いずれかを入力するだけで、広告を消すことができる。

主な効果を列挙

はて?

どんどんつっこんでください。いまはこれが正しいと思い込んでるから。
caseごと無名関数で包んでるけど効果あんの?
Stylishでやったら? 導入めんどい><