显示带有标签的帖子 搜索引擎. 显示所有帖子
显示带有标签的帖子 搜索引擎. 显示所有帖子

2013年6月17日,星期一

查找关键字,广告组和广告中的异常

2013年7月23日更新:添加了向广告添加标签的功能。这可能对创意测试很有帮助。

在一个 最近来自SearchEngineLand的帖子,拉里·金(Larry Kim)希望PPC经理不要再偷懒。除了链接诱饵,他还有一些优点。我认为花费在倾倒客户电子表格上的大部分时间实际上是在试图回答“异常在哪里?”这个简单的问题。

异常是关键字或广告组,其效果似乎与同一个广告组或广告系列中的兄弟姐妹不同。也许您有一个包含15个关键字和一个或两个关键字的广告组,似乎获得了几乎所有的点击。或者,也许您只是拥有一个广告组,似乎正在占用预算的很大一部分,而您想对其进行更多控制。在这两种情况下,您都需要某种方法来快速确定要对哪些关键字或广告组进行操作。

因此,我创建了以下脚本来帮助我做到这一点。它内含一些统计信息,可以计算一组实体(广告组或关键字)的均值和标准差。然后,我将标签应用于似乎与均值相差两个标准偏差以上的任何实体,这表明该实体的性能要比其同级产品更好或更差。这样,我可以在自己的帐户中轻松地对这些实体采取措施。

该脚本还将每天向您发送一封电子邮件,其中包含它认为是异常的实体的摘要。您的目标是通过将这些问题转移到自己的广告系列和广告组中,或者甚至完全消除它们(可能是负面的)来解决这些问题。

目前,脚本将检查AdWordsApp.stats对象中可用的每个指标。可以轻松对其进行修改,以检查诸如每次转化费用或每次展示利润。您将在下面的代码中看到,您可以在其中删除也不感兴趣的统计信息。

需要警告的一点是,我并没有声称自己是统计学家,所以这可能不是一种看待事物的有效方法。希望它可以帮助您快速找到帐户中的问题并加以解决,以便您可以将更多时间花在懒惰上:)

另外,我欢迎您对此脚本提出意见和建议。对其他人有用吗?

谢谢,
拉斯

/**************************************
* Find the Anomalies
* Created By:  拉斯  Savage
* Version: 1.2
* Changelog v1.2
*  - Fixed divide by 0 错误
*  - Changed SIG_FIGS to DECIMAL_PLACES
* Changelog v1.1
*  - Added ability to tag  广告  anomalies as well
* FreeAdWordsScripts.com
**************************************/
var DATE_RANGE = 'LAST_30_DAYS';
var DECIMAL_PLACES = 3;
var STANDARD_DEVIATIONS = 2;
var TO = ['[email protected]_domain.com'];
 
function main() {
  // This will  广告 d 标签 to and send  电子邮件 s about 广告群组, 关键字 and  广告 . Remove any if you like.
  var levels_to_tag = ['adgroup','keyword','ad'];
  for(var x in levels_to_tag) {
    var report = getContentRows(levels_to_tag[x]);
    var 实体_map = buildEntityMap(levels_to_tag[x]);
    for(var parent_id in 实体_map) {
      var child_list = 实体_map[parent_id];
      var  统计资料 _list = Object.keys(child_list[0].stats);
      for(var i in  统计资料 _list) {
        var  意思  = getMean(child_list,stats_list[i]);
        var stand_dev = getStandardDev(child_list,mean,stats_list[i]);
        var  标签 _name =  统计资料 _list[i]+"_anomaly";
        report +=  广告 dLabelToAnomalies(child_list,mean,stand_dev,stats_list[i],label_name,levels_to_tag[x]);
      }
    }
    sendResultsViaEmail(report,levels_to_tag[x]);
  }
}
  
//Takes a report and the level of 报告 and sends and  电子邮件 
//with the report as an  在 tachment.
function sendResultsViaEmail(report,level) {
  var rows = report.match(/\n/g).length - 1;
  if(rows == 0) { return; }
  var options = { 附件: [Utilities.newBlob(report, 'text/csv', level+"_anomalies_"+_getDateString()+'.csv')] };
  var  电子邮件 _body = "There are " + rows + " " + level + "s that have abnormal performance. See  在 tachment for details.";
  var subject = 'Abnormal ' + _initCap(level) + ' Entities Report - ' + _getDateString();
  for(var i in TO) {
    MailApp.sendEmail(TO[i], subject,  电子邮件 _body, options);
  }
}
  
