OXY NOTES

FuckAdBlockを利用して自由自在にAdblock対策をする方法

本格的にAdblock・Crystal対策するなら自作がお勧め

前回「WordPressで手軽にAdblock対策するならBetter Stop AdBlockで決まり!」という対策を紹介しました。

今回は更に発展させてWordPressのプラグインを使わずに、FuckAdBlockというスクリプトを利用して自前の対策をする方法を紹介します。
解説の都合上WordPressの例で紹介しますが、それ以外のサイトでもJavaScriptとPHPが動作すれば問題なく動作します。


目次


FuckAdBlockのダウンロードとWordPressへの導入

まずgithubにあるFuckAdBlockのページからzipファイルをダウンロードします。

WordPressのテーマフォルダに「js」というディレクトリを作成します。(この解説ではtwentytenの子テーマとしてtwentyten-childを利用している例で進めます)
その「\wp-content\themes\twentyten-child\js\」の中に解凍した「fuckadblock.js」を保存します。

続いて「functions.php」を開き、wp_enqueue_scriptsというアクションフックを利用してJavaScriptを登録します。

function my_scripts_method() {
	// fuckadblock.js用
	wp_enqueue_script( 'fuckadblock.js', '/wp-content/themes/twentyten-child/js/fuckadblock.js' );
}
add_action('wp_enqueue_scripts', 'my_scripts_method');

wp_enqueue_scriptsmy_scripts_methodという関数で追加するというよくある例です。

これでWordPressのheaderに「fuckadblock.js」が追加できました。

FuckAdBlockの使い方

githubにサンプルと解説がありますが、わりと控えめな解説なので補足を加えつつ解説します。
以下がFuckAdBlockの最も簡単な使用例です。

<script>
// Adblockが無効なときに実行される関数
function adBlockNotDetected() {
	alert('AdBlock is not enabled');
}
// Adblockが有効なときに実行される関数
function adBlockDetected() {
	alert('AdBlock is enabled');
}

// Adblock等が'fuckadblock.js'をブロックした場合と、
// 'fuckadblock'で正常に広告ブロックソフトを検知した場合の設定
if(typeof fuckAdBlock === 'undefined') {
	adBlockDetected();
} else {
	fuckAdBlock.onDetected(adBlockDetected);
	fuckAdBlock.onNotDetected(adBlockNotDetected);
}
</script>

13行目から18行目がAdblockを検知するコードです。

if(typeof fuckAdBlock === ‘undefined’)はfuckAdBlockという変数が存在するかを調べています。広告ブロックソフトがインストールされている場合はfuckAdBlockという文字列を無視するように設定されていることが多いので、フラグにしています。

fuckAdBlock.onDetected()はAdblock検出時に実行される関数を指定します。
fuckAdBlock.onNotDetected()は逆で、検出されない時に実行される関数を指定します。

これが基本形ですが、githubのサンプルではfuckAdBlock.on()を使った例も紹介されています。

fuckAdBlock.on()第1引数にtureを指定するとAdblockが有効な場合第2引数の関数を実行します。第1引数にfalseを指定するとAdblockが無効な場合に関数を実行します。

つまり、fuckAdBlock.on(true, adBlockDetected)fuckAdBlock.onDetected(adBlockDetected)と同じ。
fuckAdBlock.on(false, adBlockNotDetected)fuckAdBlock.onNotDetected(adBlockNotDetected)と同じ動作をします。

他にも繋げた例も紹介されていますが、上のどちらかを使うのがわかりやすいと思います。


FuckAdBlockのオプション

FuckAdBlockは予め適切なデフォルト値が設定されています。そのため、あまり使用することはありませんが、オプションで細かく動作を制御することができます。

デバックモード

デバックモードを有効にする。出力はconsoleに流れます。

debug: true

チェックメソッドの実行

trueで広告ブロックソフトの動作をチェックするためのループ(後述する)を実行する。falseにするとチェックするためのループを実行しない。

checkOnLoad: true

このオプションをfalseにしても、広告ブロックソフトをキャッチする仕組みは別にもあるので、正常にキャッチする可能性もある。

