こんにちは。サーバーサイドエンジニアの杉浦です。
前回は、Google広告(旧:GoogleAdWords)のデータをキャンペーンパフォーマンスレポートを使って取得する方法をご紹介しました。
今回は、アカウントパフォーマンスレポートから、前日にCVの上がったアカウント一覧をメールで送信する方法をご紹介します。
前提
Google広告を運用しているGoogleアカウントがあること
入門内容
アカウントパフォーマンスレポートより、下記項目を取得し、HTMLメールを送信することを目標とします。
Google広告画面でのイメージとしては、対象MCCアカウントの「アカウント>掲載結果」に表示される項目を取得します。
- アカウントID
- アカウント名
- コンバージョン
- 表示回数
- クリック数
- 費用
- CTR
- CPC
コード
* 前日にCVが上がったアカウント情報をメールで送信します
var MAIL_ADDRESS = ['AAAAAAAAAA@AAA.co.jp'
,'BBBBBBBBBB@BBB.co.jp'];
// レポート出力対象CustomerID ★変更してください★
var MANAGER_CUSTOMER_ID = '1234567890';
procAccountDataToMail(function(account) {
var data = getAccountData(account);
var mail_date = shapeDataToHtml(allData);
function procAccountDataToMail(callback) {
var accountSelector = MccApp.accounts()
.withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'")
.withCondition("Conversions > 0")
.forDateRange("YESTERDAY")
.orderBy("ExternalCustomerId ASC");
var accountIterator = accountSelector.get();
Logger.log('アカウント件数 : ' + accountIterator.totalNumEntities());
while(accountIterator.hasNext()) {
var account = accountIterator.next();
// MCC内に存在する対象のアカウント情報を取得
// main()内にて宣言したfunctionを実行する
function getAccountData() {
var rows = AdWordsApp.report(
'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, Ctr, Conversions'
+ ' FROM ACCOUNT_PERFORMANCE_REPORT '
data['ExternalCustomerId'] = row['ExternalCustomerId'];
data['CustomerDescriptiveName'] = row['CustomerDescriptiveName'];
data['Impressions'] = row['Impressions'];
data['Clicks'] = row['Clicks'];
data['Cost'] = row['Cost'];
data['Ctr'] = row['Ctr'];
data['Conversions'] = row['Conversions'];
Logger.log('Exception:'+ex);
function shapeDataToHtml(data){
d.setDate(d.getDate() - 1);
var subject = '【CVあり】アカウントレポート_' + Utilities.formatDate(d, 'JST', 'yyyy年MM月dd日');
body.push('<h1>昨日のCVが1以上のアカウント一覧</h1>');
body.push('<table border="1">');
body.push('td width="80" style="background: #a3daff">アカウントID</td>');
body.push('<td width="500" style="background: #a3daff">アカウント名</td>');
body.push('<td width="100" style="background: #a3daff">コンバージョン</td>');
body.push('<td width="80" style="background: #a3daff">表示回数</td>');
body.push('<td width="80" style="background: #a3daff">クリック数</td>');
body.push('<td width="80" style="background: #a3daff">費用</td>');
body.push('<td width="80" style="background: #a3daff">CTR</td>');
body.push('<td width="80" style="background: #a3daff">CPC</td>');
for(var i = 0; i < data.length; i++) {
body.push('<td>'+data[i]['ExternalCustomerId']+'</td>');
body.push('<td>'+data[i]['CustomerDescriptiveName']+'</td>');
body.push('<td>'+data[i]['Conversions']+'</td>');
body.push('<td>'+data[i]['Impressions']+'</td>');
body.push('<td>'+data[i]['Clicks']+'</td>');
body.push('<td>'+data[i]['Cost']+'</td>');
body.push('<td>'+data[i]['Ctr']+'</td>');
body.push('<td>'+(Math.round(getRatio(toInteger(data[i]['Cost']), toInteger(data[i]['Clicks']), false)))+'</td>');
return { "subject": subject, "body": body.join('\n') };
* @param {array} mail_date
function sendmail(mail_date) {
MailApp.sendEmail(MAIL_ADDRESS
, {htmlBody: mail_date["body"]} );
Logger.log('メールを送信しました。 件名:' + mail_date["subject"]);
* @param {string} value 数字の文字列
function toInteger(value) {
return parseInt(value.split(',').join(''), 10)
Logger.log('[ERROR] function:toInteger value='+value+', ex='+ex);
* @param {int} bunshi 割られる数
* @param {boolean} isPercent true:%表記で返す/false:100を掛けない元データで返す
function getRatio(bunshi, bunbo, isPercent) {
if (bunshi == 0 || bunbo == 0) { return 0; }
return bunshi / bunbo * 100;
/**
* 前日にCVが上がったアカウント情報をメールで送信します
*/
// メールアドレス ★変更してください★
var MAIL_ADDRESS = ['AAAAAAAAAA@AAA.co.jp'
,'BBBBBBBBBB@BBB.co.jp'];
// レポート出力対象CustomerID ★変更してください★
var MANAGER_CUSTOMER_ID = '1234567890';
function main() {
// データを取得
var allData = [];
procAccountDataToMail(function(account) {
// アカウントレポートデータ取得
var data = getAccountData(account);
// 全データをため込む
allData.push(data);
});
// データをHTMLに加工
var mail_date = shapeDataToHtml(allData);
// メール送信
sendmail(mail_date);
}
function procAccountDataToMail(callback) {
// アカウント情報を取得
var accountSelector = MccApp.accounts()
.withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'")
.withCondition("Conversions > 0")
.forDateRange("YESTERDAY")
.orderBy("ExternalCustomerId ASC");
var accountIterator = accountSelector.get();
Logger.log('アカウント件数 : ' + accountIterator.totalNumEntities());
// アカウント単位でデータ抽出
while(accountIterator.hasNext()) {
var account = accountIterator.next();
// MCC内に存在する対象のアカウント情報を取得
MccApp.select(account);
// main()内にて宣言したfunctionを実行する
callback(account);
}
}
/**
* アカウントレポートデータ取得
*/
function getAccountData() {
var data = [];
var rows = AdWordsApp.report(
'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, Ctr, Conversions'
+ ' FROM ACCOUNT_PERFORMANCE_REPORT '
+ ' DURING YESTERDAY'
).rows();
while (rows.hasNext()) {
var row = rows.next();
try {
data['ExternalCustomerId'] = row['ExternalCustomerId'];
data['CustomerDescriptiveName'] = row['CustomerDescriptiveName'];
data['Impressions'] = row['Impressions'];
data['Clicks'] = row['Clicks'];
data['Cost'] = row['Cost'];
data['Ctr'] = row['Ctr'];
data['Conversions'] = row['Conversions'];
} catch (ex) {
Logger.log('Exception:'+ex);
}
}
return data;
}
/**
* 受け取ったデータをHTML形式にして返します
* @param {attay} data
*/
function shapeDataToHtml(data){
// 日付取得
var d = new Date();
d.setDate(d.getDate() - 1);
// 件名設定
var subject = '【CVあり】アカウントレポート_' + Utilities.formatDate(d, 'JST', 'yyyy年MM月dd日');
var body = [];
body.push('<h1>昨日のCVが1以上のアカウント一覧</h1>');
body.push('<table border="1">');
// タイトル表示
body.push(' \<tr>');
body.push('td width="80" style="background: #a3daff">アカウントID</td>');
body.push('<td width="500" style="background: #a3daff">アカウント名</td>');
body.push('<td width="100" style="background: #a3daff">コンバージョン</td>');
body.push('<td width="80" style="background: #a3daff">表示回数</td>');
body.push('<td width="80" style="background: #a3daff">クリック数</td>');
body.push('<td width="80" style="background: #a3daff">費用</td>');
body.push('<td width="80" style="background: #a3daff">CTR</td>');
body.push('<td width="80" style="background: #a3daff">CPC</td>');
body.push('</tr>');
// データ表示
for(var i = 0; i < data.length; i++) {
body.push('<tr>');
body.push('<td>'+data[i]['ExternalCustomerId']+'</td>');
body.push('<td>'+data[i]['CustomerDescriptiveName']+'</td>');
body.push('<td>'+data[i]['Conversions']+'</td>');
body.push('<td>'+data[i]['Impressions']+'</td>');
body.push('<td>'+data[i]['Clicks']+'</td>');
body.push('<td>'+data[i]['Cost']+'</td>');
body.push('<td>'+data[i]['Ctr']+'</td>');
body.push('<td>'+(Math.round(getRatio(toInteger(data[i]['Cost']), toInteger(data[i]['Clicks']), false)))+'</td>');
body.push('</tr>');
}
body.push("</table>");
return { "subject": subject, "body": body.join('\n') };
}
/**
* メール送信
* @param {array} mail_date
*/
function sendmail(mail_date) {
MailApp.sendEmail(MAIL_ADDRESS
,mail_date["subject"]
,''
, {htmlBody: mail_date["body"]} );
Logger.log('メールを送信しました。 件名:' + mail_date["subject"]);
}
/**
* 文字列を受け取り、数値で返します
* @param {string} value 数字の文字列
*/
function toInteger(value) {
//undefineの場合
if(value === void 0){
return 0;
}
try {
return parseInt(value.split(',').join(''), 10)
} catch (ex) {
Logger.log('[ERROR] function:toInteger value='+value+', ex='+ex);
return 0;
}
}
/**
* 割り算した結果を返す
* 分子または分母が0の場合は、0を返します
* @param {int} bunshi 割られる数
* @param {int} bunbo 割る数
* @param {boolean} isPercent true:%表記で返す/false:100を掛けない元データで返す
*/
function getRatio(bunshi, bunbo, isPercent) {
if (bunshi == 0 || bunbo == 0) { return 0; }
if (isPercent) {
return bunshi / bunbo * 100;
}
return bunshi / bunbo;
}
/**
* 前日にCVが上がったアカウント情報をメールで送信します
*/
// メールアドレス ★変更してください★
var MAIL_ADDRESS = ['AAAAAAAAAA@AAA.co.jp'
,'BBBBBBBBBB@BBB.co.jp'];
// レポート出力対象CustomerID ★変更してください★
var MANAGER_CUSTOMER_ID = '1234567890';
function main() {
// データを取得
var allData = [];
procAccountDataToMail(function(account) {
// アカウントレポートデータ取得
var data = getAccountData(account);
// 全データをため込む
allData.push(data);
});
// データをHTMLに加工
var mail_date = shapeDataToHtml(allData);
// メール送信
sendmail(mail_date);
}
function procAccountDataToMail(callback) {
// アカウント情報を取得
var accountSelector = MccApp.accounts()
.withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'")
.withCondition("Conversions > 0")
.forDateRange("YESTERDAY")
.orderBy("ExternalCustomerId ASC");
var accountIterator = accountSelector.get();
Logger.log('アカウント件数 : ' + accountIterator.totalNumEntities());
// アカウント単位でデータ抽出
while(accountIterator.hasNext()) {
var account = accountIterator.next();
// MCC内に存在する対象のアカウント情報を取得
MccApp.select(account);
// main()内にて宣言したfunctionを実行する
callback(account);
}
}
/**
* アカウントレポートデータ取得
*/
function getAccountData() {
var data = [];
var rows = AdWordsApp.report(
'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, Ctr, Conversions'
+ ' FROM ACCOUNT_PERFORMANCE_REPORT '
+ ' DURING YESTERDAY'
).rows();
while (rows.hasNext()) {
var row = rows.next();
try {
data['ExternalCustomerId'] = row['ExternalCustomerId'];
data['CustomerDescriptiveName'] = row['CustomerDescriptiveName'];
data['Impressions'] = row['Impressions'];
data['Clicks'] = row['Clicks'];
data['Cost'] = row['Cost'];
data['Ctr'] = row['Ctr'];
data['Conversions'] = row['Conversions'];
} catch (ex) {
Logger.log('Exception:'+ex);
}
}
return data;
}
/**
* 受け取ったデータをHTML形式にして返します
* @param {attay} data
*/
function shapeDataToHtml(data){
// 日付取得
var d = new Date();
d.setDate(d.getDate() - 1);
// 件名設定
var subject = '【CVあり】アカウントレポート_' + Utilities.formatDate(d, 'JST', 'yyyy年MM月dd日');
var body = [];
body.push('<h1>昨日のCVが1以上のアカウント一覧</h1>');
body.push('<table border="1">');
// タイトル表示
body.push(' \<tr>');
body.push('td width="80" style="background: #a3daff">アカウントID</td>');
body.push('<td width="500" style="background: #a3daff">アカウント名</td>');
body.push('<td width="100" style="background: #a3daff">コンバージョン</td>');
body.push('<td width="80" style="background: #a3daff">表示回数</td>');
body.push('<td width="80" style="background: #a3daff">クリック数</td>');
body.push('<td width="80" style="background: #a3daff">費用</td>');
body.push('<td width="80" style="background: #a3daff">CTR</td>');
body.push('<td width="80" style="background: #a3daff">CPC</td>');
body.push('</tr>');
// データ表示
for(var i = 0; i < data.length; i++) {
body.push('<tr>');
body.push('<td>'+data[i]['ExternalCustomerId']+'</td>');
body.push('<td>'+data[i]['CustomerDescriptiveName']+'</td>');
body.push('<td>'+data[i]['Conversions']+'</td>');
body.push('<td>'+data[i]['Impressions']+'</td>');
body.push('<td>'+data[i]['Clicks']+'</td>');
body.push('<td>'+data[i]['Cost']+'</td>');
body.push('<td>'+data[i]['Ctr']+'</td>');
body.push('<td>'+(Math.round(getRatio(toInteger(data[i]['Cost']), toInteger(data[i]['Clicks']), false)))+'</td>');
body.push('</tr>');
}
body.push("</table>");
return { "subject": subject, "body": body.join('\n') };
}
/**
* メール送信
* @param {array} mail_date
*/
function sendmail(mail_date) {
MailApp.sendEmail(MAIL_ADDRESS
,mail_date["subject"]
,''
, {htmlBody: mail_date["body"]} );
Logger.log('メールを送信しました。 件名:' + mail_date["subject"]);
}
/**
* 文字列を受け取り、数値で返します
* @param {string} value 数字の文字列
*/
function toInteger(value) {
//undefineの場合
if(value === void 0){
return 0;
}
try {
return parseInt(value.split(',').join(''), 10)
} catch (ex) {
Logger.log('[ERROR] function:toInteger value='+value+', ex='+ex);
return 0;
}
}
/**
* 割り算した結果を返す
* 分子または分母が0の場合は、0を返します
* @param {int} bunshi 割られる数
* @param {int} bunbo 割る数
* @param {boolean} isPercent true:%表記で返す/false:100を掛けない元データで返す
*/
function getRatio(bunshi, bunbo, isPercent) {
if (bunshi == 0 || bunbo == 0) { return 0; }
if (isPercent) {
return bunshi / bunbo * 100;
}
return bunshi / bunbo;
}
解説
アカウント情報を取得
33行目にて、対象となるアカウント情報を取得しています。
var accountSelector = MccApp.accounts()
.withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'")
.withCondition("Conversions > 0")
.forDateRange("YESTERDAY")
.orderBy("ExternalCustomerId ASC");
var accountIterator = accountSelector.get();
var accountSelector = MccApp.accounts()
.withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'")
.withCondition("Conversions > 0")
.forDateRange("YESTERDAY")
.orderBy("ExternalCustomerId ASC");
var accountIterator = accountSelector.get();
var accountSelector = MccApp.accounts()
.withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'")
.withCondition("Conversions > 0")
.forDateRange("YESTERDAY")
.orderBy("ExternalCustomerId ASC");
var accountIterator = accountSelector.get();
withConditionは複数設定することが出来ます。
例えば表示回数が100回以上のデータを取得する際には、.withCondition(“Impressions >= 100”)と表記します。
.forDateRange(“YESTERDAY”) は他にも .forDateRange(“LAST_MONTH”)で先月を指定したり、 .forDateRange(“20180101,20181231”) で期間指定することも出来ます。
セレクタに関する詳細は、公式ガイドをご覧ください。
アカウントレポートを取得
56行目から始まるfunctionにて、1アカウントずつのアカウントレポート情報を取得しています。
私が初めに躓いたのは、取得する項目の組み合わせによって1つのクエリで取得できないことがある、というものでした。
例えば今回のクエリに、1つ項目を足してみましょう。
var rows = AdWordsApp.report(
'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, '
'Ctr, Conversions, ConversionTypeName' //★ConversionTypeNameを足しました
+ ' FROM ACCOUNT_PERFORMANCE_REPORT '
var rows = AdWordsApp.report(
'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, '
'Ctr, Conversions, ConversionTypeName' //★ConversionTypeNameを足しました
+ ' FROM ACCOUNT_PERFORMANCE_REPORT '
+ ' DURING YESTERDAY'
).rows();
var rows = AdWordsApp.report(
'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, '
'Ctr, Conversions, ConversionTypeName' //★ConversionTypeNameを足しました
+ ' FROM ACCOUNT_PERFORMANCE_REPORT '
+ ' DURING YESTERDAY'
).rows();
実行すると、エラーINVALID_FIELD_NAME_FOR_REPORTが発生してしまいます。
これは、ImpressionsやClicksと同時にConversionTypeNameを取得することが出来ないためです。
Google広告画面にてMCCアカウントから「アカウント>掲載結果」の表を出してみてください。
ConversionTypeNameを足す前のクエリは、この画面にて分割表示をせず、コンバージョンでフィルターを掛けた結果と同等です。
ですが「分割>コンバージョン>コンバージョンアクション」を設定すると、各コンバージョン(ConversionTypeName)単位でのコンバージョン数が表示され、その行のImpressionsやClicksには「-」が表示されています。
つまり、Google広告画面上でコンバージョン単位でのImpressionsやClicksが表示できないように、クエリでもImpressionsやClicksと同時にConversionTypeNameを取得することが出来ないのです。
広告運用をしたことが無い私にとって、この理解はなかなか難しかったです。
データをHTML形式に整形
85行目からのfunctionでは、全アカウントのデータをHTML形式に整形しています。
今回はタイトルだけに背景色を設定いたしましたが、
例えばコンバージョンが50以上のセルに背景色を設定すれば特定のデータが見やすくなりますし、
全アカウントのデータを表示回数順に並び替えてランキング形式で表示することも可能です。
ぜひ色々と工夫してみてください。
メール送信
130行目からのfunctionでは、HTML形式に整形したデータをメール送信しています。
Ads ScriptにはMailApp.sendEmailが用意されていますので、引数にメールアドレスと件名、本文を設定するだけでメールが送信されます。
メールアドレスは複数設定することが出来ますので、各担当者へ同じメールを送信することが可能です。
▼スクリプト実行後には、このようなメールが届くはずです。(値はマスクさせていただきました)

最後に
タイトルに入門とつけておりますが、かなり実用的な内容になっております。
スクリプトの自動実行を早朝に設定し昨日の実績をメールで送信したり、
一定期間内のCVが0のアカウントをアラートとしてメール通知することも出来ます。
ぜひ皆様の業務に合った内容に工夫してみてください。
*シリーズ*
Google Ads Scripts入門①
Google Ads Scripts入門②
エンジニア大募集中
Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。
興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。
ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
Join Us !
ウエディングパークでは、一緒に働く仲間を募集しています!
ご興味ある方は、お気軽にお問合せください(カジュアル面談から可)
採用情報を見る