Настройка сбора сырых данных о кампаниях Google Ads с авторазметкой

Собирая в Google BigQuery данные о сессиях с помощью алгоритма OWOX BI, вы также можете собирать в BigQuery отчеты о кампаниях Google Ads с авторазметкой данных, получая их из параметра gclid.

Обратите вниманиеНастройка отправки сырых отчетов Google Ads в Google BigQuery необходима, если у вас настроена авторазметка кампаний Google Ads. Без настройки отчетов, данные из параметра gclid, необходимые для получения информации об источниках трафика в кампаниях с авторазметкой, не будут доступны.

Если вы используете только ручную разметку кампаний Google Ads (с помощью utm-меток), настраивать дополнительные отчеты не нужно. Данные о расходах будут равномерно распределены по utm-меткам.

Если вы одновременно используете и ручной, и автоматический способы разметки объявлений, ознакомьтесь с этой статьей.

Зачем собирать сырые данные о кампаниях Google Ads

Если вы используете авторазметку в кампаниях Google Ads, URL страницы содержит только параметр gclid, а названия кампаний, ключевых слов и других параметров можно получить только при объединении с дополнительными отчетами сырых данных Google Ads в BigQuery.

Данные о кампаниях также необходимы для расчета атрибутированной стоимости сессии (поле attributedAdCost).

Перед тем, как настроить поток сбора данных о поведении пользователей, нужно сделать выгрузку из всех аккаунтов Google Ads. Если не сделать этого, то исторические данные о разметке не обновятся автоматически.

Если вы не подключили новый аккаунт Google Ads к выгрузке Data Transfer, вы можете написать нам письмо на bi@owox.com и мы поможем вам с обновлением данных.

Список подключённых аккаунтов Google Ads вы можете посмотреть в Google Analytics. Вкладка Администратор → Настройки представления:image11.png

Способы настройки сбора отчетов

OWOX BI собирает данные о рекламных кампаниях Google Ads с авторазметкой в таблицы с данными о сессиях owoxbi_sessions. Собрать данные можно двумя способами:

Способ 1. Настроить загрузку всех отчетов Google Ads в Google BigQuery через BigQuery Data Transfer Service for Google Ads.

Плюсы способа: 

  • Нативная интеграция, настраивается в несколько кликов и собирает все сырые данные.
  • Автоматически выгружает исторические данные за предыдущие месяцы.
  • Загрузка через Data Transefer — бесплатна.

Данные из нескольких аккаунтов можно загрузить как в один набор данных Google BigQuery, так и в отдельные наборы данных для каждого аккаунта. Выберите тот способ, который удобен вам.  

Способ 2. Настроить отчет Click Performance Report с помощью Google Ads script.

Плюс: 

  • Способ бесплатный.

Минусы:

  • Нет возможности запустить скрипт за промежуток дат. Отчеты можно собирать только за предыдущий день.
  • Нужно кастомизировать код скрипта под свой аккаунт. В этой статье есть пример скрипта — используйте его, но вам все равно может понадобиться настроить код.

Детальнее об этих способах читайте чуть ниже. Мы рекомендуем использовать загрузку через Data Transfer — это проще, удобнее, и у этого способа нет недостатков по сравнению с Google Ads script.

Отчеты Google Ads собирают в таблицы owoxbi_sessions такие поля:

  • trafficSource.source: google
  • trafficSource.medium: cpc
  • trafficSource.campaign
  • trafficSource.keyword
  • trafficSource.adGroup
  • trafficSource.keywordMatchType

Значения google/cpc используются в соответствии с логикой обработки данных в Google Analytics.

Обратите вниманиеЕсли у вас несколько аккаунтов Google Ads, то для получения данных о рекламных кампаниях вам нужно настроить выгрузку отчетов для каждого аккаунта.

Читайте также: Как обновляются данные о кампаниях Google Ads с авторазметкой в таблицах с данными о сессиях

