広告のプレビューをChrome Extensionで解決する

はじめまして。

medibaメディアシステム開発部の原と申します。

現在はPHPフレームワークを活用して新メディアの開発を行っておりますが、以前はモバイル向け広告配信システムの開発や運用を行っていました。

今回は、広告運用の中でよく出てくる課題について、Chrome Extensionで解決する方法をご紹介したいと思います。

掲載期日より前に、表示した状態のイメージを確認したいんだけど?

広告運用でよくある課題として、

掲載開始前にプレビューできない

ということがあります。

広告クリエイティブには世の中に出していない情報が含まれていることがあるため、モックと一緒にどこかにアップして確認という手段は採れないことが多いです。

ステージング環境もあるのですが、そもそも広告クリエイティブは直前まで差し替えが入ることもあるので、その度にいちいちステージング入稿して確認、では運用コストがかかり過ぎます。

そうだ、Chrome Extensionで本番環境の広告をローカル画像ファイルで(見た目だけ)上書きしよう!

前置きが長くなってしまいましたが、Chrome Extensionを使えば、画像ファイルをアップすることなく、さも掲載が開始されたかのような見た目を作れますよ、それで確認しちゃいましょう!というのが本稿の趣旨です。

Extensionの作成方針

さて、ではどのようなExtensionが必要でしょうか。

  • 画像をどこかにアップする仕組みは使わない
  • 本番環境なのでローカルに用意したスクリプトだけで実行可能
  • 誰でも使えるようなインターフェース

といったあたりが要件になってくると思います。 そこで、

  • popupでファイルをアップするフォームを提供
  • Web APIのFileReaderオブジェクトを利用してURLを作る
  • 広告のsrcを、作ったURLで書き換えて見た目の上書き

という設計方針を立ててみました。 ここまで決まればあとは作っていくだけです。

manifest.json

{
    "name": "auスマートパス バナーシミュレーター",
    "version": "1.0.0",
    "manifest_version": 2,

    "description": "auスマートパスのTOPバナーをプレビューするためのツールです。",

    "permissions": [
        "tabs",
        "http://*/*",
        "https://*/*"
    ],

    "browser_action": {
        "default_title": "バナーシミュレーター",
        "default_popup": "popup.html"
    }
}

ここでは特別なことはしていません。

ExtensionのポップアップHTMLから、元ページのソースを操作するのでpermissionsに"tabs"を設定していることと、アップロードフォーム用に"browser_action"の"default_popup"を設定していることがポイント。

画像アップロードフォーム(popup.html)

<div class="cmenu" id="banmenu">バナーファイル選択</div>
<div class="cbody">
    <input type="file" id="newbanner"></div>
<div class="preview" id="preview"><div class="innerpreview">preview</div></div>
<div class="bottom">
    <button id="sButton">変更する</button>
</div>

<script src="doPreview.js"></script>

CSSとかは割愛。 バナーを選択するためのinput要素と、プレビュー領域(divが2重になっているのは枠線を描くためで深い意味はありません)を用意しています。 最後に処理本体を記述するスクリプトをコールして終了。

本体Script(doPreview.js)

document.getElementById('newbanner').onchange = function() {
    var files = this.files;
    var imageType = /^image\//;

    for ( var i = 0; i = 0; i-- ) {
        previewImg.removeChild(previewImg.childNodes[i]);
    }

    previewImg.appendChild(img);

    var reader = new FileReader();
    reader.onload = ( function(aImg) { 
        return function (e) { 
            aImg.src = e.target.result;
        }; 
    })(img);
    reader.readAsDataURL(obj);
}

document.getElementById('sButton').onclick = function() {
    if ( document.getElementById('newbanner').value.length != 0 && !(!document.getElementById('banner'))) {
        changeTopBanner(document.getElementById('banner').src);
    }
}

function changeTopBanner(url) {
    chrome.tabs.executeScript(null, {
        "code": "var newBannerUrl = '"+url+"';"
    },function(){
        chrome.tabs.executeScript(null, { "file": "changeBanner.js" });
    });
}

長いですね。 内容的にはそれほど難しいことはしていないので、function単位で簡単に。

document.getElementById('newbanner').onchange = function() {
    var files = this.files;
    var imageType = /^image\//;

    for ( var i = 0; i = 0; i-- ) {
        previewImg.removeChild(previewImg.childNodes[i]);
    }

    previewImg.appendChild(img);

    var reader = new FileReader();
    reader.onload = ( function(aImg) { 
        return function (e) { 
            aImg.src = e.target.result;
        }; 
    })(img);
    reader.readAsDataURL(obj);
}

今回のキモその1。

渡された画像ファイルについてWeb APIのFileReaderオブジェクトを使って動的にプレビューする機能を提供しています。

