Warning: Cannot modify header information - headers already sent by (output started at /home/unrzeitl/public_html/webmake.info/wp-config.php:1) in /home/unrzeitl/public_html/webmake.info/wp-includes/feed-rss2.php on line 8
うぇぶまけ/webmake https://webmake.info WebマーケティングとWeb制作に関わる情報や、個人的な趣味の映画鑑賞・書籍の情報を書き溜めていく備忘録 Thu, 07 Mar 2019 15:27:06 +0000 ja hourly 1 https://wordpress.org/?v=5.1.1 KGI・KPIの意味は?ビジネスの目標設定に欠かせない指標を設定しよう https://webmake.info/kgi%e3%83%bbkpi%e3%81%ae%e6%84%8f%e5%91%b3%e3%81%af%ef%bc%9f%e3%83%93%e3%82%b8%e3%83%8d%e3%82%b9%e3%81%ae%e7%9b%ae%e6%a8%99%e8%a8%ad%e5%ae%9a%e3%81%ab%e6%ac%a0%e3%81%8b%e3%81%9b%e3%81%aa%e3%81%84/ https://webmake.info/kgi%e3%83%bbkpi%e3%81%ae%e6%84%8f%e5%91%b3%e3%81%af%ef%bc%9f%e3%83%93%e3%82%b8%e3%83%8d%e3%82%b9%e3%81%ae%e7%9b%ae%e6%a8%99%e8%a8%ad%e5%ae%9a%e3%81%ab%e6%ac%a0%e3%81%8b%e3%81%9b%e3%81%aa%e3%81%84/#respond Mon, 04 Jul 2016 05:58:21 +0000 http://webmake.info/?p=820 KGIとは

「Key Goal Indicator(キーゴールインジゲーター)」の略で、日本語に訳すと「重要目標達成指標」となる。

「Goal」と入っているとおり、KGIは最終目標が達成されているかを計測するための指標のことです。

KGIは曖昧な指標では意味を成さないため、誰でも公平に判断できるよう時期と具体的な数値を設定し、明確な判断基準としておくことが基本となります。

そのため定性的な情報ではなく、定量的な指標を用い、振り返りのタイミングで達成しているかどうかがひと目でわかるものを指標とします。

KGIの例

  • 「来期の売上を1.5倍にする」
  • 「来月に新規顧客を100名獲得する」
  • 「xという作業時間を半分に削減する」

KPIとは

「Key Performance Indicator(キーパフォーマンスインジゲーター)」の略で、日本語に訳すと「重要業績評価指標」となる。

設定したKGIに対してどのような指標を通貨すれば達成可能かどうかを洗い出し、その過程をクリア出来ているかどうかを数値で計測するのが「KPI」です。

KGIからKPIを作成する

KGI

KPIとはKGIを達成するために確かと思われる評価基準です。

目標到達のために必要な要素は単一で無いことが多いため、ほとんどのケースでは1つのKGIに対し、複数のKPIが含まれます。

KPIの例

KGI「3ヶ月後までに体重を5kg落とす」

だったとすれば、マイルストーンとして確かであろうものをKPIとして置きます。

仮に「体重の減量グラフが代謝の向上により、2次関数的な弧を描く」という仮説があるのであれば

KPI「1ヶ月後までに体重を1kg落とす」

という仮設定が出来ます。

KPIがそれぞれ洗い出すことができれば、そのKPIを達成するために必要であると考えられるアクションをタスクリストとして構築し、日々実行をしていくことでKGIが達成できるようにしていきます。

アクションの例

KPI「1ヶ月後までに体重を1kg落とす」

ために必要なアクションとして、下記などが挙げられます。

  • 毎日30分ウォーキングをする
  • 毎日30回腕立て伏せを実行する
  • 毎日30回腹筋をする

また、これらのアクションも実行の可否について振り返りが容易な指標にしておく必要があります。

KGI・KPIなどのアクションはSMARTに

目標はシンプルに、スマートにしましょう。

と言葉だけを聞くと概念論かと思いがちですが、この場のSMARTは少し意味街が異なります。

SMARTとは

KPIの目標達成のために必要な5要素の頭文字を取ったものである。

  • Specific(明確にわかるか)
  • Measurable(計測ができるか)
  • Achievable(達成可能性があるか、無謀ではないか)
  • Result-oriented or Relevant(結果が出るか。または 大目標と関連性があるか)
  • Time-bound(期限が明確か)

これらの要素に欠けるKPIやアクションプランを設定してしまうと

  • 達成したかどうかわからない
  • 達成していないが、終了期限がないのでいつまでもチャレンジ状態
  • 達成したけど問題解決に至らない

などの状態が発生してしまいます。

そうならないように、KPIやアクションプランはSMARTに沿って当てはめられるように設定をしましょう。

上記例にあてはめると

KPI「1ヶ月後までに体重を1kg落とす」
  • S(明確か?) : 1kg
  • M(計測出来るか?): 体重計に乗ればわかる
  • A(達成可能か?) : 1ヶ月1kgは現実的に可能
  • R(関連性があるか?): 大目標5kgの減量に繋がっている
  • T(期限が明確か?) : 1ヶ月
アクション「毎日30分ウォーキングをする」
  • S(明確か?) : 30分
  • M(計測出来るか?): 時計を持って外出すればOK
  • A(達成可能か?) : 1日30分歩くだけなので可能
  • R(関連性があるか?): 親目標1kgの減量に繋がる
  • T(期限が明確か?) : 1日

といったように当てはめられる。

KPIの考え方

KPIとは「人を動かすもの」であると考えています。

達成すべき指標であるのは間違いないのですが、達成するための道程が全く分からないような指標では効力がありません。(SMARTのAに違反しているケース)

KPIとは、それを設定することで、明日からするべきことがわかり、アクションに繋げられる必要があるものだと考えています。

KPIの数について

KPIの数はいくつ。といったように明確には決められていません。

しかし、多ければよいというものでもありません。

「あれも、これも。」と設定してしまうと、本当に大切な指標が埋もれてしまい大目標の達成に紐付いていないKPIが生まれてしまうケースも珍しくありません。

そうならないためにKPIツリーを作っていくことをオススメしています。

KPIツリーを作ろう

KGIとKPIの関連が正しいか、過不足はないか。ということを確認しながらKPIを設定できるKPIツリーを作りましょう。

KPIツリーを作ることで、KGIから大きく外れたものをKPI設定することが少なくなるでしょう。

KPIツリーを作る上でのポイント

1. MECEにこだわりすぎない

KPIツリーはロジック・ツリーとは異なり、KGIを達成する要素の抽出が目的です。

そのため、完璧なMECEの構造を求め、こだわりすぎないことが大切です。

1施策が複数のKPIにしか影響することは珍しくありません。

柔軟にKGIを捉え、KPIを正しく設定していきましょう。

2. 細かくしすぎない

上述の通り、KPIという指標は更に「サブKPI」というものを設定し、分解しようとすればいくらでも細かく出来ます。

しかし、KPIを細かくし過ぎると、アクション時に何故そのKPIを負っているのかわからなくなってしまう危険性を持っています。

3. 完璧を求めない

KPIツリーに完成はありません。
月日が経ったり状況が変わったりすると、それぞれの課題が変わっていきます。

当然それに併せてKGIも変化することもあるでしょう。

「完璧なKPIツリーを1度で作り上げられるかどうか。」ではなく、「アクションをした結果をKPIツリーにフィードバックできるか。」といったことのほうが大切です。

KPIを用いた業務改善ももちろんですが、KPIツリー自体にも改善を施していきましょう。

 

ECサイトのKPIツリー例

かんたんにECサイトのKPIツリー例を作成してみました。

ECサイトの売上

KPIツリーは事業者それぞれで変わってきますし、指標も1つではありません。

例えば「購入者数」というのを上記では「訪問数×転換率」で表していますが、「新規購入者+リピート購入者」やその他の指標で表すことも可能です。

どのように定義するかは、事業の特性やKGIの状態によっても異なってくると思います。
ここを考え尽くすのがカイゼンのもっとも近道なので、しっかりと考えていきましょう。

 

KPIツリーができたら

KPIツリーが一旦完成したら、複数のKPIが存在していると思います。

それぞれのKPIに対してSMARTと思われるアクションを考えて、タスク化・実行をしていきましょう。

上記ツリーはサンプルなので、ツリー自体に数値などは持たせていませんが、持たせるのであれば

KGI

「来月のECサイトの売上を110%にする」

KPI

購入者数をx人増やす
購入商品数を120%にする

アクション

カート落ち率を0.5%改善する
カートページ下部にレコメンデーションコンテンツを追加する

のような形などが考えられると思います。

しっかりと現状の課題を見つめ、KGIに紐付いたKPIを設定してカイゼンを回していきましょう!

]]>
https://webmake.info/kgi%e3%83%bbkpi%e3%81%ae%e6%84%8f%e5%91%b3%e3%81%af%ef%bc%9f%e3%83%93%e3%82%b8%e3%83%8d%e3%82%b9%e3%81%ae%e7%9b%ae%e6%a8%99%e8%a8%ad%e5%ae%9a%e3%81%ab%e6%ac%a0%e3%81%8b%e3%81%9b%e3%81%aa%e3%81%84/feed/ 0
gulp-frontnoteでファイルリンク一覧が生成されないトラブルの解決方法 https://webmake.info/gulp-frontnote%e3%81%a7%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e3%83%aa%e3%83%b3%e3%82%af%e4%b8%80%e8%a6%a7%e3%81%8c%e7%94%9f%e6%88%90%e3%81%95%e3%82%8c%e3%81%aa%e3%81%84%e3%83%88%e3%83%a9%e3%83%96/ https://webmake.info/gulp-frontnote%e3%81%a7%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e3%83%aa%e3%83%b3%e3%82%af%e4%b8%80%e8%a6%a7%e3%81%8c%e7%94%9f%e6%88%90%e3%81%95%e3%82%8c%e3%81%aa%e3%81%84%e3%83%88%e3%83%a9%e3%83%96/#respond Sun, 15 May 2016 11:06:59 +0000 http://webmake.info/?p=801 FrontNoteとは
frontNote スクリーンショット

frontNote スクリーンショット

LIGのCTOである林先生が作ったスタイルガイドジェネレータ。
Githubのアイコンかわいい。

スタイルガイドとは

簡単に説明すると、デザインの仕様書です。
エンジニアにとって馴染みの深いスタイルガイドの代表として、Bootstrapが挙げられます。

複数人での開発にはぜひ導入して置いたほうが良いと言えるもの。
(これがないとオレオレCSSが量産され、地獄を見ることも・・・)
ナレッジの共有を視覚的に図れるので開発効率の向上にもつながる。

FrontNote以外の有名なスタイルガイドジェネレータ

StyleDocco
http://jacobrask.github.io/styledocco/

KSS
http://warpspire.com/kss/

トラブルが発生した環境

$ node -v
v0.10.40

$ npm -v
1.4.28

$ gulp -v
CLI version 1.2.1
Local version 3.9.1

CLIでビルドしていないので、関係ないですが、念の為。

gulpfile.jsのサンプル

今回は課題解決にフォーカスし、最低限のgulpfileにて検証しました。

var gulp = require("gulp"),
    watch = require('gulp-watch'),
    frontNote = require("gulp-frontnote");
var debug = require('gulp-debug');

var sassDir = "./sass/scss/";

function docs() {
  gulp.src(sassDir + '**/*.scss')
    .pipe(frontNote())
    .pipe(debug({title: "debug:"}));
}

gulp.task("docs", docs);

gulp.task('default', function() {
  gulp.watch(sassDir + '**/*.scss',["docs"]);
});

問題の概要

初回の書き出しは、全ての対象ファイルのスタイルガイドが正常に生成される。

frontnote-filelist-enable

正しくファイル一覧が表示されている場合

しかし、2回目以降、gulp.watchでファイルを編集したトリガーでの生成の場合、編集した対象ファイルのみの生成となり、サイドバーのファイルリンク一覧が消えてしまう。

scss/cssファイルを編集し、gulpが動くとファイル一覧が消えてしまう

scss/cssファイルを編集し、gulpが動くとファイル一覧が消えてしまう

上記のような不具合が発生していた。

トラブル解決方法

  • gulp-debug
  • console.logコマンド

などで地味にトレースした結果、下記のような流れで処理が行われていた。

  1. gulp.wacthで対象ファイルの更新検知
  2. docs関数をコール
  3. gulp.srcのMiniMatchパターンに適合
  4. frontNote関数をコール

パッと見たところ、MiniMatchパターンに適合しているファイルはすべてfrontNoteをコールしていたので、gulpfile側は問題ないと考えられる。

frontNoteの内部実装をチェック

gulpfileに問題はないようで、詰まってしまったので内部実装をチェックすることに。

gulp-frontnode/index.js

  1. ストリームでファイル一覧を取得
  2. frontnoteのrenderに投げる

frontnote/lib/frontnote.js

  1. ファイルを1ファイルずつ全て読み込む
  2. スタイルガイドを生成するべきファイルをparserで抽出
  3. スタイルガイド生成処理
  4. ごにょごにょして生成完了!

おおよそ上記の流れだったのですが、課題である「ファイルリンク一覧が消える」という問題は「スタイルガイドを生成するべきファイルをparserで抽出」で起こっていました。

ファイルリンク一覧の生成方法

ファイルリンクの生成方法は「frontnote/template/index.ejs」を確認するとすぐわかりました。

<nav class="fn-sidebar">
        <a href="./index.html" class="fn-menu<%if(overview){%> fn-is-active<%}%>">Overview</a>
        <!-- 全ファイル -->
        <% files.forEach(function(file) { %> // この行が鍵
        <!-- スタイルガイドへのリンク -->
        <a href="<%- file.url %>" class="fn-menu<%if(helpers.isCurrent(current,file)){%> fn-is-active<%}%>">
            <% if (file.overview) { %>
                <%- file.overview.title %>
            <% } else { %>
                <%- file.fileName %>
            <% } %>
        </a>
        <% }) %>
    </nav>

filesという変数の中身をループ処理しているのですが、上記の「スタイルガイドを生成するべきファイルを抽出」という部分でそもそも「files」の中身が変更した1ファイルのみになっていたのです。

その処理を行っている実体ファイルは「frontnote/lib/parser/parser.js」でした。