Загрузка отчетов с помощью BigQuery Data Transfer Service for Google Ads

Чтобы настроить выгрузку данных из AdWords API в Google BigQuery с помощью сервиса Data Transfer:

1. Активируйте Data Transfer в BigQuery, следуя этой инструкции.

2. После активации — настройте поток передачи данных в BigQuery, как описано здесь. Если у вас несколько аккаунтов Google Ads, которые подключены к управляющему аккаунту (manager account, MCC), то выгрузку для всех подключенных аккаунтов можно настроить из управляющего.

Загрузка данных начнется момента сохранения настроек либо в заданный вами момент времени в будущем.

Вы можете импортировать исторические данные из Google Ads в Google BigQuery. Для этого создайте backfill-запрос в BigQuery Data Transfer Service.
Обратите внимание, для корректной работы OWOX BI необходимы данные параметра gclid из Click Performance Report, который доступен только за последние 90 дней при выгрузке через Data Transfer. В связи с этим при создании backfill-запроса следует указывать период, не превышающий 90 дней c момента создания запроса.

3. В OWOX BI:

- Откройте страницу вашего потока

- Во вкладке Настройка, в разделе Сбор данных о сессиях, нажмите Изменить настройки

edit1_ru.png

- На экране Настройка сбора данных о сессиях на основе данных о хитах, включите Отчеты Google Ads и нажмите Изменить настройки

edit2_ru.png

- На открывшемся экране Данные кампаний Google Ads с авторазметкой, выберите Авторазметка (значения GCLID)BigQuery Data Transfer и укажите проект и набор данных, куда будут загружаться данные BigQuery Data Transfer. Подтвердите изменения, нажав Сохранить настройки на текущем экране.

save1_ru.png

- Вернувшись на экран Настройка сбора данных о сессиях на основе данных о хитах, снова нажмите Сохранить настройки, чтобы применить изменения к настройкам вашего потока.

save2_ru.png

 

Загрузка отчетов с помощью Google Ads scripts

Для экспорта данных в BigQuery можно использовать стандартный скрипт AdWords API:

Куда добавить скрипт?

  1. В Google Ads, в правой верхней части интерфейса, кликните на Tools.
  2. В колонке Bulk Actions кликните на ScriptsAds_scripts_1-2.png
  3. Нажмите на плюс, чтобы добавить скрипт: Ads_scripts_3.png
  4. В поле для скрипта скопируйте и вставьте скрипт, приведенный под скриншотом: Ads_scripts_4.png

Пример скрипта для одного аккаунта (не MCC). Скопируйте его и замените BQ project name и Your email на название вашего проекта в Google BigQuery ваш имейл соответственно:

/**
* @name Export Data to BigQuery
*
* @overview The Export Data to BigQuery script sets up a BigQuery
* dataset and tables, downloads a report from AdWords and then
* loads the report to BigQuery.
*
* @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
*
* @version 1.3
*/

var CONFIG = {
BIGQUERY_PROJECT_ID: 'BQ project name',
BIGQUERY_DATASET_ID: AdWordsApp.currentAccount().getCustomerId().replace(/-/g, '_'),

// Truncate existing data, otherwise will append.
TRUNCATE_EXISTING_DATASET: false,
TRUNCATE_EXISTING_TABLES: true,

// Lists of reports and fields to retrieve from AdWords.
REPORTS: [],

RECIPIENT_EMAILS: [
'Your email'
]
};