検証処理終了時にイベントをリセットする

具体的にはdetectedやnotDetectedイベントの開放。デフォルトはfalse。メモリ解放対策でしょうか、特別重い関数を登録する場合を除き、神経質になる必要はないと思います。

resetOnEnd: true

ループの時間を指定

ループの1回あたりの時間をミリ秒単位で指定(解説は下参照)。デフォルト50ms。

loopCheckTime: 50

ループの回数を指定

ループを実行する回数を指定。デフォルト5。

loopMaxNumber: 5

デフォルトは以下のとおりです。

Time (ms) = 50*(5-1) = 200ms (per default)

これは、FuckAdBlockがAdBlockは無効と判定するためのループです。

試しにデバックモードを有効にして、以下の設定で実行してみます。

fuckAdBlock.setOption({
	debug: true,
	loopCheckTime: 5000,
	loopMaxNumber: 5
});

するとconsoleに以下のように出力されます。

fuckadblock.js:57 [FuckAdBlock][setOption] The option "debug" he was assigned to "true"
fuckadblock.js:57 [FuckAdBlock][setOption] The option "loopCheckTime" he was assigned to "5000"
fuckadblock.js:57 [FuckAdBlock][setOption] The option "loopMaxNumber" he was assigned to "5"
fuckadblock.js:57 [FuckAdBlock][onload->eventCallback] A check loading is launched
fuckadblock.js:57 [FuckAdBlock][_creatBait] Bait has been created
fuckadblock.js:57 [FuckAdBlock][check] An audit was requested with a loop
fuckadblock.js:57 [FuckAdBlock][check] A check is in progress ...
fuckadblock.js:57 [FuckAdBlock][_checkBait] A check (1/5 ~1ms) was conducted and detection is negative
fuckadblock.js:57 [FuckAdBlock][_checkBait] A check (2/5 ~5001ms) was conducted and detection is negative
fuckadblock.js:57 [FuckAdBlock][_checkBait] A check (3/5 ~10001ms) was conducted and detection is negative
fuckadblock.js:57 [FuckAdBlock][_checkBait] A check (4/5 ~15001ms) was conducted and detection is negative
fuckadblock.js:57 [FuckAdBlock][_checkBait] A check (5/5 ~20001ms) was conducted and detection is negative
fuckadblock.js:57 [FuckAdBlock][_stopLoop] A loop has been stopped
fuckadblock.js:57 [FuckAdBlock][_destroyBait] Bait has been removed
fuckadblock.js:57 [FuckAdBlock][emitEvent] An event with a negative detection was called
fuckadblock.js:57 [FuckAdBlock][emitEvent] Call function 1/1
fuckadblock.js:57 [FuckAdBlock][clearEvent] The event list has been cleared

このように最初の1回目は即時に実行され、残りの4回が5秒間隔で実行されます。
最終的には約20秒経過後にAdblockが無効と判定し、無効時の関数を実行しました。

デフォルトでは5回のループで200ms後に無効時の関数を実行しています。
ページの読み込み順序や広告ブロックソフトのタイムラグ対策としてこのような仕組みになっているようです。

この時間を応用して関数にディレイをかけることも可能ですね。

Adblock用の餌を設定

特定のクラスとスタイルを餌に広告ブロックソフトを動作させる。この動作をループで指定した回数実行する。(広告ブロックソフトはこの設定を広告と勘違いしてブロックする)
スタイルを見てもらえば分かる通り、餌を設置する場所はブラウザで閲覧できる領域の遥か外なので、表示されることはありません。

baitClass: 'pub_300x250 pub_300x250m pub_728x90 text-ad textAd text_ad text_ads text-ads text-ad-links'
baitStyle: 'width: 1px !important; height: 1px !important; position: absolute !important; left: -10000px !important; top: -1000px !important;'

FuckAdBlockのメソッドの指定方法

オプションが1つの場合

fuckAdBlock.setOption(options, value);

複数ある場合は以下のようにする。

fuckAdBlock.setOption({
	debug: true,
	loopCheckTime: 1000,
	loopMaxNumber: 5
});

ループを停止する