var Parser = function(){};
Parser.prototype = {
    parse: function(file,content,useCache) {
        if (useCache) {
            var cacheData = cache.get(file);
            if (cacheData === content) {
                return null;
            }
            cache.put(file,content);
        }
        var overview = content.match(PATTERNS.overview);
        if (overview) {
            overview = this.parseComments(overview);
            if (overview) {
                overview = overview[0];
            }
        }
        var colors = content.match(PATTERNS.colors);
        if (colors) {
            colors = this.parseColors(colors);
        }
        var comments = content.match(PATTERNS.comment);
        if (comments) {
            comments = this.parseComments(comments);
        }
        if(overview || comments || colors) {
            var fileName = path.basename(file,path.extname(file));
            var relPath = path.relative(__dirname, path.dirname(file));
            if (relPath) {
                relPath = relPath.replace(/\.\.\//g,'').replace(/\.\.\\/g,'').replace(/\//g,'-').replace(/\\/g,'-') + '-';
            }
            return {
                file: file,
                fileName: fileName,
                url: relPath + fileName + '.html',
                dirs: file.split(path.sep),
                ext: path.extname(file),
                sections: comments,
                overview: overview,
                colors: colors
            };
        }
        return null;
    },

どうやらキャッシュを使う設定にしてあると、生成ファイル一覧から除外されるようです。
負荷のかからない良い対応だと思いますが、ファイルリンクが消えてしまうのは痛い。。

おそらく相当数利用されているプロダクトだと思う割に、同様の事例が見受けられなかったので、そもそも私の検証・利用環境がおかしいのかもしれません。。。

対応・解決方法

どうやらキャッシュが課題だということが判明したので、解決法は簡単です。
gulpfile.jsのfrontNote呼び出し部分にオプションとして

  • キャッシュを無効化する
  • ファイルを毎回生成する

のいずれかを設定してやれば、全てのファイルを走査するようになり、リンク一覧が正常に生成されます。

function docs() {
  gulp.src(sassDir + '**/*.scss')
    .pipe(frontNote({
      cache: false, // こっちはキャッシュの無効
      clean: true,  // こっちで毎回削除→生成
    }))
    .pipe(debug({title: "debug:"}));
}

以上で、初回出力時と同様、きれいに全てのファイルが生成されるようになりました。

最後に

今回、社内のデザイナーからお手上げ案件ということで調査・対応依頼が回ってきて対応しました。
普段、フロントエンドのプロダクトはそこまで深く触っていないものなので、若干苦戦しましたが楽しく取り組めました。

何より、このような素敵なスタイルガイドジェネレータを提供してくれている林先生には感謝です。

ぜひみなさんもスタイルガイドジェネレータを利用して、社内のコード統一・健全化に努めてはいかがでしょうか。

 

]]>
https://webmake.info/gulp-frontnote%e3%81%a7%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e3%83%aa%e3%83%b3%e3%82%af%e4%b8%80%e8%a6%a7%e3%81%8c%e7%94%9f%e6%88%90%e3%81%95%e3%82%8c%e3%81%aa%e3%81%84%e3%83%88%e3%83%a9%e3%83%96/feed/ 0
睡眠効率を向上させるアイマスク「NeuroOn」を購入したので開封&レビュー https://webmake.info/%e7%9d%a1%e7%9c%a0%e5%8a%b9%e7%8e%87%e3%82%92%e5%90%91%e4%b8%8a%e3%81%95%e3%81%9b%e3%82%8b%e3%82%a2%e3%82%a4%e3%83%9e%e3%82%b9%e3%82%af%e3%80%8cneuroon%e3%80%8d%e3%82%92%e8%b3%bc%e5%85%a5%e3%81%97/ https://webmake.info/%e7%9d%a1%e7%9c%a0%e5%8a%b9%e7%8e%87%e3%82%92%e5%90%91%e4%b8%8a%e3%81%95%e3%81%9b%e3%82%8b%e3%82%a2%e3%82%a4%e3%83%9e%e3%82%b9%e3%82%af%e3%80%8cneuroon%e3%80%8d%e3%82%92%e8%b3%bc%e5%85%a5%e3%81%97/#respond Wed, 11 May 2016 13:46:41 +0000 http://webmake.info/?p=785 Kickstarterで出資を募り、製品開発に至った睡眠改善アイマスク「NeuroOn」がついに到着しました。
せっかくなので、簡単にレビューをしてみます。

NeuroOnとは

インテリジェント睡眠用アイマスク」という良くわからない最新プロダクト。

公式 https://neuroon.jp/

睡眠時の脳波、心拍、体温、体の動きを計測したり、光を使った起床を促したり、バイオリズムを調整したり。
「なんかわからないけど、すごそう。」
そんなプロダクトですw

発注から到着まで

確かKickstarterが始まった時、TechCrunchに取り上げられていた記憶があります。
その記事を見て、すぐに予約注文をしました。

かなりの歳月が過ぎ、ようやく「2016-05-09」に手元に届きました。
マイページを見ると、注文日は「2015-01-14」となっていました。

1年4ヶ月も待っていたのですね…

開封の儀

外装

Neuro:on DHL

DHLが配送業者です。
海外からの発送のため、関税の「2,380円」が受け取り時にかかりました。

梱包

Neuro:on 外箱

袋の中には、ダンボールで包まれたNeuroOnが!

開梱

Neuro:on 化粧箱