var report = {
NAME: 'CLICK_PERFORMANCE_REPORT', //https://developers.google.com/adwords/api/docs/appendix/reports/click-performance-report
CONDITIONS: '',
FIELDS: {'AccountDescriptiveName': 'STRING',
'AdFormat': 'STRING',
'AdGroupId': 'STRING',
'AdGroupName': 'STRING',
'AoiCountryCriteriaId': 'STRING',
'CampaignId': 'STRING',
'CampaignLocationTargetId': 'STRING',
'CampaignName': 'STRING',
'CampaignStatus': 'STRING',
'Clicks': 'INTEGER',
'ClickType': 'STRING',
'CreativeId': 'STRING',
'CriteriaId': 'STRING',
'CriteriaParameters': 'STRING',
'Date': 'DATE',
'Device': 'STRING',
'ExternalCustomerId': 'STRING',
'GclId': 'STRING',
'KeywordMatchType': 'STRING',
'LopCountryCriteriaId': 'STRING',
'Page': 'INTEGER'
},
DATE_RANGE: new Date(new Date().setDate(new Date().getDate()-1)).toISOString().slice(0, 10).replace(/-/g, "")+','+new Date(new Date().setDate(new Date().getDate()-1)).toISOString().slice(0, 10).replace(/-/g, ""),
DATE: new Date(new Date().setDate(new Date().getDate()-1)).toISOString().slice(0, 10).replace(/-/g, "")
};

//Regular export
CONFIG.REPORTS.push(JSON.parse(JSON.stringify(report)));

//One-time historical export
//for(var i=2;i<91;i++){
// report.DATE_RANGE = new Date(new Date().setDate(new Date().getDate()-i)).toISOString().slice(0, 10).replace(/-/g, "")+','+new Date(new Date().setDate(new Date().getDate()-i)).toISOString().slice(0, 10).replace(/-/g, "");
// report.DATE = new Date(new Date().setDate(new Date().getDate()-i)).toISOString().slice(0, 10).replace(/-/g, "");
// CONFIG.REPORTS.push(JSON.parse(JSON.stringify(report)));
//}

/**
* Main method
*/
function main() {
createDataset();
for (var i = 0; i < CONFIG.REPORTS.length; i++) {
var reportConfig = CONFIG.REPORTS[i];
createTable(reportConfig);
}

var jobIds = processReports();
waitTillJobsComplete(jobIds);
sendEmail(jobIds);
}


/**
* Creates a new dataset.
*
* If a dataset with the same id already exists and the truncate flag
* is set, will truncate the old dataset. If the truncate flag is not
* set, then will not create a new dataset.
*/
function createDataset() {
if (datasetExists()) {
if (CONFIG.TRUNCATE_EXISTING_DATASET) {
BigQuery.Datasets.remove(CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID, {'deleteContents' : true});
Logger.log('Truncated dataset.');
} else {
Logger.log('Dataset %s already exists. Will not recreate.',
CONFIG.BIGQUERY_DATASET_ID);
return;
}
}

// Create new dataset.
var dataSet = BigQuery.newDataset();
dataSet.friendlyName = CONFIG.BIGQUERY_DATASET_ID;
dataSet.datasetReference = BigQuery.newDatasetReference();
dataSet.datasetReference.projectId = CONFIG.BIGQUERY_PROJECT_ID;
dataSet.datasetReference.datasetId = CONFIG.BIGQUERY_DATASET_ID;

dataSet = BigQuery.Datasets.insert(dataSet, CONFIG.BIGQUERY_PROJECT_ID);
Logger.log('Created dataset with id %s.', dataSet.id);
}

/**
* Checks if dataset already exists in project.
*
* @return {boolean} Returns true if dataset already exists.
*/
function datasetExists() {
// Get a list of all datasets in project.
var datasets = BigQuery.Datasets.list(CONFIG.BIGQUERY_PROJECT_ID);
var datasetExists = false;
// Iterate through each dataset and check for an id match.
if (datasets.datasets != null) {
for (var i = 0; i < datasets.datasets.length; i++) {
var dataset = datasets.datasets[i];
if (dataset.datasetReference.datasetId == CONFIG.BIGQUERY_DATASET_ID) {
datasetExists = true;
break;
}
}
}
return datasetExists;
}