//Helper function to return a single row of the report formatted correctly
function toReportRow(entity,level,label_name) {
  var ret_val = [AdWordsApp.currentAccount().getCustomerId(),
                 entity.getCampaign().getName()];
  ret_val.push( (level == 'adgroup') ? 实体.getName() : 实体.getAdGroup().getName() );
  if(level == 'keyword') {
    ret_val = ret_val.concat([entity.getText(),entity.getMatchType()]); 
  } else if(level == 'ad') {
    ret_val = ret_val.concat([entity.getHeadline(),entity.getDescription1(),entity.getDescription2(),entity.getDisplayUrl()]); 
  }
  ret_val.push(label_name);
  return '"' + ret_val.join('","') + '"\n';
}
  
//Helper function to return the column headings for the report
function getContentRows(level) {
  var ret_val = ['AccountId','CampaignName','AdGroupName'];
  if(level == 'keyword') {
    ret_val = ret_val.concat(['KeywordText','MatchType']); 
  } else if(level == 'ad') {
    ret_val = ret_val.concat(['Headline','Description1','Description2','DisplayUrl']);
  }
  ret_val.push('LabelName');
  return '"' + ret_val.join('","') + '"\n';
}
  
//Function to  广告 d the 标签 to the entities based on the 标准偏差 and  意思 .
//It returns a  CSV  formatted string for 报告
function  广告 dLabelToAnomalies(entites,mean,sd,stat_key,label_name,level) {
  createLabelIfNeeded(label_name);
  var report = '';
  for(var i in entites) {
    var 实体 = entites[i]['entity'];
    var deviation = Math.abs(entites[i]['stats'][stat_key] -  意思 );
    if(sd != 0 && deviation/sd >= STANDARD_DEVIATIONS) {
      entity.applyLabel(label_name);
      report += toReportRow(entity,level,label_name);
    } else {
      entity.removeLabel(label_name);
    }
  }
  return report;
}
  
//This is a helper function to 创造 the  标签  if it does not already exist
function 创造LabelIfNeeded(name) {
  if(!AdWordsApp.labels().withCondition("Name = '"+name+"'").get().hasNext()) {
    AdWordsApp.createLabel(name);
  }
}
  
//This function returns the 标准偏差 for a set of entities
//The stat key determines which stat to calculate it for
function getStandardDev(entites,mean,stat_key) {
  var total = 0;
  for(var i in entites) {
    total += Math.pow(entites[i]['stats'][stat_key] -  意思 ,2);
  }
  if(Math.sqrt(entites.length-1) == 0) {
    return 0;
  }
  return round(Math.sqrt(total)/Math.sqrt(entites.length-1));
}
  
//Returns the  意思  (average) for the set of entities
//Again, stat key determines which stat to calculate this for
function getMean(entites,stat_key) {
  var total = 0;
  for(var i in entites) {
    total += entites[i]['stats'][stat_key];
  }
  if(entites.length == 0) {
    return 0;
  }
  return round(total/entites.length);
}
  
//This function returns a map of the entities that I am processing.
//The format for the map can be found on the first line.
//It is  意思 t to work on AdGroups and Keywords
function buildEntityMap(entity_type) {
  var map = {}; // { parent_id : [ { 实体 : 实体,  统计资料  : 实体_stats } ], ... }
  var iter = getIterator(entity_type);
  while(iter.hasNext()) {
    var 实体 = iter.next();
    var  统计资料  = 实体.getStatsFor(DATE_RANGE);
    var  统计资料 _map = getStatsMap(stats);
    var parent_id = getParentId(entity_type,entity);
    if(map[parent_id]) { 
      map[parent_id].push({entity : 实体,  统计资料  :  统计资料 _map});
    } else {
      map[parent_id] = [{entity : 实体,  统计资料  :  统计资料 _map}];
    }
  }
  return map;
}
  
//Given an 实体 type (adgroup or 关键词) this will return the parent id
function getParentId(entity_type,entity) {
  switch(entity_type) {
    case 'adgroup' :
      return 实体.getCampaign().getId();
    case 'keyword':
      return 实体.getAdGroup().getId();
    case 'ad':
      return 实体.getAdGroup().getId();
  }
}
  
//Given an 实体 type this will return the iterator for that.
function getIterator(entity_type) {
  switch(entity_type) {
    case 'adgroup' :
      return AdWordsApp.adGroups().forDateRange(DATE_RANGE).withCondition("Impressions > 0").get();
    case 'keyword' :
      return AdWordsApp.keywords().forDateRange(DATE_RANGE).withCondition("Impressions > 0").get();
    case 'ad' :
      return AdWordsApp.ads().forDateRange(DATE_RANGE).withCondition("Impressions > 0").get();
  }
}
  