|ω・`)ノ ヤァ
綺麗な化粧箱です。

Neuro:on 開封

さらに開くと、そこにはアイマスクが!

Neuro:on 背

なんとなく高級感がありますね。

アイマスク本体

Neuro:on アイマスク本体

アイマスク自体は、普通のアイマスクです。
若干ゴーグルのようなつけ心地です。

謎の電子部品

Neuro:on 本体

これが本体ですね。この金属端子から脳波などを検知するのでしょうか。

Neuro:on アイマスク側部

アイマスクのバンド部分はマジックテープで固定する方式なので、自分のサイズに合わせて調整することが可能です。

NeuroOnの機能

モバイルアプリと連携し、

  • レム睡眠の時間
  • 眠りに落ちるまでの時間
  • 目覚めた回数

などを計測し、0~100の数値で表す「睡眠スコア」なるものを算出します。

起床時間を設定し、その時間に「光」と「振動」で起床を促す。

マスクの着用感

ゴーグルのような形でフィット感はよい。
ただ、金属端子なのでしばらくの間は冷たく感じるw
肌が弱い人とかにはどうなんでしょうか…?

アプリの使用感

アプリの完成度はまだ低い?
Androidのアプリ名には「Beta」ってまだついていますし…。
症状としては、ペアリングが切れたり、データ転送の時間が結構掛かったりします。

電池について

充電はおよそ40分程度。
給電方法はMicroUSB。
1日使うとバッテリー残量が30%程度になっていたので、基本的に毎日充電が必要かと思います。

アイマスク外れに関して

長さはマジックテープで無段階切り替えが出来るため、自分にあった長さに調節できる。
圧迫感などはなく調節できるのはいいですね。

そんなNeuroOnが欲しい方は

公式サイトで購入が行えます。
2016-05-11 時点で、発注から10週間で届くようです。

購入はこちらから→ https://neuroon.jp/#buy-now

今すぐ欲しいという人は…

割高にはなりますが、Amazonで販売されているようです。

[amazonjs asin=”B00SATHV5A” locale=”JP” title=”neuro:on 安眠アイマスク ニューローン 2時間睡眠で目覚めスッキリ!魔法の快眠アイマスク”]

公式サイトで買った場合と金額比較

  • 本体 $299
  • 送料 $27
  • 関税 2,380円

なので、1ドル108円とした場合、37,588円となります。

いまは動作確認のみを行った中古品が39,800円であるので、10週間を2,000円で買う感じになるようです。

参考までにどうぞ。

]]>
https://webmake.info/%e7%9d%a1%e7%9c%a0%e5%8a%b9%e7%8e%87%e3%82%92%e5%90%91%e4%b8%8a%e3%81%95%e3%81%9b%e3%82%8b%e3%82%a2%e3%82%a4%e3%83%9e%e3%82%b9%e3%82%af%e3%80%8cneuroon%e3%80%8d%e3%82%92%e8%b3%bc%e5%85%a5%e3%81%97/feed/ 0
Adwords Scriptで日次レポートをメール送信するスクリプトを書いた https://webmake.info/adwords-script%e3%81%a7%e6%97%a5%e6%ac%a1%e3%83%ac%e3%83%9d%e3%83%bc%e3%83%88%e3%82%92%e3%83%a1%e3%83%bc%e3%83%ab%e9%80%81%e4%bf%a1%e3%81%99%e3%82%8b%e3%82%b9%e3%82%af%e3%83%aa%e3%83%97%e3%83%88/ https://webmake.info/adwords-script%e3%81%a7%e6%97%a5%e6%ac%a1%e3%83%ac%e3%83%9d%e3%83%bc%e3%83%88%e3%82%92%e3%83%a1%e3%83%bc%e3%83%ab%e9%80%81%e4%bf%a1%e3%81%99%e3%82%8b%e3%82%b9%e3%82%af%e3%83%aa%e3%83%97%e3%83%88/#comments Tue, 10 May 2016 11:09:22 +0000 http://webmake.info/?p=738 「書いた」と言っても、公式のサンプルソリューションにあるスクリプトを改変したものになるんですけどね。

 

このAdwordsスクリプトを使って得られるもの

前日のAdwordsアカウント成果の概要をメールで自動送信することができます。
情報を見に行く手間が省けるため、

  1. チームで成果を共有出来る。
  2. 習慣化しやすい

というのが最も大きいメリットだと思います。

 

このAdwordsスクリプトを作った理由

公式のサンプルソリューションのスクリプトを改変して、しばらくの間運用していたのですが、少しずつ課題が出てきたためです。

また、先日Adwordsスクリプト使おうぜ!みたいな記事を書いた手前、実際のサンプル的なものも上げとかないといけない気分になったからですw

日々、リスティング広告の運用をしていると、同じようなことを反復することがよくあると思います。 毎週x曜日に○○レポートをダウンロー...

公式のAdwords Scriptの機能

サンプルソリューションとして提供されているアドワーズスクリプトが提供する機能は下記のとおりです。

  • 日次で”アカウント”の下記要素をGoogleスプレッドシートに記録する
    • コスト
    • 平均クリック単価(CPC)
    • クリック率(CTR)
    • 平均掲載順位
    • 表示回数(Impression)
    • クリック数
  • htmlメールで「前日」「前々日」「7日前」のデータを送信する

このスクリプトを毎日朝8時などに自動で起動するようにしておけば、前日のおおよそのアカウント健全度がわかるようになっています。

予算が少なく、検索数も少ないようなアカウントなどで、最低限の予算管理をしたい場合、このスクリプトを使うだけで、管理画面に入る手間を減らせるメリットがあります。

アカウント単位という弊害

アカウント全体の予算感を把握するだけなら、サンプルスクリプトで何ら問題がありませんでした。
ですが、アカウントによっては「平均掲載順位」や「CTR」の数値を追っておきたいものも少なくありません。

しかし、平均掲載順位やCTR、Impressionなどは「検索連動広告」と「ディスプレイ広告」で全く違うデータになってしまいます。
つまり、1アカウント内で検索連動とディスプレイ両方に出稿しているアカウントにおいて、平均掲載順位やCTRのデータは”正しい”といえる平均値ではないものが返って来てしまうことが課題として噴出しました。

公式スクリプトからの改変点

公式で紹介されているアドワーズスクリプトから、下記の点を改変しております。

表示項目の追加

表示項目に

  • コンバージョンに至ったクリック
  • CPA

を追加。

ECなどの場合、コンバージョンの値を取得し、ROASを計算しても面白いと思います。

グラフの変更・追加

要素の追加に合わせて、スプレッドシートにグラフの追加。
また、小規模アカウントは日によってグラフの数値が変動しすぎるため、7日の移動平均でのグラフ描画に変更。

コンバージョンが0の場合、CPAが0円になるのも気持ち悪いので、正確ではないですが、CVが0の場合、Cost=CPAとなるようにしています。
CV=0が多発するアカウントの場合、調整したほうが良いかと思います。グラフ自体はスプレッドシートのただのグラフなので、編集は容易かと思います。

データをネットワークごとに分割

「アカウントすべてのデータ」を「ネットワークごと」に分割することで、検索のデータとディスプレイネットワークのデータを分けて表示するように変更。

メールにグラフデータを追加

メールで概要把握が出来るのは素晴らしかったのですが、細かい数字の変動を見てもよくわからないので、上記の移動平均グラフをメールに添付するように変更。

以上、4点の改変を加えております。

日次レポートスクリプトの使い方

1.Googleスプレッドシートをテンプレートから作成

下記URLからテンプレートであるスプレッドシートを開き「コピーを作成」を選択。

2.スプレッドシートの編集

「コピーを作成」で、自分のGoogleドライブに保存したスプレッドシートの編集を行います。

screenshot_01

ファイル名を変更。

screenshot_02

わかりやすいファイル名に変更しておいたほうが良いでしょう。
名前はなんでもかまいません。

メール送信機能を利用する場合、メールの宛先を登録。

screenshot_03

メールの宛先をスプレッドシートに記入します。
CCも登録出来ますので、複数人に送る場合にご利用ください。

また、CCが複数人いる場合、カラム内にカンマで区切ってメールアドレスを入力ください。

例)example.a@test.com,example.b@test.com

3.Google Adwordsのスクリプト作成

[Adwords > アカウント > 一括処理 > スクリプト] のスクリプトの作成と管理をクリック。

screenshot_04

「+スクリプト」ボタンを押して、新規スクリプト作成画面へ。

screenshot_05

下記スクリプトをコピー&貼り付け。

// スプレッドシートのURLを入力して下さい
var SPREADSHEET_URL = "";

var settings = {
  "Summary" : {
    "sheet_name": "Summary",    //スプレッドシートの検索+ディスプレイ結果シート名
  },  
  "Search Network" : {
    "sheet_name": "Search Network", //スプレッドシートの検索連動広告結果シート名
    "graphs" : {
      "impression" : "ここにグラフ画像(検索連動広告・表示回数)のURLを入力",
      "click" : "ここにグラフ画像(検索連動広告・クリック数)のURLを入力",
      "ctr" : "ここにグラフ画像(検索連動広告・CTR)のURLを入力",
      "avg_cpc" : "ここにグラフ画像(検索連動広告・CPC)のURLを入力",
      "avg_position" : "ここにグラフ画像(検索連動広告・平均掲載順位)のURLを入力",
      "cost" : "ここにグラフ画像(検索連動広告・費用)のURLを入力",
      "conversion_click" : "ここにグラフ画像(検索連動広告・コンバージョンクリック)のURLを入力",
      "cpa": "ここにグラフ画像(検索連動広告・CPA)のURLを入力"
    }
  },
  "Display Network" : {
    "sheet_name": "Display Network",    //スプレッドシートのディスプレイ広告結果シート名
    "graphs" : {
      "impression" : "ここにグラフ画像(ディスプレイ・表示回数)のURLを入力",
      "click" : "ここにグラフ画像(ディスプレイ・クリック数)のURLを入力",
      "ctr" : "ここにグラフ画像(ディスプレイ・CTR)のURLを入力",
      "avg_cpc" : "ここにグラフ画像(ディスプレイ・CPC)のURLを入力",
      "cost" : "ここにグラフ画像(ディスプレイ・費用)のURLを入力",
      "conversion_click" : "ここにグラフ画像(ディスプレイ・コンバージョンクリック)のURLを入力",
      "cpa": "ここにグラフ画像(ディスプレイ・CPA)のURLを入力"
    },
  }
};

// グラフの横幅
var graph_w = 400;

// 日付を明示指定する場合入力。通常は空欄でOK。
var start_date = "";
var end_date = "";
var last_check_date = "";

/**
* メイン関数
*/
function main() {
  var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  // スプレッドシートのアカウントIDをAdwordsのアカウントIDで上書き
  spreadsheet.getRangeByName("account_id_report").setValue(AdWordsApp.currentAccount().getCustomerId());

  var email = spreadsheet.getRangeByName("email").getValue();
  var email_cc = spreadsheet.getRangeByName("email_cc").getValue();

  // データの最終更新日をサーチ・ディスプレイそれぞれスプレッドシートから取得する
  last_check_date = spreadsheet.getRangeByName("last_check").getValue();

  var check_date = getStartDate(start_date, last_check_date);
  var finish_date = getEndDate(end_date);

  var results = {
    "Search Network" : [],
    "Display Network" : [],
    "Summary" : []
  };

  // 開始日から終了日までループさせ、キャンペーンのデータを取得する
  while(check_date.getTime() <= finish_date.getTime()) {
    var report_data = getReportRowForDate(check_date);
     
    for(k in report_data) {
      results[k].push([new Date(check_date), report_data[k]['Cost'], report_data[k]['AverageCpc'], report_data[k]['Ctr'],
                       report_data[k]['AveragePosition'], report_data[k]['Impressions'], report_data[k]['Clicks'], report_data[k]['CostPerConversion'], report_data[k]['Conversions']]);
    }
    // 日付を進める
    check_date.setDate(check_date.getDate() + 1);
  }
   
  for(k in settings){
    Logger.log(results[k]);
    // スプレッドシートのシートをシート名で取得
    var access = new SpreadsheetAccess(SPREADSHEET_URL, settings[k]["sheet_name"]);
    // 空白行を走査し、書き込めるかどうかをチェックする
    var emptyRow = access.findEmptyRow(6, 2);
    if (emptyRow < 0) {
      access.addRows(results[k].length);
      emptyRow = access.findEmptyRow(6, 2);
    }
    // 空白行にデータを書き込む
    access.writeRows(results[k], emptyRow, 2);
  }

  // メールの送信先が定義されていればレポートメールを送信する
  if (email) {
    sendEmail(email,email_cc);
  }

  // スプレッドシートの最終取得日を更新する
  spreadsheet.getRangeByName("last_check").setValue(finish_date);
}

/**
* データの取得開始日を取得する。
* 優先度: 1.明示指定した開始日。 2.スプレッドシートの最終更新日の翌日。 3.昨日
*/
function getStartDate(start_date, last_check_date) {
  var date = "";
  // 最終更新日が未定義なら昨日、定義済みなら最終日の翌日からデータ取得を開始する
  if(start_date !== "") {
    date = new Date(start_date);
  } else {
    if (last_check_date === "") {
      date = new Date(getYesterday());
    } else {
      date = new Date(last_check_date);
      date.setDate(date.getDate() + 1);
    }
  }
  return date;
}
  
/**
* データの取得終了日を取得する。
* 優先度: 1.明示指定した終了日。 2.昨日
*/
function getEndDate(end_date) {
  var date = "";
  if(end_date !== "") {
    date = new Date(end_date);
  } else {
    date = new Date(getYesterday());
  }
  return date;
}
  
/**
* 指定日のレポートデータを取得する
*/
function getReportRowForDate(date, network) {
  var accountDate = new Date(Utilities.formatDate(date, "PST", "MMM dd,yyyy HH:mm:ss"));
  var dateString = Utilities.formatDate(accountDate, "PST", "yyyyMMdd");
  return getReportRowForDuring(dateString + "," + dateString);
}
  
/**
* レポートデータを取得し、それぞれの配列に追加する
* @param during 取得指定範囲日
*/
function getReportRowForDuring(during) {
  // AWQLで取得データを定義。https://developers.google.com/adwords/api/docs/guides/awql
  var report = AdWordsApp.report(
    "SELECT AdNetworkType1, Cost, AverageCpc, Ctr, AveragePosition, Impressions, Clicks, CostPerConversion, Conversions " +
    "FROM ACCOUNT_PERFORMANCE_REPORT " +
    "DURING " + during);
   
  var rows = report.rows();
  var results = {};
  var networks = [];
  while (rows.hasNext()) {
    var row = rows.next();
    networks.push(row["AdNetworkType1"]);
    try {
      results[row["AdNetworkType1"]] = {};
      results[row["AdNetworkType1"]]["Ctr"] = row["Ctr"];
      results[row["AdNetworkType1"]]["AverageCpc"] = row["AverageCpc"];
      results[row["AdNetworkType1"]]["AveragePosition"] = row["AveragePosition"];
      results[row["AdNetworkType1"]]["Impressions"] = toInteger(row["Impressions"]);
      results[row["AdNetworkType1"]]["Clicks"] = toInteger(row["Clicks"]);
      results[row["AdNetworkType1"]]["Cost"] = toInteger(row["Cost"]);
      results[row["AdNetworkType1"]]["Conversions"] = toInteger(row["Conversions"]);
      results[row["AdNetworkType1"]]["CostPerConversion"] = toInteger(row["CostPerConversion"]);
    } catch (ex) {}  
  }
   
  // ネットワークすべての値を加算して、サマリーを算出する
  if(networks.length > 0) {
    results["Summary"] = {"Impressions": 0, "Clicks": 0, "Cost": 0, "Conversions": 0};
    for(k in networks) {
      results["Summary"]["Impressions"] += results[networks[k]]["Impressions"];
      results["Summary"]["Clicks"] += results[networks[k]]["Clicks"];
      results["Summary"]["Cost"] += results[networks[k]]["Cost"];
      results["Summary"]["Conversions"] += results[networks[k]]["Conversions"];
    }
    if(results["Summary"]["Impressions"] > 0 && results["Summary"]["Clicks"]) {
        results["Summary"]["Ctr"] = parseFloat(results["Summary"]["Clicks"] / results["Summary"]["Impressions"] * 100).toFixed(2) + "%";
        results["Summary"]["AverageCpc"] = parseFloat(results["Summary"]["Cost"] / results["Summary"]["Clicks"]).toFixed(2);
        if(results["Summary"]["Conversions"] > 0){
          results["Summary"]["CostPerConversion"] = toInteger(results["Summary"]["Cost"] / results["Summary"]["Conversions"]);
        } else {
          results["Summary"]["CostPerConversion"] = 0;
        }
    } else {
        results["Summary"]["Ctr"] = 0;
        results["Summary"]["AverageCpc"] = 0;
        results["Summary"]["CostPerConversion"] = 0;
    }
  }
   
  return results;
}
  
/**
* レポートメールの送信を行う。
*/
function sendEmail(email,email_cc) {
  // 前日データを取得
  var target_day = getYesterday();
  var yesterday_report = getReportRowForDate(target_day);
  // 一昨日データ(前日の1日前)を取得
  target_day.setDate(target_day.getDate() - 1);
  var two_days_ago_report = getReportRowForDate(target_day);
  // 先週データ(2日前の5日前)を取得
  target_day.setDate(target_day.getDate() - 5);
  var week_ago_report = getReportRowForDate(target_day);
  
  var yesterday = getYesterday();
  var format_day = yesterday.getFullYear() + '/' + (yesterday.getMonth() + 1) + '/' + yesterday.getDate();
     
  // 画像を取得し、Blob化する(インライン画像埋め込みのため)
  var s_impression_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["impression"]).getBlob().setName('s_impression_blob');
  var s_click_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["click"]).getBlob().setName('s_click_blob');
  var s_ctr_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["ctr"]).getBlob().setName('s_ctr_blob');
  var s_avg_cpc_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["avg_cpc"]).getBlob().setName('s_avg_cpc_blob');
  var s_avg_pos_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["avg_position"]).getBlob().setName('s_avg_pos_blob');
  var s_cost_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["cost"]).getBlob().setName('s_cost_blob');
  var s_conversion_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["conversion_click"]).getBlob().setName('s_conversion_blob');
  var s_cpa_blob = UrlFetchApp.fetch(settings["Search Network"]["graphs"]["cpa"]).getBlob().setName('s_cpa_blob');
   
  var d_impression_blob = UrlFetchApp.fetch(settings["Display Network"]["graphs"]["impression"]).getBlob().setName('d_impression_blob');
  var d_click_blob = UrlFetchApp.fetch(settings["Display Network"]["graphs"]["click"]).getBlob().setName('d_click_blob');
  var d_ctr_blob = UrlFetchApp.fetch(settings["Display Network"]["graphs"]["ctr"]).getBlob().setName('d_ctr_blob');
  var d_avg_cpc_blob = UrlFetchApp.fetch(settings["Display Network"]["graphs"]["avg_cpc"]).getBlob().setName('d_avg_cpc_blob');
  var d_cost_blob = UrlFetchApp.fetch(settings["Display Network"]["graphs"]["cost"]).getBlob().setName('d_cost_blob');
  var d_conversion_blob = UrlFetchApp.fetch(settings["Display Network"]["graphs"]["conversion_click"]).getBlob().setName('d_conversion_blob');
  var d_cpa_blob = UrlFetchApp.fetch(settings["Display Network"]["graphs"]["cpa"]).getBlob().setName('d_cpa_blob');
  
  var html = [];
  html.push(
    "<html>",
      "<body>",
    "<h1><a href='" + SPREADSHEET_URL + "'>" + AdWordsApp.currentAccount().getName() + " - アカウントレポート</a></h1>",
        "<table width=800 cellpadding=0 border=0 cellspacing=0>",
          "<tr>",
            "<td colspan=2 align=right>",
              "<div style='font: italic normal 10pt Times New Roman, serif; margin: 0; color: #666; padding-right: 5px;'>Powered by AdWords Scripts</div>",
            "</td>",
          "</tr>",
          "<tr bgcolor='#3c78d8'>",
            "<td width=500>",
              "<div style='font: normal 18pt verdana, sans-serif; padding: 3px 10px; color: white'>サマリー</div>",
            "</td>",
            "<td align=right>",
              "<div style='font: normal 14pt verdana, sans-serif; padding: 3px 10px; color: white'>", AdWordsApp.currentAccount().getName() + "<br>" + AdWordsApp.currentAccount().getCustomerId(), "</div>",
            "</td>",
            "</tr>",
          "</table>",
          "<table width=800 cellpadding=0 border=0 cellspacing=0>",
            "<tr bgcolor='#ddd'>",
              "<td></td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5px 5px; background-color: #ddd; text-align: left'>前日</td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5px 5px; background-color: #ddd; text-align: left'>一昨日</td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5x 5px; background-color: #ddd; text-align: left'>先週</td>",
            "</tr>",
            emailRow('Cost', 'Cost', yesterday_report["Summary"], two_days_ago_report["Summary"], week_ago_report["Summary"]),
            emailRow('Average Cpc', 'AverageCpc', yesterday_report["Summary"], two_days_ago_report["Summary"], week_ago_report["Summary"]),
            emailRow('Ctr', 'Ctr', yesterday_report["Summary"], two_days_ago_report["Summary"], week_ago_report["Summary"]),
            emailRow('Impressions', 'Impressions', yesterday_report["Summary"], two_days_ago_report["Summary"], week_ago_report["Summary"]),
            emailRow('Clicks', 'Clicks', yesterday_report["Summary"], two_days_ago_report["Summary"], week_ago_report["Summary"]),
            emailRow('CostPerConversion', 'CostPerConversion', yesterday_report["Summary"], two_days_ago_report["Summary"], week_ago_report["Summary"]),
            emailRow('Conversions', 'Conversions', yesterday_report["Summary"], two_days_ago_report["Summary"], week_ago_report["Summary"]),
        "</table>",
        "<hr />",
          "<table width=800 cellpadding=0 border=0 cellspacing=0>",
          "<tr bgcolor='#3c78d8'>",
            "<td colspan=2>",
              "<div style='font: normal 18pt verdana, sans-serif; padding: 3px 10px; color: white'>検索</div>",
            "</td>",
            "</tr>",
          "</table>",
          "<table width=800 cellpadding=0 border=0 cellspacing=0>",
            "<tr bgcolor='#ddd'>",
              "<td></td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5px 5px; background-color: #ddd; text-align: left'>前日</td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5px 5px; background-color: #ddd; text-align: left'>一昨日</td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5x 5px; background-color: #ddd; text-align: left'>先週</td>",
            "</tr>",
            emailRow('Cost', 'Cost', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
            emailRow('Average Cpc', 'AverageCpc', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
            emailRow('Ctr', 'Ctr', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
            emailRow('Average Position', 'AveragePosition', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
            emailRow('Impressions', 'Impressions', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
            emailRow('Clicks', 'Clicks', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
            emailRow('CostPerConversion', 'CostPerConversion', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
            emailRow('Conversions', 'Conversions', yesterday_report["Search Network"], two_days_ago_report["Search Network"], week_ago_report["Search Network"]),
              "</table>",
        "<h2>グラフ</h2>",
        "<img src='cid:s_impression_graph' width='"+ graph_w +"' border='2'> ",
        "<img src='cid:s_click_graph' width='"+ graph_w +"' border='2'><br>",
        "<img src='cid:s_ctr_graph' width='"+ graph_w +"' border='2'> ",
        "<img src='cid:s_avg_cpc_graph' width='"+ graph_w +"' border='2'><br>",
        "<img src='cid:s_avg_pos_graph' width='"+ graph_w +"' border='2'> ",
        "<img src='cid:s_cost_graph' width='"+ graph_w +"' border='2'><br>",
        "<img src='cid:s_conversion_graph' width='"+ graph_w +"' border='2'> ",
        "<img src='cid:s_cpa_graph' width='"+ graph_w +"' border='2'><br>",
        "<hr />",
          "<table width=800 cellpadding=0 border=0 cellspacing=0>",
          "<tr bgcolor='#3c78d8'>",
            "<td colspan=2>",
              "<div style='font: normal 18pt verdana, sans-serif; padding: 3px 10px; color: white'>ディスプレイ</div>",
            "</td>",
            "</tr>",
          "</table>",
          "<table width=800 cellpadding=0 border=0 cellspacing=0>",
            "<tr bgcolor='#ddd'>",
              "<td></td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5px 5px; background-color: #ddd; text-align: left'>前日</td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5px 5px; background-color: #ddd; text-align: left'>一昨日</td>",
              "<td style='font: 12pt verdana, sans-serif; padding: 5px 0px 5x 5px; background-color: #ddd; text-align: left'>先週</td>",
            "</tr>",
            emailRow('Cost', 'Cost', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
            emailRow('Average Cpc', 'AverageCpc', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
            emailRow('Ctr', 'Ctr', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
            emailRow('Average Position', 'AveragePosition', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
            emailRow('Impressions', 'Impressions', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
            emailRow('Clicks', 'Clicks', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
            emailRow('CostPerConversion', 'CostPerConversion', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
            emailRow('Conversions', 'Conversions', yesterday_report["Display Network"], two_days_ago_report["Display Network"], week_ago_report["Display Network"]),
              "</table>",
        "<h2>グラフ</h2>",
        "<img src='cid:d_impression_graph' width='"+ graph_w +"' border='2'> ",
        "<img src='cid:d_click_graph' width='"+ graph_w +"' border='2'><br>",
        "<img src='cid:d_ctr_graph' width='"+ graph_w +"' border='2'> ",
        "<img src='cid:d_avg_cpc_graph' width='"+ graph_w +"' border='2'><br>",
        "<img src='cid:d_cost_graph' width='"+ graph_w +"' border='2'><br>",
        "<img src='cid:d_conversion_graph' width='"+ graph_w +"' border='2'> ",
        "<img src='cid:d_cpa_graph' width='"+ graph_w +"' border='2'><br>",
      "</body>",
    "</html>");
  MailApp.sendEmail({
    to: email,
    cc: email_cc,
    subject: "【" + AdWordsApp.currentAccount().getName() + "】" + "AdWords日次レポート " + format_day+ " (" +AdWordsApp.currentAccount().getCustomerId() + ") ",
    htmlBody: html.join("\n"),
    inlineImages:
    {
      s_impression_graph: s_impression_blob,
      s_click_graph: s_click_blob,
      s_ctr_graph: s_ctr_blob,
      s_avg_cpc_graph: s_avg_cpc_blob,
      s_avg_pos_graph: s_avg_pos_blob,
      s_cost_graph: s_cost_blob,
      s_conversion_graph: s_conversion_blob,
      s_cpa_graph: s_cpa_blob,
      d_impression_graph: d_impression_blob,
      d_click_graph: d_click_blob,
      d_ctr_graph: d_ctr_blob,
      d_avg_cpc_graph: d_avg_cpc_blob,
      d_cost_graph: d_cost_blob,
      d_conversion_graph: d_conversion_blob,
      d_cpa_graph: d_cpa_blob
    }
  });
}
function emailRow(title, column, yesterdayRow, twoDaysAgoRow, weekAgoRow) {
  return "<tr> \
 <td style='padding: 5px 10px'>" + title + "</td> \
 <td style='padding: 0px 10px'>" + yesterdayRow[column] + "</td> \
 <td style='padding: 0px 10px'>" + twoDaysAgoRow[column] + formatChangeString(yesterdayRow[column], twoDaysAgoRow[column]) + "</td> \
 <td style='padding: 0px 10px'>" + weekAgoRow[column] + formatChangeString(yesterdayRow[column], weekAgoRow[column]) + "</td> \
 </tr>";
}
/**
* タイムゾーンを考慮した昨日を取得する
*/
function getYesterday() {
  var now = new Date(Utilities.formatDate(new Date(),
                  AdWordsApp.currentAccount().getTimeZone(), "MMM dd,yyyy HH:mm:ss"));
  var yesterday = new Date(now.getTime() - 24 * 3600 * 1000);
  yesterday.setHours(12);
  return yesterday;
}
  
/**
* 前日対比などを計算し、要素に合わせて整形する
*/
function formatChangeString(newValue,  oldValue) {
  newValue += "";
  oldValue += "";
  var x = newValue.indexOf('%');
  if (x != -1) {
    newValue = newValue.substring(0, x);
    var y = oldValue.indexOf('%');
    oldValue = oldValue.substring(0, y);
  }
 
  newValue = toInteger(newValue);
  oldValue = toInteger(oldValue);

  var change = parseFloat(newValue - oldValue).toFixed(2);
  var changeString = change;
  if (x != -1) {
    changeString = change + '%';
  }
   
  if (change >= 0) {
    return "<span style='color: #38761d; font-size: 8pt'> (+" + changeString + ")</span>";
  } else {
    return "<span style='color: #cc0000; font-size: 8pt'> (" + changeString +  ")</span>";
  }
}
/**
* スプレッドシートにアクセスし、データを書き込む
*/
function SpreadsheetAccess(spreadsheetUrl, sheetName) {
  this.spreadsheet = SpreadsheetApp.openByUrl(spreadsheetUrl);
  this.sheet = this.spreadsheet.getSheetByName(sheetName);
   
  // what column should we be looking at to check whether the row is empty?
  this.findEmptyRow = function(minRow, column) {
    var values = this.sheet.getRange(minRow, column, this.sheet.getMaxRows(), 1).getValues();
    for (var i = 0; i < values.length; i ++) {
      if (!values[i][0]) {
        return i + minRow;
      }
    }
    return -1;
  }
  this.addRows = function(howMany) {
    this.sheet.insertRowsAfter(this.sheet.getMaxRows(), howMany);
  }
  this.writeRows = function(rows, startRow, startColumn) {
    this.sheet.getRange(startRow, startColumn, rows.length, rows[0].length).setValues(rows);
  }
}
  
/**
 * 文字列を数値化する
 */
function toInteger(value) {
  try {
    // 空文字を追加し、stringに変換する
    value += "";
    return parseInt(value.split(",").join(""), 10)
  } catch (ex) {
    Logger.log(ex + ":" + value);
    return 0;
  }
}

screenshot_06

2017/04/11 更新
「コンバージョンに至ったクリック」系のAPI削除を受けてコード微調整。
2016/06/10 更新

メールタイトルの日付が1週前の日付を返してしまっていたので修正。

 

4.スプレッドシートのURLを設定

1行目のスプレッドシートURLを、2でコピーしたスプレッドシートURLに修正する。

screenshot_07

5.グラフ関連の画像URLを取得

スプレッドシートで[ファイル > ウェブに公開]を選択。

screenshot_08

リンクタブで公開するドキュメントを指定できます。
公開ドキュメントにあたるグラフには、シート別にそれぞれ

  • Cost
  • CPC
  • CTR
  • Clicks
  • AverageCPC
  • Average Position
  • CV
  • CPA

と名前がついています。

今回、メールに掲載するグラフは

検索連動広告・ディスプレイ広告のグラフすべて。

なので、「Search Network」「Display Network」のシート内のグラフすべてを公開設定に変更します。

screenshot_11

この時、インタラクティブと画像の2種類が選択できますが、「画像」のURLをコピーしてください。

公開を押すと、公開URLが発布されます。screenshot_12
その公開URLをAdwords Scriptのsettings欄にそれぞれ記入していきます。

screenshot_16

 

6.スクリプトに名前をつける

ここまでできれば最低限の設定は完了です。
せっかくなので、スクリプトに名前をつけましょう。

名前自体は何でも構いません。
今回はわかりやすく「アカウント日次レポート」としました。

screenshot_17

7.プレビュー

続いて、実際に稼働するかプレビューを実行します。
プレビューを実行すると「承認が必要です」と実行許可を求められます。

screenshot_18

承認をしないとスクリプトは動かないので、「今すぐ許可」を押して承認をします。

screenshot_19

ここでの許可は、「スプレッドシートへのアクセス許可」や「メール送信許可」「自動でこのプログラムが動いて良いかの許可」などであり、私などの外部の人間がアクセス出来る許可ではないので、安心して承認してください。

問題がなければ30秒程度でプレビューが完了します。
「プレビュー」といっても、スクリプトは実際に稼働しているので、

  1. スプレッドシートにデータの追記
  2. メールの送信

が実行されています。

プレビュー機能とは、Adwordsアカウントに影響をもたらすような(入札単価の変更、広告の開始・停止など)スクリプトの場合、実際にアカウントに変更をもたらさない機能です。

スプレッドシート

screenshot_21

メール

screenshot_22

データは実際に動かしているアカウントなので、詳細はマスクします。
グラフ部分が表示されていないのは、7日の移動平均データのグラフを採用しているからです。

運用額が小さく、コンバージョンが無い日もあるようなアカウントを短日グラフで追ってしまうと、起伏が激しすぎて本来グラフとして読み取りたいデータが読み取れなかったためです。

実際に運用を続けると下記のようなグラフに変わっていくと思います。

screenshot_29

動かない場合は…。コメント欄に情報を添えて記入いただければわかるかもしれません。

8.実行予約

問題なくプレビューで動作した場合、Adwords Scriptの自動実行を登録します。
このスクリプトは昨日のデータ集計を行うものなので、日が変わった後に実行します。

screenshot_24

screenshot_27

0時や1時などを指定してしまうと、データの反映が終わっていない場合がありますので、5時以降に設定することをおすすめします。

これで設定は完了です。

番外編:過去データを取得する

当スクリプトの基本機能は

  1. 最終データ取得から、昨日のデータを取得し、スプレッドシートに転記する
  2. 昨日のデータと一昨日、1週間前のデータを比較し、メール通知する

というものです。

基本的に毎日稼働し始めれば、キレイに回るのですが、過去データをスプレッドシートに転記したいと思うと、少し手を加える必要があります。

開始日・終了日の設定

通常の動作においては空欄にするべき項目ですが、過去データを日時指定してデータ取得する場合のみに利用するオプションです。

screenshot_28

画像の例では、2014年2月6日から運用開始しているアカウントでしたので、そこから現在までのデータを取得しようと試みています。
現在までなのに、なぜ2015年1月31日が終了日になっているかは次の実行時間制限があるからです。

Adwords Scriptの実行時間制限

Adwordsスクリプトには1スクリプト「30分」という実行時間の制限があります。

数年間のデータを取得にはどうしても時間がかかり、制限である30分を超える可能性が大いにあります。
30分を超えると、処理が打ち切られ、データの転記が出来ません。

そこで、約1年程度に期間を制限して取得しています。

これを複数回繰り返すことで、過去数年のデータもスプレッドシートに転記することが出来ます。

長期的なデータをあつめ、分析すると、いろいろと分かることも増えてきます。

1年で「平均掲載順位が横ばいなのに、CPCが1.8倍程度になっている」ようなアカウントも競合の参入が激しい分野では珍しくありません。

きちんとデータを日々追って、正しい施策を打てるようにしていきたいですね。

当スクリプトの補足事項

当スクリプトには、いろいろとオレオレ仕様があります。

  1. 小規模アカウントで凸凹グラフになり過ぎないよう、CVが0件時のCPAはCV=1と同義としてグラフを描く
  2. CVR関係はまったく指標に載せていない

などです。

CVRを載せない理由

リスティングを運用している人なら、誰しもがわかっていることだと思いますが、コンバージョンは「コンバージョンした日」ではなく「広告をクリックした日」につきます。

つまり、

  1. 5月1日に広告クリック
  2. サイトを訪問し、類似サイトと比較のためブックマークに入れてサイトを離脱
  3. 5月4日にブックマークからサイトに訪れコンバージョン

上記ケースの場合、コンバージョンデータは5月1日に付与されます。

つまり、前日のデータを取得し、スプレッドシートに転記する当スクリプトでは、正確なCV数とそれに関連するデータを把握することは難しいのです。

検討期間がほぼ0日しかないアカウントならば、ある程度正しい数値が取れるでしょうが、1日以上の検討が存在するアカウントにおいて、CV関連データはほぼ虚像データとなってしまいます。

CVRという重要なデータだからこそ、正しくないデータを見続けるのは、誤った認識を植え付ける原因になり、良くないと考えているから、トラッキングから外しています。

CPAのグラフ描画を適当に丸めているのも同様の理由となります。

自らが把握したい本当のデータをしっかりと見定めて、ツールに使われるのではなく、ただのツールとして使うことが大切です。

スクリプトの価値

今回はレポーティングのAdwords Scriptの紹介でした。
このスクリプトのコーディングにかかった時間は約2時間です。

この2時間をスクリプトの価値で取り戻せれば、効果の高いものと言えるでしょう。

しかし、今回は反省の多いハックでした。

コードを読める人ならばわかるでしょうが、かなりのクソコードです。
既存の機能を元に拡張していた結果、「あれも欲しいな」「こんなこともしたいな」「こんなのも想定したほうがいいな」とツギハギだらけのプログラムになりました。

特に後半のメールまわりは、メソッド化しようとすれば出来るのですが、面倒になって強行突破しましたw

きちんとやりたいことを明確にして、ゼロから作ったほうが速くてキレイなものが組めたような気がします。
まあとりあえず動くからいいだろう。

また、自分でコードを書かなくても、サンプルプログラムで有用なものも非常に多くありますし、便利なスクリプトを公開していただいている方も多いです。

今回は、アカウントの詳細を見るために、ある程度のサマリー+グラフを用いましたが、もっと「ざっくりと手間をかけずに、複数アカウントの概要だけ把握したい」という場合は、山田さんが公開されているMCCスクリプトが非常に便利で活用できると思います。

コードが書ける方は、是非よりよいスクリプトを作成・公開してスクリプトの輪を広げていきましょう。

 

]]>
https://webmake.info/adwords-script%e3%81%a7%e6%97%a5%e6%ac%a1%e3%83%ac%e3%83%9d%e3%83%bc%e3%83%88%e3%82%92%e3%83%a1%e3%83%bc%e3%83%ab%e9%80%81%e4%bf%a1%e3%81%99%e3%82%8b%e3%82%b9%e3%82%af%e3%83%aa%e3%83%97%e3%83%88/feed/ 8
リスティング広告の運用をラクに。Adwords Scriptのススメ https://webmake.info/%e3%83%aa%e3%82%b9%e3%83%86%e3%82%a3%e3%83%b3%e3%82%b0%e5%ba%83%e5%91%8a%e3%81%ae%e9%81%8b%e7%94%a8%e3%82%92%e3%83%a9%e3%82%af%e3%81%ab%e3%80%82adwords-script%e3%81%ae%e3%82%b9%e3%82%b9%e3%83%a1/ https://webmake.info/%e3%83%aa%e3%82%b9%e3%83%86%e3%82%a3%e3%83%b3%e3%82%b0%e5%ba%83%e5%91%8a%e3%81%ae%e9%81%8b%e7%94%a8%e3%82%92%e3%83%a9%e3%82%af%e3%81%ab%e3%80%82adwords-script%e3%81%ae%e3%82%b9%e3%82%b9%e3%83%a1/#respond Sun, 08 May 2016 12:13:01 +0000 http://webmake.info/?p=736 日々、リスティング広告の運用をしていると、同じようなことを反復することがよくあると思います。

  • 毎週x曜日に○○レポートをダウンロードして、△をチェックする
  • 入札単価を変更して、平均掲載順位をx位にする
  • TD(広告)のA/Bテストをしており、規定数のImpressionを超えているものはA/Bテストを終了させる。
  • TD入稿後、品質スコアをチェックする。

など、その他にも「ルーティン」となっている業務というのは少なく無いと考えています。

入札単価に関しては、「自動入札ツール」が導入され、ある程度の条件に対しては簡単に管理画面上で設定ができるようになりました。

しかし、TDのABテストやレポーティング。品質スコアのチェックなど、面倒なルーティンだけど管理画面上の機能だけで自動化出来ないことも数多く残っています。

そんな課題を少しでも解決してくれるのが「アドワーズスクリプト」です。

Adwords Script(アドワーズスクリプト)とは

Adwords ScriptはGoogle Adwords上で動くスクリプトで、日々の面倒な様々な作業を自動化出来る可能性を秘めています。

特別、サーバーなどの追加投資も必要がないため、Adwords APIよりも気軽に利用ができます。

記述自体はJavascriptとなります。
フロントエンドエンジニアと言われる方がドキュメントを見たら簡単に書けると思いますし、プログラム未経験の方でも比較的習得しやすい言語だと思います。

Adwords Scriptで出来ること

そのアドワーズスクリプトですが、何が出来るの?と思われるかもしれません。
私がパッと思いつくだけでもかなりのことが出来ます。

  • 日次のアカウントレポートを記録・送信する(当記事)
  • ラベルごとに独自定義したレポートを作成する
  • キーワードの品質スコアの遷移をGoogleスプレッドシートに記録する
  • 条件を定義し、その条件を超える値を持った広告をメール通知をする
  • リンク先URLにアクセス出来ない場合、メール通知をする
  • A/Bテストが完了する見込みがたった広告があれば通知する
  • 天気情報を外部から取得し、入札単価の変更(雨の日に入札を上げるなど)
  • 祝日に広告を停止する

基本的に、情報が取得でき、かつトリガーが存在しているものであれば、ほとんどのことは自動化できます。

詳しい話は、Adwords/Analytics界隈で有名な山田さんが記事を書かれておりますので、ぜひご覧ください。

 

自動化をするメリット

日々、広告アカウントを運用している人でヒマという人は少ないでしょう。
受け持っているアカウントも少なくとも数種類はあるでしょう。

その中のすべてのアカウントに対し、毎日管理画面を隅々までチェックする。というのは非常に大変なことです。

大変なことですが、数日・数週・数ヶ月と運用を続けていくと、アカウントの法則みたいなものがある程度見えてきたりします。

その法則性をスクリプトに落とし、自動化することによって、今までかかっていた作業時間がほぼゼロになることも少なくありません。

あまり管理しなくて良いアカウントを作る

アカウントを複数管理して行く上で、すべてのアカウントに等しく全力を注ぐことは出来ません。
なぜなら、時間という最も代えがたい制約がそこにはあるからです。

やはり運用していく中で、力を注ぐアカウントと、そうでないアカウントが出てきます。

力を注ぐアカウントの例

  • 予算が大きい(インプレッション/クリック数が多い)
  • 構築直後

効率運用したいアカウントの例

  • 予算が小さい(インプレッション/クリック数が少ない)
  • ある程度完成している

効率運用をしたいアカウントはできるだけ自動化・最適化をほどこし、「あまり見なくてもきちんと稼働するアカウント」を作っていきましょう。

頑張らないことを決めれば、頑張っているものが速く終わる

時間というリソースは有限です。
その有限の時間の中で、やらないことを決め、しっかりと力を注ぐアカウントに力を注ぎ、成果の出る運用をしていきましょう。

そして、大きなアカウントでも構造を完成させることで、自動化の流れに乗せることが出来ます。
自動化ができれば、運用コストが減り、次のアカウント改善に力を注げるようになる。
そんな、正のスパイラルを構築していきましょう。

 

スクリプトの更なる発展のために必要なこと

ぼそぼそと思っていることを…。

Adwords Scriptは非常に便利なものです。
数年前から存在しており、ここまで便利なのになぜ広まらないのか。

大きな原因は2種類かと考えています。

  • 広告運用者とシステム担当者の知識に大きな壁がある
  • Yの存在

広告運用者とシステム担当者の知識に大きな壁がある

広告というものはここ数年、非常に早い速度で進化・複雑化しています。
その複雑化する流れに対応するためには、システムの力を頼らざるを得ません。

PLAのファイルを手作業で作る。なんてものは考えたくもないですね。

しかし、システムと広告運用者というものが協業することは少なく、お互いの知識が乏しいと感じています。
実際、私は数年間広告運用の世界におりますが、システムに明るく運用も出来るのは、上記で紹介したSEM Technologyの筆者、山田さんの他にはパッと思い当たりません。

広告の運用者の方ではPLAの設定が出来なかったり、タグの設定が出来ない方もいらっしゃいます。

逆にシステムの方では、広告の知識がないため、何をしていいのかわかりません。

サイト制作者が、何故か分からないけど「広告トラッキングのタグを消した」という話はよく聞く話です。

歩み寄りが大切

この問題に対しては、それぞれが歩みよるほか無いと思います。

広告運用者はシステムを作れる必要はないと思っています。
しかし、「システムを使えば何が出来るのか?」というシステム思考を身につけ、しっかりと要件を定義し、システム担当者へオーダーをする。

[amazonjs asin=”4061573055″ locale=”JP” title=”実践システム・シンキング 論理思考を超える問題解決のスキル (KS社会科学専門書)”]

システム担当者は、ちょっと何を言われているのかわからない部分があったり、セキュリティ的に難しい問題であっても「出来ない」と頭ごなしに潰さずに、きちんと説明をすることが大切でしょう。

 

Yの存在

日本にはGoogleともう一社、リスティング広告で無視できないY社が存在します。

いかんせん、このY社。システムの中核はGoogleと同じモノを使っているにもかかわらず、スクリプトをサポートしていない。
APIの公開も限られた正規代理店のみにしか行わない。という状態です。

Googleの運用をいかに効率的に行なっても、Y社の存在がある限り、完全自動化の道は遠いのが現状です。

どうせGoogleを自動化しても、タスクが実はそこまで減らない。というのがスクリプトが進まない一つの理由ではないでしょうか。

Adwordsスクリプトを勉強したい方に

Adwordsスクリプトの文法であるJavascriptを覚えることさえできれば、スクリプトを書くこと自体は難しくありません。

おすすめ書籍

[amazonjs asin=”4774153761″ locale=”JP” title=”ノンプログラマのためのJavaScriptはじめの一歩 (WEB+DB PRESS plus)”]

学習系Webサービス

関連スライド


システム系の勉強は、大変ですし面倒ですが、それは「楽をするための努力」なのです。
数年レベルの視点で見たとき、それは回収できる投資なのかもしれません。

今後、デジタルマーケティングはどんどんシステム化が進みます。
テクノロジーに寄り添い、ラクに仕事を出来るように頑張りましょう。

追記

薦めておいて自らが行動していないのはちょっとアレなので、書いてみました。

Adwords Scriptで日次レポートをメール送信するスクリプトを書いた

]]>
https://webmake.info/%e3%83%aa%e3%82%b9%e3%83%86%e3%82%a3%e3%83%b3%e3%82%b0%e5%ba%83%e5%91%8a%e3%81%ae%e9%81%8b%e7%94%a8%e3%82%92%e3%83%a9%e3%82%af%e3%81%ab%e3%80%82adwords-script%e3%81%ae%e3%82%b9%e3%82%b9%e3%83%a1/feed/ 0
やっちゃった…タグマネージャーで配信事故を防ぐ初期設定 https://webmake.info/%e3%82%84%e3%81%a3%e3%81%a1%e3%82%83%e3%81%a3%e3%81%9f%e3%82%bf%e3%82%b0%e3%83%9e%e3%83%8d%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bc%e3%81%a7%e9%85%8d%e4%bf%a1%e4%ba%8b%e6%95%85%e3%82%92%e9%98%b2/ https://webmake.info/%e3%82%84%e3%81%a3%e3%81%a1%e3%82%83%e3%81%a3%e3%81%9f%e3%82%bf%e3%82%b0%e3%83%9e%e3%83%8d%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bc%e3%81%a7%e9%85%8d%e4%bf%a1%e4%ba%8b%e6%95%85%e3%82%92%e9%98%b2/#respond Thu, 05 May 2016 09:11:57 +0000 http://webmake.info/?p=729 タグマネージャーの代表的な2つ。
Googleタグマネージャー、Yahooタグマネージャーのどちらも共通することですが、タグマネージャーのコンテナを作成したら自動的に「すべてのページ」というページ指定の方法ができています。

まさかの事故を防止。すべてのページを削除

私は、タグマネージャーを新規作成した後に標準で生成されている「すべてのページ」などのルールは使わないことを推奨しています。

理由としては、思いもよらぬ事故を引き起こしたケースが存在したためです。

すべてのページは、本当に「すべて」だった

Googleのタグマネージャーにしても、Yahooのタグマネージャーにしても、初期設定ではおおよそ下記のような設定が含まれています。

GTM:すべてのページ *
YTM:【削除不可】コンテナ名_コンテナID **

基本的にこの表現ですべてのページがヒットするようになります。

「何が問題なんだ?」と思われる方もいるかと思います。

実際、通常通り利用していれば、このままで問題はありません。
しかし、思いもよらぬところから問題というものは起こるものです。

誤って別サイトのタグマネージャーのタグを貼り付けた

  • サイトA -> サイトAのタグマネージャー
  • サイトB -> サイトBのタグマネージャー

それぞれが正しく設定されていれば問題ないのですが、下記のように誤った指定をしてしまったケースに問題は起こります。

  • サイトA -> サイトAのタグマネージャー
  • サイトB -> サイトAのタグマネージャー

標準の設定だと、ワイルドカードでドメインを問わず、本当に「すべてのページ」がヒットしてしまうため、「サイトB」にアクセスしても「サイトAのタグマネージャのタグ」が発火してしまうのです。

このミス設置が発生すると、Google Analyticsなどのアクセス解析データは

  • サイトAのセッションは急増
  • サイトBはトラッキングデータなし

という状態になってしまいます。

解析データのみならず、広告にも影響をおよぼす可能性

解析データがおかしくなるだけならまだマシです。
昨今のサイトの多くに、訪問ユーザを広告で追跡する”リマーケティング”タグが埋め込まれています。

つまり、アクセス解析のデータがおかしくなるだけにとどまらず

サイトA

  • ターゲットでもない顧客を広告費を使う
  • CPA悪化のリスクが増える

サイトB

  • 本来リマーケティングで追える見込み顧客を逃す

ことになります。

上記のような失敗を事業主がしてしまうならまだしも、代理店がやらかしてしまった場合…
考えたくもないですね。

そのような事故を防止するために

既存で存在している「すべてのページ」などのルールを使わない。

自らドメイン名をしっかりと指定したルールの作成を行いましょう。

【ALL】サイト名_すべてのページ {.*example.com.*}

こうすることで、サブドメインなどのページにミス設置されない限りは上記のよう影響が出ることがなくなります。

サブドメインまで固定したい場合は

【ALL】サイト名_すべてのページ {^www.example.com.*}

とすればよいのです。

正規表現の設定方法などは

タグマネージャーの指定、正規表現のススメ

に詳しく記載しております。ぜひご覧ください。

最後に

ツールはどんどん進化していきます。
しかし、どんな良いツールでも使いこなせなければ価値が失われてしまいます。

また、人間はミスをする生き物です。
ミスをする前提で、どうやったらミスの発生確率を下げられるか。ミスが起こっても被害が最小限でとどまるかを考え、防波堤を複数作っておくことが大切だと思います。

]]>
https://webmake.info/%e3%82%84%e3%81%a3%e3%81%a1%e3%82%83%e3%81%a3%e3%81%9f%e3%82%bf%e3%82%b0%e3%83%9e%e3%83%8d%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bc%e3%81%a7%e9%85%8d%e4%bf%a1%e4%ba%8b%e6%95%85%e3%82%92%e9%98%b2/feed/ 0
タグマネージャーの指定、正規表現のススメ https://webmake.info/%e3%82%bf%e3%82%b0%e3%83%9e%e3%83%8d%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bc%e3%81%ae%e6%8c%87%e5%ae%9a%e3%80%81%e6%ad%a3%e8%a6%8f%e8%a1%a8%e7%8f%be%e3%81%ae%e3%82%b9%e3%82%b9%e3%83%a1/ https://webmake.info/%e3%82%bf%e3%82%b0%e3%83%9e%e3%83%8d%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bc%e3%81%ae%e6%8c%87%e5%ae%9a%e3%80%81%e6%ad%a3%e8%a6%8f%e8%a1%a8%e7%8f%be%e3%81%ae%e3%82%b9%e3%82%b9%e3%83%a1/#respond Wed, 04 May 2016 07:42:02 +0000 http://webmake.info/?p=674 YTM(Yahooタグマネージャー)やGTM(Googleタグマネージャー)では、タグを配信したいページを指定し、そのページに対して配信するタグを紐付けていきます。

その際に、タグマネージャーでは基本的に

  1. ワイルドカード
  2. 正規表現

2つの手法でページの設定が行えます。
どちらの方法が良いのでしょうか?出来ることは同じでしょうか?

ワイルドカードと正規表現の違い

ワイルドカードと正規表現は似て非なるものです。
どちらも「文字列の一部をパターン化して表現する」という部分は同じです。
しかし、その表現の幅に大きな差があります。

ワイルドカードのパターン文字列は2種類。

  • ?」任意の1文字。
  • *」任意の0文字以上の文字列。

です。

それに対し、正規表現の表現方法は多岐にわたります。

・難易度
ワイルドカード < 正規表現

・利便性
ワイルドカード > 正規表現

といった感じかと思います。

さて、それではタグマネージャーの設定にはワイルドカードと正規表現のどちらをつかうのが良いのでしょうか?

おすすめの設定方法は正規表現

私は下記の理由で正規表現の利用を奨めています。

  1. 実現できる表現がワイルドカードより広い
  2. いろんなツールで用いられている表現のため学習リターンが大きい

 

GTMとYTMの正規表現記述の違い

GoogleタグマネージャーとYahooタグマネージャー、どちらも正規表現に対応していますが、若干記述方法が異なります。

Googleタグマネージャー Yahooタグマネージャー
正規表現 通常通り記述 {}で囲んで記述

Googleタグマネージャーは「正規表現に一致」などのオプション指定をした上でのページ選択になりますが、Yahooタグマネージャーでは、自由入力欄に「ワイルドカード」もしくは「正規表現」を記述するという自由な形式になっています。

そのため、「{」と「}」で囲むことで正規表現で記述したことを宣言しています。

正規表現で最低限覚えたいルール

正規表現には特殊な意味を持つ「メタ文字」というものが存在します。
代表的でよく利用するメタ文字を紹介します。

.(ドット)

任意の1文字という意味です。
どんな文字にでも適合します。


 

*(アスタリスク)

0回以上の繰り返しという意味です。

よく「.*」という用例が見受けられますが、これは「なんらかの文字が0回以上繰り返される」という意味です。


 

[]

カッコ内の任意の文字という意味です。


 

|(パイプ)

パイプという記号になり、この記号の左右のものをつなぐことが出来るという意味です。


 

() (カッコ)

グループ化を行うという意味です。
置換をすることが無いタグマネ上ではあまり利用するケースはありませんが、可読性向上にも利用できます。


 

^(チルダ)

先頭一致という意味です。
チルダの後ろに指定した文字から開始する文字列に適合します。

例)「^webmake\.info.*」とすることで、「webmake.info」から始まる文字列を指定することが出来ます。


 

$

後方一致という意味です。
$の前のに指定した文字で終わる文字列に適合します。

例)「^webmake\.info/.*\.jpg$」とすると、「webmake.info」の中のjpgファイルを指定することが出来ます。


 

\(バックスラッシュ)

エスケープ文字というものになります。
この文字は初心者が最もはまりやすい項目です。

これまでに上げたメタ文字ですが、通常の表記にもよく用いられる文字が存在します。
しかし、普通に記述してしまうとメタ文字扱いされてしまい、正しく指定出来ないことがあります。

そこで、メタ文字に利用されている文字を通常通りの文字として扱うために「エスケープ」を行います。

例)

  • × example.com
  • ○ example\.com

上記のように通常通り記述してしまうと、ドットがメタ文字として扱われるため

  • examplexcom
  • example.com
  • exampleacom

などドット以外の文字に何でも反映されてしまうようになります。

正規表現はサルでも分かる正規表現入門というサイト様がわかりやすいです。

参考:http://www.mnet.ne.jp/~nakama/

それぞれのサンプル

example.comというドメインに対し、タグマネを設置している前提になります。

ページ指定 ワイルドカード 正規表現
example.com
のすべてのページ
example.com** {^example\.com.*}
example.com
のすべてのページ
(サブドメインも含む)
example.com** {.*example\.com.*}
example.com/index.html
のページのみ(パラメタの有無どちらでも)
example.com/index.html {^example\.com/index\.html.*}
example.com/blog/
以降のディレクトリすべて
example.com/blog/** {^example\.com/blog/.*}

簡単な例ですが数点ご紹介しました。

見て感じた方もいらっしゃるかもしれませんが、ほとんど「.」「*」のメタ文字でまかなえます。
これらの条件に対し、「配信しないURL」も同様に組み立てて配信したいページを限定していく形となります。

最初は難しいと思う正規表現ですが、タグマネージャーに利用するという限られたシチュエーションだと覚えることもそう多くなく、実用できるのではないでしょうか。

ワイルドカードでの指定について

正規表現よりシンプルでとっつきやすいワイルドカード。

  • タグマネージャーを管理する全員が正規表現をマスター出来ない。
  • そこまで本気でタグマネージャーを使わない。

という場合は検討してもよいと思います。

YTMのタグマネージャーについて詳しくはでぶててさんのブログに紹介されておりますので、こちらも合わせてご覧ください。

最後に

ワイルドカードと正規表現を比較し、”個人的には”正規表現をおすすめしました。

しかし、もっとも大切なのは「統一すること」です。
一つのタグマネージャー内に、ワイルドカードと正規表現が混在するようなことがあれば、思わぬ設定ミスが生まれます。

正規表現を理解できない人がチーム内に居て、習得がどうしても難しく、ワイルドカードでも当分問題ないのであればワイルドカードでも良いでしょう。

手段が目的になってしまってはなりません。
しっかりと「なぜこの方法を選択したのか」「しなければならないのか」など制約とメリットを考えて取捨選択を行いましょう。

]]>
https://webmake.info/%e3%82%bf%e3%82%b0%e3%83%9e%e3%83%8d%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bc%e3%81%ae%e6%8c%87%e5%ae%9a%e3%80%81%e6%ad%a3%e8%a6%8f%e8%a1%a8%e7%8f%be%e3%81%ae%e3%82%b9%e3%82%b9%e3%83%a1/feed/ 0
Yahoo!アドバンスドURLでGoogle Analyticsのパラメータをトラッキング設定する方法 https://webmake.info/yahoo%e3%82%a2%e3%83%89%e3%83%90%e3%83%b3%e3%82%b9%e3%83%89url%e3%81%a7google-analytics%e3%81%ae%e3%83%91%e3%83%a9%e3%83%a1%e3%83%bc%e3%82%bf%e3%82%92%e3%83%88%e3%83%a9%e3%83%83%e3%82%ad%e3%83%b3/ https://webmake.info/yahoo%e3%82%a2%e3%83%89%e3%83%90%e3%83%b3%e3%82%b9%e3%83%89url%e3%81%a7google-analytics%e3%81%ae%e3%83%91%e3%83%a9%e3%83%a1%e3%83%bc%e3%82%bf%e3%82%92%e3%83%88%e3%83%a9%e3%83%83%e3%82%ad%e3%83%b3/#respond Sun, 01 May 2016 05:12:18 +0000 http://webmake.info/?p=716 先日、ヤフーのアドバンスドURL移行についての記事を書きました。

具体的に今後の新規入稿の手続きはどうなるのか?ということを、一般的?なケースを元にまとめてみました。

そもそも「なんでパラメータをトラッキングするの?」という部分は、アナグラムさんのブログに非常にわかりやすくまとめられていますので、そちらをご覧ください。

想定環境

今回のサンプルケースとして下記条件を前提として話を進めていきます。

対象ドメイン・ページ

example.com/index.html

トラッキングする項目

  • キャンペーン名
  • 広告グループ名
  • キーワード

分析ツール

  • アドエビスなどの広告成果分析ツールは使っていない
  • Google Analyticsでの分析を行なっている

アドバンスドURL導入前まで

表示URLにドメイン。リンク先URLにトラッキングパラメータまで含めたURLを設定をしていた。

表示URL:「example.com
リンク先URL:「http://example.com/index.html?utm_source=yahoo&utm_medium=cpc&utm_term={keywords}&utm_content=adGroupName&utm_campaign=campaignName

非常に長いですが、こんなところでしょうか。

{keyword} というのは、Yahoo!スポンサードサーチの機能で、検索されたキーワードが代入される変数です。

上記のように設定することで、Googleアナリティクス上では

  • 流入元:yahoo
  • メディア:cpc(有料検索)
  • キャンペーン:campaignName
  • コンテンツ:adGroupName
  • キーワード:検索キーワード

と計測されます。

アドバンスドURL導入後の方法

これまで、すべて「リンク先URL」に設定していたものを「トラッキングURL」に移行します。

また、管理をラクにするためにパラメータを利用するのがオススメです。

スポンサードサーチで標準搭載されているパラメータ

パラメータ 内容
{keyword} 検索キーワードの内容が代入される
{lpurl} アドバンスドURL以降に実装。
最終リンクURLの内容が代入される。

上記以外でも、自らカスタムパラメータを作ることが出来る。
カスタムパラメータは「キャンペーン > 広告グループ > 広告」と親子関係が続いていれば継承される。

例) 「キャンペーンA」の中に、3種類の広告グループ「X」「Y」「Z」があり、広告グループそれぞれに「X1」「X2」「Y1」「Y2」「Z1」「Z2」と2種類の広告が登録されている。

広告のグループ図

「A」にカスタムパラメータを設定した場合:すべての広告グループ、広告に継承される。
「X」にカスタムパラメータを設定した場合「X1」「X2」のみに継承される。また、「A」に指定したカスタムパラメータと同名のパラメータを、その下位に設定すると上書きされる。

設定サンプル

表示URL:「example.com
最終リンクURL:「http://example.com/index.html
トラッキングURL:「{lpurl}?utm_source=yahoo&utm_medium=cpc&utm_term={keywords}&utm_content=adGroupName&utm_campaign=campaignName

とすることで、入稿出来ます。

トラッキングURLを利用することで、これまですべての広告でバラバラだった最終リンクURLが1つになるため、審査もスピーディに終わりそうですね。
トラッキング関連もまとめられるので、管理がラクになりそうです。

 

さらにラクをするために

勘がいい人は気づいていると思いますが、キャンペーン名や広告グループ名もカスタムパラメータでパラメータ化することで、更に省力化が可能です。

ここではキャンペーン名を「campaignName」、広告グループ名を「adGroupName」というパラメータ名にします。

パラメータ名は自由なので、適宜変更いただいても構いません。

1.キャンペーンにカスタムパラメータを設定する。

キャンペーンのにカスタムパラメータを設定する。
ここからはYahoo!提供のキャンペーンエディターのスクリーンショットともに説明をしていきます。

campaign_editor-custom_parameter

パラメータ名は上記ルールに従い「campaignName」とする。

2.広告グループにも同様にカスタムパラメータを設定する。

広告グループもキャンペーンと同様にカスタムパラメータを設定する。
パラメータ名は、こちらも上記ルールに従い「adGroupName」とする。

3.広告のトラッキングURLを設定する。

広告のトラッキングURLを設定する。

campaign_editor-trackingurl

「{lpurl}?utm_source=yahoo&utm_medium=cpc&utm_term={keyword}&utm_content={_adGroupName}&utm_campaign={_campaignName}」

と設定を行う。

このようにすることで上記で設定したパラメータの値が代入され、

「http://example.com/index.html?utm_source=yahoo&utm_medium=cpc&utm_term=[検索キーワード]&utm_content=広告グループX&utm_campaign=キャンペーンA」
というトラッキングURLが動的に生成されるようになります。

すべてのキャンペーン・広告グループに適切にカスタムパラメータが設定されていれば、すべての広告を上記のトラッキングURLで統一できるので、入稿が楽になります。

当然といえば当然ですが、広告のトラッキングURLは入稿してからの変更はできません。
入稿前にしっかりと確認を行い、設定を行いましょう。

広告の計測パラメータは何のために設定するか

これまで広告のパラメータの設定方法についてご紹介してきましたが、最も大切なのは、そもそも「なぜパラメータを設定するのか」ということです。

考えるにパラメータを設定することで、Google Analytics上で正確にデータを反映することが出来るため、改善に繋げやすい。というのが最も大きい理由だと思います。

その目的をしっかりと理解した上で、どこまでパラメータを計測するのかを取捨選択していけばよいでしょう。

今回、例として広告グループ名のトラッキングまでを設定しましたが、必須ではないと思っています。
なぜなら、Google Adwordsから送られ、Google Analyticsに反映される「utm_content」の値は広告グループ名ではなく「広告見出し」だからです。

とはいえ、データが無いより、あった方が改善には当然つなげやすいため、設定の手間・分析の手間・データの実用性を総合的に鑑みて設定を行えばよいと考えています。

追われることの気持ち悪さ

我々は「分析」し「改善」するのが仕事の一つです。
だからか、時に一般的な感覚を失うこともあります。

何も知らない状態で、広告(とも分からない)をクリックしたらURLに自分の検索したキーワードが付いてきた。

「気持ち悪い。」と不気味がられ、直帰されることもゼロではないでしょう。

気持ち悪さの軽減のために

その気持ち悪さを軽減する方法を2つご紹介します。

URLエンコード

追われてる感を軽減するために、キャンペーン名などをURLエンコード処理することも一つの手段です。
URLエンコードをすると、パット見は読めない文字列になるため、心理的圧迫は少なくなるでしょう。

試しに「テスト」という文字列をエンコードしてみると「%83e%83X%83g」となりました。
こういった対策をすることで、ストーキング感を減らすのは一つの手段ではないでしょうか。

なお、エンコードした文字列は「デコード」という解読処理で元に戻すことが出来ます。

ID化

上記は文字列を読めなくする。という手段ですが、その他の策として「そもそも文字列に意味を持たせなくする」という手段もあります。
例えば「キャンペーンA」というものに対し、管理的に「CP01:キャンペーンA」と定義し、カスタムパラメータには「CP01」のみを定義します。

ユーザ的にはCP01と表示されていても、何か付いている。という感覚以上にはなり難いのではないでしょうか。

大切なのは改善する意志

上記2つの手段を取ると、当然Googleアナリティクス上でも同様の表示になるため、見づらくなってしまいます。

しかし、改善するための計測によって敬遠されてしまい、改悪になってしまっていては本末転倒です。
トラッキングも大切ですが、しっかりと消費者感覚をもち、多角的に改善に動いていければ良いですね。

 

]]>
https://webmake.info/yahoo%e3%82%a2%e3%83%89%e3%83%90%e3%83%b3%e3%82%b9%e3%83%89url%e3%81%a7google-analytics%e3%81%ae%e3%83%91%e3%83%a9%e3%83%a1%e3%83%bc%e3%82%bf%e3%82%92%e3%83%88%e3%83%a9%e3%83%83%e3%82%ad%e3%83%b3/feed/ 0
Yahoo!のアドバンスドURLシステム移行での既存広告と新規入稿の取り扱いについて https://webmake.info/yahoo%e3%81%ae%e3%82%a2%e3%83%89%e3%83%90%e3%83%b3%e3%82%b9%e3%83%89url%e3%82%b7%e3%82%b9%e3%83%86%e3%83%a0%e7%a7%bb%e8%a1%8c%e3%81%a7%e3%81%ae%e6%97%a2%e5%ad%98%e5%ba%83%e5%91%8a%e3%81%a8%e6%96%b0/ https://webmake.info/yahoo%e3%81%ae%e3%82%a2%e3%83%89%e3%83%90%e3%83%b3%e3%82%b9%e3%83%89url%e3%82%b7%e3%82%b9%e3%83%86%e3%83%a0%e7%a7%bb%e8%a1%8c%e3%81%a7%e3%81%ae%e6%97%a2%e5%ad%98%e5%ba%83%e5%91%8a%e3%81%a8%e6%96%b0/#respond Sat, 30 Apr 2016 12:38:03 +0000 http://webmake.info/?p=712 Google Adwordsの「改良版URL」に引き続き、Yahoo!スポンサードサーチでも2016年4月13日より「アドバンスドURLシステム」というものに設定URLが移行されます。

アドバンスドURLとは

アドバンスドURLとは

既存は「広告」は

  1. 表示URL
  2. リンク先URL

という2種類のみのパラメータを持っていたが、今後は

  1. 表示URL
  2. 最終リンクURL
  3. スマートフォン向けURL
  4. トラッキングURL
  5. カスタムパラメータ

という5種類の要素を持つことになる。

このURLシステムのことを「アドバンスド(高機能)URL」と呼ぶのだろう。

旧来とのもっとも大きな変化は、これまで広告の成果などを分析するために利用しているトラッキング関連のパラメータを「リンク先URL」に記載していたが、今後は「トラッキングURL」欄に記載することだろう。

アドバンスドURLの目的

Yahoo!公式のヘルプより下記声明を引用する。

  • 1.広告市場の健全性の向上ユーザーに誤解を与えるような広告を減らし、ユーザーがより安心して情報を受け取れる環境を提供します。
  • 2.広告主様の運用工数削減による効率的な広告管理リンク先情報やトラッキング情報を分割管理できるようになり、広告主様はより効率的な運用していただけるようになります。

出典:公式ヘルプ

最終リンクURLを明記することでのなりすましの防止や、パラメータの変更コストが減ることによる運用者の手間が軽減されるとのことだ。

詳細は特設サイトに明記されているので、そちらを参照くださいませ。

 

既存広告と新規入稿について

上記特設サイトにも記載があるが、「すべての広告管理者が作業をする必要がある」

既存広告の移行について

移行については、現状の運用方法によって3種類に分類される。

  1. Yahoo提供の移行ツールでの移行
  2. Webの広告管理ツール上での移行
  3. キャンペーンエディター上での移行

特に分析をするためのシステムを利用していない限りは、1番の「移行ツール」の登場を待ったほうが無難な気もしている。

移行機能の提供を待たず、自身の手で移行作業を実施したい場合は、名古屋の広告代理店である”カルテットコミュニケーションズ”さんがブログでやり方を紹介しています。

また、公式の移行機能は2016年6月での提供を予定しているようです。

新規入稿について

既存広告は上記の通り、移行機能の登場を待ち、性能を見定めてから対応を考えれば良いが、新規入稿はそうもいかない。

4月13日以降は旧来の「表示先URL」と「リンク先URL」では入稿ができなくなりました。

今後は

  • 表示URL:旧来と同様
  • 最終リンクURL:広告のランディングページの表示に必要なURLのみを記述
  • トラッキングURL:各種計測パラメータを含んだ計測に必要なURL

といった分類で入稿をしていくのが良いでしょう。

所感・まとめ

ここ数年、デジタル広告領域はすさまじいスピードで変化を遂げています。
扱うデータも大量になり、今後ビックデータが実際に運用に活用されるケースも増えてくるでしょう。

しかしそうなると、当然ながら運用コストは上がっていきます。
そのコストを吸収できる一つの方法が「システム化」であることは間違いないでしょう。

しっかりとプロダクトとシステムに寄り添い、運用を省力化し、最大の効力を上げていけるよう精進していきたいと思います。

]]>
https://webmake.info/yahoo%e3%81%ae%e3%82%a2%e3%83%89%e3%83%90%e3%83%b3%e3%82%b9%e3%83%89url%e3%82%b7%e3%82%b9%e3%83%86%e3%83%a0%e7%a7%bb%e8%a1%8c%e3%81%a7%e3%81%ae%e6%97%a2%e5%ad%98%e5%ba%83%e5%91%8a%e3%81%a8%e6%96%b0/feed/ 0
MongoDBがダウンするようになったので、v2.4.5からv3.0にアップデートした話 https://webmake.info/mongodb%e3%81%8c%e3%83%80%e3%82%a6%e3%83%b3%e3%81%99%e3%82%8b%e3%82%88%e3%81%86%e3%81%ab%e3%81%aa%e3%81%a3%e3%81%9f%e3%81%ae%e3%81%a7%e3%80%81v2-4-5%e3%81%8b%e3%82%89v3-0%e3%81%ab%e3%82%a2%e3%83%83/ https://webmake.info/mongodb%e3%81%8c%e3%83%80%e3%82%a6%e3%83%b3%e3%81%99%e3%82%8b%e3%82%88%e3%81%86%e3%81%ab%e3%81%aa%e3%81%a3%e3%81%9f%e3%81%ae%e3%81%a7%e3%80%81v2-4-5%e3%81%8b%e3%82%89v3-0%e3%81%ab%e3%82%a2%e3%83%83/#respond Fri, 29 Apr 2016 15:07:54 +0000 http://webmake.info/?p=687 ある日、急にMongoDBが落ちるようになった。

ログを見る限り、「ロックファイルが残っているため立ちあげられない」という情報のみだった。

ダウン原因の調査

エラーログやMuninなどの監視ツールを調査した結果、ダウンした際の共通点があった。
それは

  1. メモリを2GB以上喰った際に落ちている
  2. MongoDBのロックファイルが残っている

ということだった。

一歩前進はしたが「メモリを2GB以上使うと落ちる」といった言語仕様や症例もなく、これだけでは原因特定が困難だった。

しかし、よくよく調べていくと同一サーバで動いているMySQLもおおよそ2GBのメモリを使用している。

そして、このサーバの物理メモリは4GBだ。

MongoDBがダウンする原因の仮説

  1. メモリリークが起こり、MongoDBがロックファイルを残したままダウンする
  2. 自動復帰するために再起動するが、ロックファイルがあるため立ち上がらない

というものである。

MongoDBダウンの回避対策

MongoDBのメモリ利用量を制限することで、メモリリークを起こさないようにしてみる。

しかし、残念ながらMongoDB v2系はメモリ制限の機能が存在せず、上記のような対策をとることが出来ない。

だが諦めるのはまだ早い。
Mongo v3から新規で追加されたストレージエンジン「Wired Tiger」にはメモリを制限する機能が備わっている。

これだ!

ということで、

  1. MongoDBをv2からv3にアップデートし
  2. さらにストレージエンジンを「mmap」から「Wired Tiger」に切り替え
  3. 「Wired Tiger」でメモリ制限をする

ことでMongoDBのダウンが再現しないか検証する。

ぶっちゃけ、AWSなどのクラウド環境や、DBサーバを並列で立てられたり移行出来る場合は移行して潤沢なメモリを割り当ててやるのが一番だと思いますw

MongoDBがインストールされているサーバ環境

環境はさくらのVPSです。

メモリ4GBのHDDプランだったと思いますので、下記の感じですかね。

  • OS: CentOS 6.5
  • メモリ:4GB

どこにでもあるCentOSのVPS機です。
では、これからMongoDBアップデートの旅に出かけましょう。

MongoDBのアップデートの前準備

CentOSのビットを確認

MongoDBには32bit版と64bit版があります。

32bit版は非推奨なので、OSが64bitであれば、可能な限り64bitをインストールしたいので、OSのbitを調べます。

[root@server ~]# uname -m
x86_64

上記が「x86_64」なら64bitです。

32bitだと「i686」などが返ってきます。

MongoDBをバックアップ

mongod.confの場所を調べ、mongodbのデータパスを明確にする。

ps aux | grep mongod
mongod   11488  8.4  4.9 877920 192672 ?       Sl   Mar05 514:23 /usr/bin/mongod -f /etc/mongod.conf

dbpathを調べる

[root@server ~]# grep dbpath /etc/mongod.conf  
dbpath=/var/lib/mongo

mongodbを停止

[root@server ~]# service mongod stop  
Stopping mongod:                                           [  OK  ]

バックアップを取得

バックアップ先のディレクトリを作る

[root@server ~]# mkdir ~/mongo_dump

作成したディレクトリにバックアップのダンプを出力

[root@server ~]# mongodump -v --dbpath /var/lib/mongo --out ~/mongo_dump/
Wed Mar  9 23:40:19.786 [tools] flushing directory /var/lib/mongo
Wed Mar  9 23:40:19.794 [tools] run command admin.$cmd { isdbgrid: 1 }
Wed Mar  9 23:40:19.794 [tools] command admin.$cmd command: { isdbgrid: 1 } ntoreturn:1 keyUpdates:0  reslen:99 0ms
Wed Mar  9 23:40:19.795 [tools] all dbs
Wed Mar  9 23:40:19.795 [tools] run command admin.$cmd { listDatabases: 1 }
Wed Mar  9 23:40:19.806 [tools] opening db:  local
Wed Mar  9 23:40:19.840 [tools] opening db:  admin
Wed Mar  9 23:40:19.859 [tools] opening db:  db1

… 中略

Wed Mar  9 23:40:21.299 dbexit: 
Wed Mar  9 23:40:21.299 [tools] shutdown: going to close listening sockets...
Wed Mar  9 23:40:21.299 [tools] shutdown: going to flush diaglog...
Wed Mar  9 23:40:21.299 [tools] shutdown: going to close sockets...
Wed Mar  9 23:40:21.299 [tools] shutdown: waiting for fs preallocator...
Wed Mar  9 23:40:21.299 [tools] shutdown: closing all files...
Wed Mar  9 23:40:21.315 [tools] closeAllFiles() finished
Wed Mar  9 23:40:21.315 [tools] shutdown: removing fs lock...
Wed Mar  9 23:40:21.330 dbexit: really exiting now

MongoDBをアンインストール

バックアップの作成が完了したので、2.x系のMongoDBをアンインストールしましょう。

アンインストールはコマンド一発です。

[root@server ~]# yum remove mongo*     
Loaded plugins: fastestmirror, security
Setting up Remove Process
No Match for argument: mongo_dump
Loading mirror speeds from cached hostfile
Could not get metalink https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=x86_64 error was
14: problem making ssl connection
 * epel: ftp.kddilabs.jp
 * rpmforge: mirror.fairway.ne.jp
10gen                                                                                                                                                                                                                                                 |  951 B     00:00     
base                                                                                                                                                                                                                                                  | 3.7 kB     00:00     
extras                                                                                                                                                                                                                                                | 3.4 kB     00:00     
"> [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
Trying other mirror.
rpmforge                                                                                                                                                                                                                                              | 1.9 kB     00:00     
updates                                                                                                                                                                                                                                               | 3.4 kB     00:00     
updates/primary_db                                                                                                                                                                                                                                    | 3.9 MB     00:00

無事アンインストールができたら、次にv3系のMongoDBをインストールしましょう。

公式サイトを参考にMongoDB v3.2をインストール

それではMongoDBをインストールしていきましょう。
インストールの手順は下記の公式マニュアルが非常に丁寧に説明してくれているので、見ながら順に実行していけば問題ないかと思います。

公式リポジトリを登録

vi /etc/yum.repos.d/mongodb-org-3.2.repo

[mongodb-org-3.2]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64/
gpgcheck=0
enabled=1

YumでMongoDBをインストール

インストールもyumコマンド一発です。
簡単になりましたね。

[root@server ~]# sudo yum install -y mongodb-org
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
Could not get metalink https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=x86_64 error was
14: problem making ssl connection
 * epel: ftp.kddilabs.jp
 * rpmforge: mirror.fairway.ne.jp
http://www.hop5.in/yum/el6/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
Trying other mirror.
mongodb-org-3.2                                                                                                                                                                                                                                       | 2.5 kB     00:00     
mongodb-org-3.2/primary_db                                                                                                                                                                                                                            |  26 kB     00:00     
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package mongo-10gen.i686 0:2.4.5-mongodb_1 will be obsoleted
---> Package mongodb-org.x86_64 0:3.2.4-1.el6 will be obsoleting
--> Processing Dependency: mongodb-org-tools = 3.2.4 for package: mongodb-org-3.2.4-1.el6.x86_64
--> Processing Dependency: mongodb-org-shell = 3.2.4 for package: mongodb-org-3.2.4-1.el6.x86_64
--> Processing Dependency: mongodb-org-server = 3.2.4 for package: mongodb-org-3.2.4-1.el6.x86_64
--> Processing Dependency: mongodb-org-mongos = 3.2.4 for package: mongodb-org-3.2.4-1.el6.x86_64
--> Running transaction check
---> Package mongo-10gen-server.i686 0:2.4.5-mongodb_1 will be obsoleted
---> Package mongodb-org-mongos.x86_64 0:3.2.4-1.el6 will be installed
---> Package mongodb-org-server.x86_64 0:3.2.4-1.el6 will be obsoleting
---> Package mongodb-org-shell.x86_64 0:3.2.4-1.el6 will be installed
---> Package mongodb-org-tools.x86_64 0:3.2.4-1.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=============================================================================================================================================================================================================================================================================
 Package                                                                Arch                                                       Version                                                         Repository                                                           Size
=============================================================================================================================================================================================================================================================================
Installing:
 mongodb-org                                                            x86_64                                                     3.2.4-1.el6                                                     mongodb-org-3.2                                                     5.8 k
     replacing  mongo-10gen.i686 2.4.5-mongodb_1
 mongodb-org-server                                                     x86_64                                                     3.2.4-1.el6                                                     mongodb-org-3.2                                                      13 M
     replacing  mongo-10gen-server.i686 2.4.5-mongodb_1
Installing for dependencies:
 mongodb-org-mongos                                                     x86_64                                                     3.2.4-1.el6                                                     mongodb-org-3.2                                                     5.6 M
 mongodb-org-shell                                                      x86_64                                                     3.2.4-1.el6                                                     mongodb-org-3.2                                                     6.9 M
 mongodb-org-tools                                                      x86_64                                                     3.2.4-1.el6                                                     mongodb-org-3.2                                                      37 M

Transaction Summary
=============================================================================================================================================================================================================================================================================
Install       5 Package(s)

Total download size: 63 M
Downloading Packages:
(1/5): mongodb-org-3.2.4-1.el6.x86_64.rpm                                                                                                                                                                                                             | 5.8 kB     00:00     
(2/5): mongodb-org-mongos-3.2.4-1.el6.x86_64.rpm                                                                                                                                                                                                      | 5.6 MB     00:00     
(3/5): mongodb-org-server-3.2.4-1.el6.x86_64.rpm                                                                                                                                                                                                      |  13 MB     00:01     
(4/5): mongodb-org-shell-3.2.4-1.el6.x86_64.rpm                                                                                                                                                                                                       | 6.9 MB     00:00     
(5/5): mongodb-org-tools-3.2.4-1.el6.x86_64.rpm                                                                                                                                                                                                       |  37 MB     00:03     
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                                                                                                                                         11 MB/s |  63 MB     00:05     
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing : mongodb-org-tools-3.2.4-1.el6.x86_64                                                                                                                                                                                                                      1/7 
  Installing : mongodb-org-server-3.2.4-1.el6.x86_64                                                                                                                                                                                                                     2/7 
  Installing : mongodb-org-mongos-3.2.4-1.el6.x86_64                                                                                                                                                                                                                     3/7 
  Installing : mongodb-org-shell-3.2.4-1.el6.x86_64                                                                                                                                                                                                                      4/7 
  Installing : mongodb-org-3.2.4-1.el6.x86_64                                                                                                                                                                                                                            5/7 
  Erasing    : mongo-10gen-server-2.4.5-mongodb_1.i686                                                                                                                                                                                                                   6/7 
warning: /var/log/mongo/mongod.log saved as /var/log/mongo/mongod.log.rpmsave
  Erasing    : mongo-10gen-2.4.5-mongodb_1.i686                                                                                                                                                                                                                          7/7 
  Verifying  : mongodb-org-shell-3.2.4-1.el6.x86_64                                                                                                                                                                                                                      1/7 
  Verifying  : mongodb-org-mongos-3.2.4-1.el6.x86_64                                                                                                                                                                                                                     2/7 
  Verifying  : mongodb-org-server-3.2.4-1.el6.x86_64                                                                                                                                                                                                                     3/7 
  Verifying  : mongodb-org-tools-3.2.4-1.el6.x86_64                                                                                                                                                                                                                      4/7 
  Verifying  : mongodb-org-3.2.4-1.el6.x86_64                                                                                                                                                                                                                            5/7 
  Verifying  : mongo-10gen-server-2.4.5-mongodb_1.i686                                                                                                                                                                                                                   6/7 
  Verifying  : mongo-10gen-2.4.5-mongodb_1.i686                                                                                                                                                                                                                          7/7 

Installed:
  mongodb-org.x86_64 0:3.2.4-1.el6                                                                                                  mongodb-org-server.x86_64 0:3.2.4-1.el6                                                                                                 

Dependency Installed:
  mongodb-org-mongos.x86_64 0:3.2.4-1.el6                                                   mongodb-org-shell.x86_64 0:3.2.4-1.el6                                                   mongodb-org-tools.x86_64 0:3.2.4-1.el6                                                  

Replaced:
  mongo-10gen.i686 0:2.4.5-mongodb_1                                                                                                mongo-10gen-server.i686 0:2.4.5-mongodb_1                                                                                               

Complete!

これでインストールは完了です。
続いて設定に移っていきましょう。

MongoDBが起動出来るようにSELinuxを設定する

SELinuxに対し、MongoDBを許容するように設定します。
今回は問題となっているダウン症状が再現するかどうかのテストが目的であり、面倒だったので、SELinuxを無効化します。

必要に応じて、許容か無効化かを選択してください。

SELinuxの設定ファイルを作成する

vi /etc/selinux/config

作成したファイル内に下記を書き込む

SELINUX=disabled

無効になっているかを確認

「getenforce」コマンドでDisabledになれば無効になっています。

[root@server ~]# getenforce 
Disabled

WiredTiger用の設定ファイルを記述する

MongoDBの新たなストレージエンジンである「WiredTiger」を利用するための設定を記述します。
設定はYAML形式で記述します。

設定ファイルを作成する。

ファイル名は何でもOKです。

vi /etc/mongod_wt.conf

設定を記述する

DBパスやその他設定は適宜変更してください。
今回、メモリを2GB以上利用して死んでいたので、サイズ制限を1GBに設定しました。

storage:
 dbPath: "/var/lib/mongo_wt"
 engine: "wiredTiger"
 directoryPerDB: true
 wiredTiger:
 engineConfig:
 cacheSizeGB: 1
 directoryForIndexes: true
 statisticsLogDelaySecs: 0
 collectionConfig:
 blockCompressor: "snappy"
 indexConfig:
 prefixCompression: true
 journal:
 enabled: true
systemLog:
 destination: file
 path: "/var/log/mongodb/mongodb_wt.log"
 logAppend: true
processManagement:
 fork: true

参考http://qiita.com/kuwa_tw/items/0a5704e9e505cffeae34

MongoDBの起動・リストア

アンインストール、インストール、設定ファイルの記述と終われば、いよいよ起動です。

起動の前に設定ファイルで定義したデータ保存ディレクトリがなければ作っておきましょう。

mkdir /var/lib/mongo_wt

設定ファイルのパスを指定して起動する

毎回起動する際の設定がめんどうならば、

  1. 設定ファイルを「/etc/mongod.conf」に作成する
  2. 「/etc/init.d/mongod」の設定ファイルパスを書き換える

のどちらかでパス指定が不要になります。

[root@server ~]# mongod -f /etc/mongod_wt.conf 
about to fork child process, waiting until server is ready for connections.
forked process: 32733
child process started successfully, parent exiting
[root@server ~]# service mongod status
mongod (pid 32733) is running...

バックアップしていたデータファイルをリストアする

無事起動したら、バックアップしていたデータをリストアしましょう。

[root@server ~]# mongorestore -v --db db1 ~/mongo_dump/db1/
2016-03-10T00:20:41.028+0900	using write concern: w='1', j=false, fsync=false, wtimeout=0
2016-03-10T00:20:41.028+0900	building a list of collections to restore from /root/mongo_dump/db1 dir

… 中略

2016-03-10T00:20:59.229+0900	finished restoring db1.items (65950 documents)
2016-03-10T00:20:59.229+0900	done

これで起動は完了です。
大きなトラブルもなく、v2系からv3系に更新出来てよかったよかった。

 

そんなに世の中甘くない

バージョンアップにトラブルはつきもの。

うまく行ったと思っていたが、MongoDBのデータを読み書きするPHPスクリプトにアクセスしたら…

 

_人人人人人人_
> 突然の死 <
 ̄Y^Y^Y^Y^Y ̄

 

えっ。真っ白だよ。

PHPからMongoDBにつながらない。

そう。PHPからMongoDBにつながらないのである。
原因を確認したところ、なんと現行で動かしていたドライバ「mongo」にエラーが発生した。

PeclでインストールしたドライバがMongo v2.6までしか対応していなかったのだ。

仕方ないので、mongodb1.1をインストールすることに。

peclでインストールしたドライバがダメなら新しいやつインストールしてみよう。
そう思い、mongodb1.1をインストールしてみる。

sudo pecl install mongodb

vi /etc/php.d/mongo.so

開いたファイルの内容を

extension=mongodb.so

に変更。

 

よしよし。問題なくPHPドライバ「mongodb」がインストールされたので、再度確認。

 

_人人人人人人人人人人人人人人人人人人人人人人人人_
> Fatal Error – Class ‘MongoClient’ not found <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

よくよく中身をみると、そもそものコネクションクラスから、データのやり取りまで何から何まで変わってた。← あたりまえである。

 

つまり・・・当然プログラム全部書き換え!!

そんなのはお断りです。

…ということで

PHPドライバについて調べる

を参照。

mongo1.6ならmongodb v3.0に対応してるっぽい。

peclのmongoのバージョン確認

[root@server ~]# pecl list
Installed packages, channel pecl.php.net:
=========================================
Package   Version State
jsonc     1.3.9   stable
memcache  2.2.7   stable
memcached 2.2.0   stable
mongo     1.6.12  stable
mongodb   1.1.3   stable
xdebug    2.3.3   stable
xhprof    0.9.4   beta
zip       1.13.1  stable

1.6だった。

ということで…。

再度DB側でv3.2をアンインストール→v3.0をインストール。

一度やった手続きなので慣れたもの。
サービスを停止し、MongoDBをアンインストールする。
やり方自体は公式ドキュメントに懇切丁寧に書いてあったので、それをなぞる。

sudo service mongod stop
sudo yum erase $(rpm -qa | grep mongodb-org)

v3.0をインストール

vi /etc/yum.repos.d/mongodb-org-3.0.repo

リポジトリを登録。
この時、以前登録した3.2のファイルは消しておきましょう。

[mongodb-org-3.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.0/x86_64/
gpgcheck=0
enabled=1

yumでインストール実行。

sudo yum install -y mongodb-org

設定ファイルを指定し、起動。バックアップからデータのリストアを実行。

[root@server plugins]# mongod -f /etc/mongod_wt.conf
[root@server plugins]# mongorestore -v --db db1 ~/mongo_dump/db1/
2016-03-10T01:16:24.897+0900 using write concern: w='1', j=false, fsync=false, wtimeout=0

… 中略

2016-03-10T01:16:33.330+0900 finished restoring db1.items (65950 documents)
2016-03-10T01:16:33.330+0900 done

元気に稼働し始めました。

よかったよかった。

番外編:Muninの監視から外れる

muninでメモリとか監視していたが、再インストール後監視から外れていたため、再登録を行う。
調べると、muninのmongodbプラグインはインストールされているが、稼働していない状態だった。

とりあえずMuninのプラグインを単体でテストしてみる。

[root@server plugins]# cd /etc/munin/plugins
[root@server plugins]# munin-run mongo_ops 
Traceback (most recent call last):
  File "/etc/munin/plugins/mongo_ops", line 56, in <module>
    doData()
  File "/etc/munin/plugins/mongo_ops", line 33, in doData
    ss = getServerStatus()
  File "/etc/munin/plugins/mongo_ops", line 28, in getServerStatus
    raw = urllib2.urlopen(req).read()
  File "/usr/lib64/python2.6/urllib2.py", line 126, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib64/python2.6/urllib2.py", line 391, in open
    response = self._open(req, data)
  File "/usr/lib64/python2.6/urllib2.py", line 409, in _open
    '_open', req)
  File "/usr/lib64/python2.6/urllib2.py", line 369, in _call_chain
    result = func(*args)
  File "/usr/lib64/python2.6/urllib2.py", line 1190, in http_open
    return self.do_open(httplib.HTTPConnection, req)
  File "/usr/lib64/python2.6/urllib2.py", line 1165, in do_open
    raise URLError(err)
urllib2.URLError: <urlopen error [Errno 111] Connection refused>

エラーメッセージを読むと、RESTアクセス出来てないようだ。

調べたところ、セキュリティの関係上、MongoDBはデフォルトではRESTが無効になっているようだ。

設定ファイルに下記を加筆して、再度Mongoを起動

net:
   http:
      enabled: true
      JSONPEnabled: true
      RESTInterfaceEnabled: true

これでRESTを許可したので、Muninからもアクセスが行えるようになったはずである。

再度テスト

[root@server plugins]# munin-run mongo_ops 
getmore.value 0
insert.value 0
update.value 0
command.value 0
query.value 1
delete.value 0

いけた。

munin-nodeの再起動

[root@server plugins]# service munin-node restart
Stopping Munin Node agents:                                [  OK  ]
Starting Munin Node:                                       [  OK  ]

MuninのMongoDBのトラッキングも再設定できました。

 

あとがき

1ヶ月以上運用した結果、いまのところ以前のようなMondoDBダウンは1度も発生していません。
仮説の通り、単純にメモリ不足によるエラーだったものと思われます。

MongoDB自体アップグレードし、性能が上がり、メモリ利用量も少なくなっているので、結果オーライだと思います。

教訓: アップグレードは慎重に。でも大切なのでしっかりやろう。そして、事前の情報収集もしっかりと。

 

]]>
https://webmake.info/mongodb%e3%81%8c%e3%83%80%e3%82%a6%e3%83%b3%e3%81%99%e3%82%8b%e3%82%88%e3%81%86%e3%81%ab%e3%81%aa%e3%81%a3%e3%81%9f%e3%81%ae%e3%81%a7%e3%80%81v2-4-5%e3%81%8b%e3%82%89v3-0%e3%81%ab%e3%82%a2%e3%83%83/feed/ 0