JavaScriptなんかでこそこそ広告ブロックしてみたけど

前回の続き。

いろいろやってみたが、結局面倒になってきた。

SCRIPT要素をブロック

下のスクリプト (拡張子は.jsで保存) を使えば、SCRIPT要素で実行するJavaScriptがすべて無効になるので、もうそれでいいかな、と思う。

  • kill-scripts.js
(function(){
  window.opera.addEventListener('BeforeScript',function(){
    event.preventDefault();
  },false);
})();

これのいいところは、他のユーザーJSはSCRIPT要素になるわけではないので、ちゃんと実行されるということ。ブックマークレットも大丈夫。

ただし、ユーザーJSやブックマークレットの中でappendChild('script')とかしたらブロックされてしまう。

他にも、onclick="window.open"とかいう場合にもページ側のスクリプトは実行されてしまうので、完璧ではない。


document.writeを消しても、XMLHttpRequestで広告を読んでinnerHTMLで書き出すようなところ (http://www.nikkei.co.jp/news/main/ とか http://eow.alc.co.jp/opera/UTF-8/ とか) では無力なので、それもどうにかしようとしていたら、結局「広範囲に使える」ではなくて個別に書かないといけないっぽかったので、発想の転換でやっぱりOperaのurlfilter.iniを使うのがいいのではないかと思ってきた。

ページが読み込むスクリプトを表示

Operaのurlfilter.iniは画像やフラッシュをブロックするのには分かり易くていいのだが、テキスト広告に対してはソースを見てURLを調べるなりしないといけないので面倒だ。

下を使えば、ロード時に読み込んだ外部スクリプト (src属性のあるSCRIPT要素のソースURL) を全部教えてくれるので、あやしいやつは全部urlfilter.iniに突っ込んでしまうのがいい。

  • show-external-scripts.js
(function(){
  var scripts = [];
  window.opera.addEventListener('BeforeExternalScript',function(){
    scripts.push(event.element.src);
  },false);

  document.addEventListener('load',function(){
    alert(scripts.join("\n"));
  },false);
})();

こんな感じで。

やっぱりこのぐらい簡潔なほうが広告ブロックはいいと思う。

他に作ったもの

ちゃんとそれなりの動作をすると思うけど、自分では使う気がなくなったのでちゃんと検証されてないかもしれないものを公開しておく。捨てるのももったいないし、後で自分へのメモとしても。

外部スクリプトをブロック
  • kill-external-scripts.js
(function() {
  if (window == window.top) {
    var d = document.domain;
    if (d.match(/^\w+\.\w+\./)) { //if there are more than two fullstops
      d = d.replace(d.match(/^\w+\./), ''); //remove first word
    }
    d += '/';

    window.opera.addEventListener('BeforeExternalScript',function() {
      if (event.element.src.indexOf(d) < 0) {
        event.preventDefault();
      }
    },false);
  }
})();

外部というのは、表示中のぺージのドメインの一部がスクリプトのソースURLに入っていないとき。

正確には、ドメインに1つしかピリオドがない場合は、そのドメインの最後にスラッシュを付けた文字列 (example.comならexample.com/という文字列) 、ドメインに2つ以上ピリオドのある場合は、最初のピリオドまでを除いて最後にスラッシュを付けたもの (www.example.comならexample.com/という文字列) を見ている。

例えばwww.example.comというページでは、advertisement.com/は外部だが、example.com/は外部ではなく、ads.example.com/も外部ではない。しかし、advertisement.com/ad?site=example.comというのも外部ではないと判断してしまう。

ロードイベント前のdocument.writeを殺す
  • kill-document_write.js
(function(){
  document._write = document.write;
  document.write = Function();

  document.addEventListener('load',function(){
    document.write = document._write;
    document._write = null;
  },false);
})();
ロードイベント前のdocument.writeとXMLHttpRequestを殺す
(function(){
  document._write = document.write;
  document.write = Function();
  window._XMLHttpRequest = window.XMLHttpRequest;
  window.XMLHttpRequest = Function();

  document.addEventListener('load',function(){
    document.write = document._write;
    document._write = null;
    window.XMLHttpRequest = window._XMLHttpRequest;
    window._XMLHttpRequest = null;
  },false);
})();

これは弊害も多いと思うので、特定サイトを@includeすればいいと思う。

ただ、そんなことをいちいちするぐらいなら、show-loading-scripts.jsで一覧表示して消すかな。

ロードイベント前の外部スクリプトによるdocument.writeを殺す
  • kill-external-document_write.js
(function() {
  document._write = document.write;

  if (window == window.top) {
    var d = document.domain;
    if (d.match(/\.\w+\./)) {
      d = d.replace(d.match(/\w+\./), '');
    }
    d += '/';

    window.opera.addEventListener('BeforeExternalScript',function() {
      if (event.element.src.indexOf(d) < 0) {
        document.write = Function();
      }
    },false);

    window.opera.addEventListener('AfterScript',function() {
      document.write = document._write;
    },false);
  }

  document.addEventListener('load',function() {
    document._write = null;
  },false);
})();