FileReader.readAsDataURL()メソッドを利用して、画像ファイルのバイナリから、base64 エンコーディングされた data:URL文字列にすることが出来ます。 どこにもアップせずに画像ファイル自体を文字列としてimgタグのsrc属性にセットすることが出来るわけです。

参考 developer.mozilla.org

document.getElementById('sButton').onclick = function() {
    if ( document.getElementById('newbanner').value.length != 0 && !(!document.getElementById('banner'))) {
        changeTopBanner(document.getElementById('banner').src);
    }
}

ここは特記することはありません。 フォームのボタンを押したら元ページの画像差し替えFunctionを叩くよ、というだけ。

function changeTopBanner(url) {
    chrome.tabs.executeScript(null, {
        "code": "var newBannerUrl = '"+url+"';"
    },function(){
        chrome.tabs.executeScript(null, { "file": "changeBanner.js" });
    });
}

今回のキモその2。

ExtensionのpopupからロードされるScriptでは、元ページの要素にアクセスしたり操作したりということが出来ません。 そうした操作を行いたい場合はchrome.tabs.executeScriptメソッドを使います。 chrome.tabs.executeScriptを使用するとき、"code"で生JavaScriptを直接書くことも出来ますし、複雑な処理をしたい場合は、別ファイルにまとめて"file"で呼び出すことも出来ます。

またそれらを入れ子にすることで、元ページにJavaScript変数を定義した上で、外部ファイルで実行されるScriptから参照するなんてことも可能です。

今回はパラメータとしてURLを渡す必要があったのと、こういう書き方ができるよ、という意味合いでこの入れ子構造で書いてみました。

参考 developer.chrome.com

元ページ側で実行されるScript(changeBanner.js)

var d = document.getElementById('banner');
d.src = newBannerUrl;

差し替えたいバナーのDOMを取得する。 事前に渡した変数でsrc要素を書き換える。

以上!

今回の処理内容の場合、先ほどのchrome.tabs.executeScriptに直接コードを書いてしまってもいいのですが、例えばif文やループなどを使用したい場合は、ファイルに記述する必要がありますので覚えておくと便利です。

ともあれこれで完成です。 では実際に動作させてみましょう。

実行してみる

作ったExtensionをChromeに読み込ませる

「Chrome設定」→「拡張機能」→「デベロッパーツール」のチェックをオンにして先ほど作ったExtensionを読み込みます。 Extensionの導入にはセキュリティ上制限がある環境も多いかと思いますので、ここは皆様自身の環境のポリシーと相談してから実施してください。

ページを表示してExtensionを起動

プレビューしたいページをChromeに表示させてから、Extensionのアイコンをクリックして立ち上げます。 今回はアイコンを定義していないので、デフォルトのパズルピースですね。

フォームから画像を選択

プレビュー領域を確認し、差し替えを実行

フォームから画像を選択すると、プレビュー領域が自動で書き換わりますので、間違いないか確認した上で、「変更する」ボタンで差し替えを実行します。

ちなみにこの時Chromeデベロッパーツールで画像のソースを確認すると……

data:URL文字列で画像が表示されていることがわかります。

差し替えの確認

変更し、Extensionを閉じると……

このとおり、バナーが差し替わりました。

バナーの色味はイメージ通りでしょうか?

広告として適切な目立ちかたになっていますか?

思う存分プレビューしてください。

注意事項

上に紹介した各APIについて、ブラウザのバージョンによっては(といっても相当古いブラウザでもサポートしていますが)サポートされていなかったり動作しない可能性があります。 各リンク先に対応ブラウザのバージョン記載がありますので念のためご確認ください。 なお、本稿執筆にあたって動作確認をしたブラウザはChromeバージョン 46.0.2490.86になります。

また、FileReaderオブジェクトの仕様は現在W3C標準化過程のWorking Draft(WD, 草案・草稿の)の段階です。 今後仕様が変わることは十分にありえますのでご注意ください。下記に本稿執筆時点の最新ドラフトドキュメントをご紹介します。

参考 http://www.w3.org/

終わりに

今回の記事について、お気づきになった方もいるかと思いますが、スマートフォンの実機では利用できない仕組みになります。

というかモバイル版ChromeではExtension機能自体が実装されていませんし、その予定もないとのこと。

ですが、data:URL文字列でどこにもアップされていない画像を扱う、というキモだけおさえておけば、例えば画像をdata:URL文字列に変換するツールと、GETパラメータで画像のsrc属性を書き換えるJavaScriptを仕込んだモックを組み合わせることでスマートフォン実機でのプレビューも可能です。

結構面白い仕組みだと思うのでぜひ色々な活用の仕方を模索してみてください。