/**
* Creates a new table.
*
* If a table with the same id already exists and the truncate flag
* is set, will truncate the old table. If the truncate flag is not
* set, then will not create a new table.
*
* @param {Object} reportConfig Report configuration including report name,
* conditions, and fields.
*/
function createTable(reportConfig) {
var tableName = reportConfig.NAME+reportConfig.DATE;
if (tableExists(tableName)) {
if (CONFIG.TRUNCATE_EXISTING_TABLES) {
BigQuery.Tables.remove(CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID, tableName);
Logger.log('Truncated table %s.', tableName);
} else {
Logger.log('Table %s already exists. Will not recreate.',
tableName);
return;
}
}

// Create new table.
var table = BigQuery.newTable();
var schema = BigQuery.newTableSchema();
var bigQueryFields = [];

// Add each field to table schema.
var fieldNames = Object.keys(reportConfig.FIELDS);
for (var i = 0; i < fieldNames.length; i++) {
var fieldName = fieldNames[i];
var bigQueryFieldSchema = BigQuery.newTableFieldSchema();
bigQueryFieldSchema.description = fieldName;
bigQueryFieldSchema.name = fieldName;
bigQueryFieldSchema.type = reportConfig.FIELDS[fieldName];

bigQueryFields.push(bigQueryFieldSchema);
}

schema.fields = bigQueryFields;
table.schema = schema;
table.friendlyName = tableName;

table.tableReference = BigQuery.newTableReference();
table.tableReference.datasetId = CONFIG.BIGQUERY_DATASET_ID;
table.tableReference.projectId = CONFIG.BIGQUERY_PROJECT_ID;
table.tableReference.tableId = tableName;

table = BigQuery.Tables.insert(table, CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID);

Logger.log('Created table with id %s.', table.id);
}

/**
* Checks if table already exists in dataset.
*
* @param {string} tableId The table id to check existence.
*
* @return {boolean} Returns true if table already exists.
*/
function tableExists(tableId) {
// Get a list of all tables in the dataset.
var tables = BigQuery.Tables.list(CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID);
var tableExists = false;
// Iterate through each table and check for an id match.
if (tables.tables != null) {
for (var i = 0; i < tables.tables.length; i++) {
var table = tables.tables[i];
if (table.tableReference.tableId == tableId) {
tableExists = true;
break;
}
}
}
return tableExists;
}

/**
* Process all configured reports
*
* Iterates through each report to: retrieve AdWords data,
* backup data to Drive (if configured), load data to BigQuery.
*
* @return {Array.<string>} jobIds The list of all job ids.
*/
function processReports() {
var jobIds = [];

// Iterate over each report type.
for (var i = 0; i < CONFIG.REPORTS.length; i++) {
var reportConfig = CONFIG.REPORTS[i];
Logger.log('Running report %s', reportConfig.NAME);
// Get data as csv
var csvData = retrieveAdwordsReport(reportConfig);
//Logger.log(csvData);
// Convert to Blob format.
var blobData = Utilities.newBlob(csvData, 'application/octet-stream');
// Load data
var jobId = loadDataToBigquery(reportConfig, blobData);
jobIds.push(jobId);
}
return jobIds;
}