デフォルトはtureなのでfalseにする。

fuckAdBlock.check(false);

falseにするとコンソールの表示が「An audit was requested with a loop」から「An audit was requested without loop」に変わり、ループは実行されない。

手動でAdblockが有効な状態をテストする

fuckAdBlock.emitEvent(true);

tureにするとAdblockが無効でも有効な状態の時に呼ばれる関数を実行できる。反対にfalseにすれば無効な状態の関数が呼び出される。

強制的に有効・無効をシミュレートして関数が実行される。

全てのイベントを無効にする

fuckAdBlock.clearEvent();

引数はない。onDetectedやonNotDetectedで実行されるイベントを無効にできる。例外ページを実装するには良いかも。


オーバーレイにメッセージを表示する方法

では実際にAnti Adblock等を使った時のように、白のオーバーレイでサイトを見えなくして、メッセージを表示してみます。

半透明のオーバーレイを実装する方法は沢山ありますが、今回はjQueryと組み合わせて使用するBlockUIを利用します。

BlockUIGitHubのページで配布されています。

BlockUIの使い方は詳細に解説しませんが、ものすごく親切なデモページで詳しく解説されているので参考にしてください。

FuckAdBlockの時と同じように「functions.php」にて登録します。fuckadblock.jsjquery.blockUI.jsは子テーマのjsディレクトリに保存しました。
jQueryはGoogleのCDNのものを利用します。