//This returns a map of all the  统计资料  for a given 实体.
//You can 评论 out the things you don't really care about.
function getStatsMap(stats) {
  return { // You can 评论 these out as needed.
          avg_cpc :  统计资料 .getAverageCpc(),
          avg_cpm :  统计资料 .getAverageCpm(),
          avg_pv :  统计资料 .getAveragePageviews(),
          avg_pos :  统计资料 .getAveragePosition(),
          avg_tos :  统计资料 .getAverageTimeOnSite(),
          bounce :  统计资料 .getBounceRate(),
          clicks :  统计资料 .getClicks(),
          cv :  统计资料 .getConversionRate(),
          conv :  统计资料 .getConversions(),
          cost :  统计资料 .getCost(),
          ctr :  统计资料 .getCtr(),
          imps :  统计资料 .getImpressions()
         };
}
  
//Helper function to format todays date
function _getDateString() {
  return Utilities.formatDate((new Date()), AdWordsApp.currentAccount().getTimeZone(), "yyyy-MM-dd");
}
  
//Helper function to capitalize the first letter of a string.
function _initCap(str) {
  return str.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); });
}

// A helper function to make rounding a little easier
function round(value) {
  var decimals = Math.pow(10,DECIMAL_PLACES);
  return Math.round(value*decimals)/decimals;
}

2013年2月26日,星期二

在Google电子表格中存储帐户级别的质量得分

EDIT 2013-04-22:签出此脚本的更新版本,该脚本存储 帐户,广告系列和广告组一级的质量得分.

EDIT 2013-03-18:我在.orderBy()子句中添加了DESC,以便它将印象从高到低排序。

我在读一篇关于 搜索EngineLand.com 前几天关于存储和 analyzing your 帐户级别的质量得分。

本文的作者提供了一个脚本,但是我认为我可以很容易地提出自己的版本。 在以下脚本中,我使用相同的逻辑来计算帐户一级的质量得分,但是我查看了过去30天内所有具有印象的广告系列和所有关键字。

根据您的帐户大小,这可能是太多数据 在AdWords规定的30分钟内进行分析,但是按展示次数排序应该可以使您获得展示次数最高的50000个关键字(同样,另一个AdWords限制)。

您可以在这里找到我存储数据的电子表格: //docs.google.com/spreadsheet/ccc?key=0Aotb6eheEOpodGNKMW1UWkZKekc5NWxkR3Zra3lzWVE

最好的选择是复制该工作表,擦除我的数据(标题除外),然后将新的电子表格网址复制到脚本中。如果您有多个要运行的帐户,则可以将数据存储在同一电子表格中,但可以为帐户名称添加其他标签。然后更新ACCOUNT_NAME变量,您应该已经准备就绪。

谢谢,
拉斯

/***************************************
* Store Account Level Quality Score in Google Spreadsheet.
* Version 1.1
* ChangeLog v1.1
*  - Changed ACCOUNT_NAME to SHEET_NAME and 更新d the default value.
*  - Removed getSpreadsheet function
*
* Created By:  拉斯  Savage
* Based on script originally found  在 : http://goo.gl/rTHbF
* FreeAdWordsScripts.com
*********************************/
function main() {
  var SPREADSHEET_URL = "Your Spreadsheet Url Goes Here";
  var SHEET_NAME = 'Sheet1';
  var today = new Date();
  var date_str = [today.getFullYear(),(today.getMonth() + 1),today.getDate()].join("-");
  
  var 电子表格 = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  var  s _sheet = 电子表格.getSheetByName(SHEET_NAME);
  
  var kw_iter = AdWordsApp.keywords()
    .withCondition("Status = ENABLED")
    .forDateRange("LAST_30_DAYS")
    .withCondition("Impressions > 0")
    .orderBy("Impressions DESC")
    .withLimit(50000)
    .get();

  var tot_imps_weighted_qs = 0;
  var tot_imps = 0;
  
  while(kw_iter.hasNext()) {
    var kw = kw_iter.next();
    var kw_stats = kw.getStatsFor("LAST_30_DAYS");
    var imps = kw_stats.getImpressions();
    var  s  = kw.getQualityScore();
    tot_imps_weighted_qs += (qs * imps);
    tot_imps += imps;
  }
    
  var acct_qs = tot_imps_weighted_qs / tot_imps;
  
   s _sheet.appendRow([date_str,acct_qs]);
}