/**
* Retrieves AdWords data as csv and formats any fields
* to BigQuery expected format.
*
* @param {Object} reportConfig Report configuration including report name,
* conditions, and fields.
*
* @return {string} csvData Report in csv format.
*/
function retrieveAdwordsReport(reportConfig) {
var fieldNames = Object.keys(reportConfig.FIELDS);
var query = 'SELECT ' + fieldNames.join(', ') +
' FROM ' + reportConfig.NAME + '' + reportConfig.CONDITIONS +
' DURING ' + reportConfig.DATE_RANGE;
Logger.log(query);
var report = AdWordsApp.report(query);
var rows = report.rows();
var csvRows = [];
// Header row
csvRows.push(fieldNames.join(','));

// Iterate over each row.
while (rows.hasNext()) {
var row = rows.next();
var csvRow = [];
for (var i = 0; i < fieldNames.length; i++) {
var fieldName = fieldNames[i];
var fieldValue = row[fieldName].toString();
var fieldType = reportConfig.FIELDS[fieldName];
// Strip off % and perform any other formatting here.
if (fieldType == 'FLOAT' || fieldType == 'INTEGER') {
if (fieldValue.charAt(fieldValue.length - 1) == '%') {
fieldValue = fieldValue.substring(0, fieldValue.length - 1);
}
fieldValue = fieldValue.replace(/,/g,'');

if (fieldValue == '--' || fieldValue == 'Unspecified') {
fieldValue = '';
}
}
// Add double quotes to any string values.
if (fieldType == 'STRING') {
if (fieldValue == '--') {
fieldValue = '';
}
fieldValue = fieldValue.replace(/"/g, '""');
fieldValue = '"' + fieldValue + '"';
}
csvRow.push(fieldValue);
}
csvRows.push(csvRow.join(','));
}
Logger.log('Downloaded ' + reportConfig.NAME + ' with ' + csvRows.length +
' rows.');
return csvRows.join('\n');
}

/**
* Creates a BigQuery insertJob to load csv data.
*
* @param {Object} reportConfig Report configuration including report name,
* conditions, and fields.
* @param {Blob} data Csv report data as an 'application/octet-stream' blob.
*
* @return {string} jobId The job id for upload.
*/
function loadDataToBigquery(reportConfig, data) {
// Create the data upload job.
var job = {
configuration: {
load: {
destinationTable: {
projectId: CONFIG.BIGQUERY_PROJECT_ID,
datasetId: CONFIG.BIGQUERY_DATASET_ID,
tableId: reportConfig.NAME + reportConfig.DATE
},
skipLeadingRows: 1
}
}
};

var insertJob = BigQuery.Jobs.insert(job, CONFIG.BIGQUERY_PROJECT_ID, data);
Logger.log('Load job started for %s. Check on the status of it here: ' +
'https://bigquery.cloud.google.com/jobs/%s', reportConfig.NAME,
CONFIG.BIGQUERY_PROJECT_ID);
return insertJob.jobReference.jobId;
}

/**
* Polls until all jobs are 'DONE'.
*
* @param {Array.<string>} jobIds The list of all job ids.
*/
function waitTillJobsComplete(jobIds) {
var complete = false;
var remainingJobs = jobIds;
while (!complete) {
if (AdWordsApp.getExecutionInfo().getRemainingTime() < 5){
Logger.log('Script is about to timeout, jobs ' + remainingJobs.join(',') +
' are still incomplete.');
}
remainingJobs = getIncompleteJobs(remainingJobs);
if (remainingJobs.length == 0) {
complete = true;
}
if (!complete) {
Logger.log(remainingJobs.length + ' jobs still being processed.');
// Wait 5 seconds before checking status again.
Utilities.sleep(5000);
}
}
Logger.log('All jobs processed.');
}

/**
* Iterates through jobs and returns the ids for those jobs
* that are not 'DONE'.
*
* @param {Array.<string>} jobIds The list of job ids.
*
* @return {Array.<string>} remainingJobIds The list of remaining job ids.
*/
function getIncompleteJobs(jobIds) {
var remainingJobIds = [];
for (var i = 0; i < jobIds.length; i++) {
var jobId = jobIds[i];
var getJob = BigQuery.Jobs.get(CONFIG.BIGQUERY_PROJECT_ID, jobId);
if (getJob.status.state != 'DONE') {
remainingJobIds.push(jobId);
}
}
return remainingJobIds;
}


/**
* Sends a notification email that jobs have completed loading.
*
* @param {Array.<string>} jobIds The list of all job ids.
*/
function sendEmail(jobIds) {
var html = [];
html.push(
'<html>',
'<body>',
'<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'>Adwords data load to " +
"Bigquery report</div>",
'</td>',
'<td align=right>',
"<div style='font: normal 18pt verdana, sans-serif; " +
"padding: 3px 10px; color: white'>",
AdWordsApp.currentAccount().getCustomerId(),
'</tr>',
'</table>',
'<table width=800 cellpadding=0 border=1 cellspacing=0>',
"<tr bgcolor='#ddd'>",
"<td style='font: 12pt verdana, sans-serif; " +
'padding: 5px 0px 5px 5px; background-color: #ddd; ' +
"text-align: left'>Report</td>",
"<td style='font: 12pt verdana, sans-serif; " +
'padding: 5px 0px 5px 5px; background-color: #ddd; ' +
"text-align: left'>JobId</td>",
"<td style='font: 12pt verdana, sans-serif; " +
'padding: 5px 0px 5x 5px; background-color: #ddd; ' +
"text-align: left'>Rows</td>",
"<td style='font: 12pt verdana, sans-serif; " +
'padding: 5px 0px 5x 5px; background-color: #ddd; ' +
"text-align: left'>State</td>",
"<td style='font: 12pt verdana, sans-serif; " +
'padding: 5px 0px 5x 5px; background-color: #ddd; ' +
"text-align: left'>ErrorResult</td>",
'</tr>',
createTableRows(jobIds),
'</table>',
'</body>',
'</html>');

MailApp.sendEmail(CONFIG.RECIPIENT_EMAILS.join(','),
'Adwords data load to Bigquery Complete', '',
{htmlBody: html.join('\n')});
}

/**
* Creates table rows for email report.
*
* @param {Array.<string>} jobIds The list of all job ids.
*/
function createTableRows(jobIds) {
var html = [];
for (var i = 0; i < jobIds.length; i++) {
var jobId = jobIds[i];
var job = BigQuery.Jobs.get(CONFIG.BIGQUERY_PROJECT_ID, jobId);
var errorResult = '';
if (job.status.errorResult) {
errorResult = job.status.errorResult;
}

html.push('<tr>',
"<td style='padding: 0px 10px'>" +
job.configuration.load.destinationTable.tableId + '</td>',
"<td style='padding: 0px 10px'>" + jobId + '</td>',
"<td style='padding: 0px 10px'>" + job.statistics.load?job.statistics.load.outputRows:0 + '</td>',
"<td style='padding: 0px 10px'>" + job.status.state + '</td>',
"<td style='padding: 0px 10px'>" + errorResult + '</td>',
'</tr>');
}
return html.join('\n');
}

Чтобы скрипт работал, нужно активировать Google BigQuery API в библиотеке API.

Для этого:

  1. В Google Ads нажмите Advanced APIsAds_scripts_5.png 
  2. Выберите BigQueryAds_scripts_6.png
  3. Перейдите по ссылке и включите API BigQuery в библиотеке API.
  4. Нажмите Save. Готово! Google BigQuery API активирован и настроенный вами скрипт будет собирать отчеты Google Ads в BigQuery. 

В таблицах с данными о сессиях будут использованы следующие поля из Google Ads Click Performance Report:

    • GclId
    • CampaignId
    • CampaignName
    • AdGroupId
    • AdGroupName
    • CriteriaId
    • CriteriaParameters
    • KeywordMatchType

Обратите внимание: Вы можете дополнительно настраивать в отчетах все необходимые вам поля, не указанные в перечне выше. При этом, из перечисленных полей все должны быть в требуемом отчете.

Была ли эта статья полезной?
Пользователи, считающие этот материал полезным: 0 из 0
Еще есть вопросы? Отправить запрос

1 Комментарии

  • 1
    Avatar
    Alexey Lukyanchuk

    Спасибо, что выкладываете такие полезные статьи. Самому сложно разобраться быстро.

Войдите в службу, чтобы оставить комментарий.