function my_scripts_method() {
	wp_deregister_script('jquery'); // 他のプラグイン等が内部のjQueryを読み込むのを阻止

	// jQuery用 GoogleのCDNを使う
	wp_enqueue_script( 'jquery.js', 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js' );

	// fuckadblock.js用
	wp_enqueue_script( 'fuckadblock.js', '/wp-content/themes/twentyten-child/js/fuckadblock.js' );

	// jQuery BlockUI Plugin用
	wp_enqueue_script( 'jquery.blockUI.js', '/wp-content/themes/twentyten-child/js/jquery.blockUI.js' );
}

add_action('wp_enqueue_scripts', 'my_scripts_method');

続いて「header.php」に以下のコードを追加します。

<script>
/* 
 * Adblockが無効な時の処理
 */
function adBlockNotDetected() {
	// 広告ブロックが無効な場合の処理
}

/* 
 * Adblockが有効な時の処理
 */
function adBlockDetected() {
	$(document).ready(function() { 
	    $.blockUI({
			message: '<ul><li>このサイトは広告費により運営されています。</li><li>広告ブロックを無効にしてください。</li></ul>', 
			css: { 
	            border: 'none', 
	            background: 'none', 
	            color: '#000' 
	        },
			overlayCSS: {
				backgroundColor: '#FFF',
				opacity: .95,
			}
		});
	});
}

/* 
 * fuckAdBlockでAdblockが有効・無効を分岐
 */
if(typeof fuckAdBlock === 'undefined') {
    adBlockDetected();
} else {
    fuckAdBlock.on(true, adBlockDetected);
    fuckAdBlock.on(false, adBlockNotDetected);
}
</script>

adBlockDetected()BlockUIを追加しました。
css:」はメッセージ用のCSS設定、「overlayCSS:」はオーバーレイ用のCSS設定になってます。

この設定でAdblockを有効にすると以下のように表示されます。


オーバーレイに画像を表示する方法

基本は上の例と同じです。
表示する画像は子テーマに「images」ディレクトリを作成して「neko.png」という名前で保存しました。
画像のパスを渡す必要があるので、get_stylesheet_directory_uri()を使って変数templatePathに代入しています。
bodyの子要素としてdisplayBoxという要素を追加し、そこにBlockUIで画像を埋め込んでいます。

<?php
	// ディレクトリのパスを取得(子テーマ用なのでget_stylesheet…を使用)
	echo '<script type="text/javascript">var templatePath = "' . get_stylesheet_directory_uri() . '";</script>';
?>

<script>
/* 
 * Adblockが無効な時の処理
 */
function adBlockNotDetected() {
	// 広告ブロックが無効な場合の処理
}

/* 
 * Adblockが有効な時の処理
 */
function adBlockDetected() {
	jQuery(function($){
		var displayBox = $('<div id="displayBox"><img src="' + templatePath + '/images/neko.png"></div>');
		$("body").prepend(displayBox); // bodyの子要素として直後に追加

		$.blockUI({
		    message: $('#displayBox'),
		    css: {
		        top:  ($(window).height() - 382) /2 + 'px',
		        left: ($(window).width() - 600) /2 + 'px',
		        width: '600px'
		    },
			overlayCSS: {
				backgroundColor: '#FFF',
				opacity: .95,
			}
		});
	});
}

/* 
 * fuckAdBlockでAdblockが有効・無効を分岐
 */
if(typeof fuckAdBlock === 'undefined') {
    adBlockDetected();
} else {
    fuckAdBlock.on(true, adBlockDetected);
    fuckAdBlock.on(false, adBlockNotDetected);
}
</script>

画像のファイルサイズによって、top: ($(window).height() – 382) /2 + ‘px’の「382」を変更します。こちらは高さ。$(window).width() – 600の方は幅を指定します。

以上の設定でAdblockが有効時に以下のように表示されます。

猫画像を使いたいという方は「前回の投稿」でいくつかのパターンを紹介しているので、自由に使ってください。


Adblockユーザーと、一般ユーザーのカウントを取得する方法

どれくらい広告をブロックしているユーザーがいるのか把握したいという需要もあると思います。
通常なんらかのページビューをカウントするにはGoogle Analyticsイベント機能を使うのが最適なのですが、Adblockは何の意図があるのかわかりませんが、Analyticsのコードもブロックするのでイベントは使えません
そこで、独自にカウントする仕組みを実装する必要があります。

今回はできるだけ処理を軽く、汎用性を保たせるために、JSON形式のファイルにカウントを書き込む形式にしました。
WordPressを利用しているのでデータベースへ書き込むというのも一つの方法です。

カウント用のJSONファイルを作成

まず以下の内容のカウント用のファイル「fab_count.json」を作成します。

{"Detected":0,"NotDetected":0}

作成したら子テーマのディレクトリへ保存します。

JSONファイルを更新するためのPHPファイルを作成

JavaScriptではJSONファイルを上書きできないのでカウントを書き込むPHPファイル「fab_count.php」を作成します。

<?php

// headerから送られたカウントをJSON形式でファイルに書き出す
define( 'COUNT_FILE', dirname(__FILE__ ) . "/fab_count.json" );

$arg = array();
$arg["Detected"] = (int) $_POST["Detected"];
$arg["NotDetected"] = (int) $_POST["NotDetected"];
$jsonData = json_encode( $arg );

file_put_contents( COUNT_FILE, $jsonData, LOCK_EX);

上のファイルと同じように子テーマのディレクトリへ保存します。

headerにカウント用と画像表示用のコードを追加

header.php」を開いて、上の画像表示用のコードを残しつつ、カウント用のコードを追加します。

<?php
	// ディレクトリのパスを取得(子テーマ用なのでget_stylesheet…を使用)
	echo '<script type="text/javascript">var templatePath = "' . get_stylesheet_directory_uri() . '";</script>';
?>

<script>
/* 
 * Adblockが無効な時の処理
 * fab_count.phpへPOSTすることでfab_count.jsonへカウントを行う
 */
function adBlockNotDetected() {
	jQuery(function($){
		$.getJSON( templatePath + "/fab_count.json", function( json ){
			var newNotDetected = json.NotDetected;
			newNotDetected++; // Adblockが無効な場合のカウントをインクリメント
			$.ajax({
				type: "POST",
				url: templatePath + "/fab_count.php",
				data: { "Detected": json.Detected, "NotDetected": newNotDetected },
		        success: function( data ) 
		        {
		            // alert("ok");
		        },
		        error: function( XMLHttpRequest, textStatus, errorThrown ) 
		        {
		            alert( 'Error : ' + errorThrown );
		        }
			});
		});
	});
}

/* 
 * Adblockが有効な時の処理
 * fab_count.phpへPOSTすることでfab_count.jsonへカウントを行う
 * 更にfuckAdBlockでオーバーレイして、画像を表示
 */
function adBlockDetected() {

	jQuery(function($){

		$.getJSON( templatePath + "/fab_count.json", function( json ){
			var newDetected = json.Detected;
			newDetected++; // Adblockが有効な場合のカウントをインクリメント
			$.ajax({
				type: "POST",
				url: templatePath + "/fab_count.php",
				data: { "Detected": newDetected, "NotDetected": json.NotDetected },
		        success: function(data) 
		        {
		            // alert("ok");
		        },
		        error: function(XMLHttpRequest, textStatus, errorThrown) 
		        {
		            alert('Error : ' + errorThrown);
		        }
			});
		});

		var displayBox = $('<div id="displayBox"><img src="' + templatePath + '/images/neko.png"></div>');
		$("body").prepend(displayBox); // bodyの子要素として直後に追加

		$.blockUI({
		    message: $('#displayBox'),
		    css: {
		        top:  ($(window).height() - 382) /2 + 'px',
		        left: ($(window).width() - 600) /2 + 'px',
		        width: '600px'
		    },
			overlayCSS: {
				backgroundColor: '#FFF',
				opacity: .95,
			}
		});
	});
}

/* 
 * fuckAdBlockでAdblockが有効・無効を分岐
 */
if(typeof fuckAdBlock === 'undefined') {
    adBlockDetected();
} else {
    fuckAdBlock.on(true, adBlockDetected);
    fuckAdBlock.on(false, adBlockNotDetected);
}
</script>

$.getJSONでJSONファイルを取得して、$.ajaxfab_count.phpにカウントを渡しています。

これでAdblockが有効な場合はDetectedがカウントされ、一般ユーザーはNotDetectedとしてカウントされます。

大量のアクセスがあるサイトで負荷を軽減する方法

これだと全てのアクセスをカウントするので、大規模サイトの場合問題が発生するかもしれません。
そんな時はインクリメントの部分をサンプリングして書き込むようにすると負荷対策ができます。

var num = Math.floor( Math.random() * 10 ) + 1;
if( 1 === num ){
	var newNotDetected = json.NotDetected + 10;
	$.ajax({
		type: "POST",
		url: templatePath + "/fab_count.php",
		data: { "Detected": json.Detected, "NotDetected": newNotDetected },
        success: function( data ) 
        {
            // alert("ok");
        },
        error: function( XMLHttpRequest, textStatus, errorThrown ) 
        {
            alert( 'Error : ' + errorThrown );
        }
	});
}

1~10の値をランダムで取得して、1の場合だけカウントを10増やして書き込むという処理です。
この対策をするとカウントは10人単位になります。規模や許容する負荷を加味して数字は変えてください。


Anti-Adblock Killer対策と雑感

今回はFuckAdBlockを利用しましたが、同じく有名な対策としてAnti-Adblockがあります。
しかしAnti-Adblockは有名すぎて、対策のためのAnti-Adblock Killerなるスクリプトが開発されています。ブラウザに導入するにはTampermonkeyをインストールして読み込むことで、Anti-Adblockを導入したサイトでも閲覧することができますそれはFuckAdBlockでも同じです。
わざわざ指摘するまでもなく、イタチごっこです。

Anti-Adblock Killerのソースを見てみたところ「FuckAdBlock」という文字列をフラグに阻止しているだけのようです。試しに「FuckAdBlock」から「FAB」へ置き換えたところAnti-Adblock Killerを無効化できました。
テキストエディタで置換処理をすれば簡単に回避できます。プラグインにして配布する場合でも、名前にソルトを付ければ回避することはできます。

ただ「そこまでして広告を非表示で閲覧したいユーザーを排除する意味があるのか」という根本的な疑問も出てきます。

Adblockユーザーには真っ白のオーバーレイを表示することで、「Adblockを入れると見たいWebサイトが正常に見られないからAdblockをやめよう」という、非暴力不服従の態度を示すのが一番の気がします。

ちなみに、すでに骨格はできているのでWordPress用のプラグインを作ることは可能です。もし需要があるようなら作成します。