Jak przesłać nieprzetworzone dane z Google Ads do Google BigQuery

Opublikowany: 2022-04-12

Analizując skuteczność kampanii reklamowych Google Ads w Google Analytics, możesz doświadczyć próbkowania, agregacji danych lub innych ograniczeń interfejsu systemu. Na szczęście ten problem można łatwo rozwiązać, przesyłając nieprzetworzone dane z usługi reklamowej do Google BigQuery.

Z tego artykułu dowiesz się, jak przesyłać nieprzetworzone dane z konta Google Ads do BigQuery i identyfikować wszystkie tagi UTM w kampaniach z automatycznymi etykietami.

OWOX BI jest potrzebne do powiązania informacji o kampanii z aktywnością użytkownika na stronie. Zarejestruj się na demo, a my szczegółowo opiszemy wszystkie wyzwania, które możesz rozwiązać dzięki OWOX BI.

ZAPISZ SIĘ NA DEMO

Spis treści

  • Dlaczego potrzebujesz nieprzetworzonych danych z Google Ads
  • Dwa sposoby przesyłania nieprzetworzonych danych z Google Ads do BigQuery
  • Jak skonfigurować przesyłanie za pomocą przenoszenia danych
  • Jak skonfigurować przesyłanie za pomocą skryptu reklam
  • Jak połączyć pobieranie danych z Google Ads z OWOX BI
  • Przydatne porady

Poznaj prawdziwą wartość kampanii

Automatycznie importuj dane o kosztach do Google Analytics ze wszystkich usług reklamowych. Porównaj koszty kampanii, CPC i ROAS w jednym raporcie.

Rozpocząć proces

Dlaczego potrzebujesz nieprzetworzonych danych z Google Ads

Surowe dane z Google Ads pozwolą Ci analizować kampanie reklamowe z dokładnością do każdego słowa kluczowego. Przesyłając dane do BigQuery, możesz:

  • Twórz raporty tak szczegółowe, jak chcesz, bez ograniczeń GA.
  • Określ skuteczność kampanii reklamowych na poziomie sesji i użytkownika.
  • Oblicz ROI, ROAS, CRR według regionu, typu użytkownika (nowego lub zwróconego), urządzenia i dowolnego innego parametru.
  • Efektywnie zarządzaj stawkami i twórz listy remarketingowe.
  • Połącz dane z Google Ads, Google Analytics i CRM, aby ocenić skuteczność kampanii w oparciu o marżę i wymienialność Twoich produktów.
  • Wytrenuj swój model ML, aby uzyskać dokładniejsze planowanie.

Aby zrozumieć, które kampanie, reklamy i słowa kluczowe przyciągają użytkowników do Twojej witryny, musisz połączyć dane z Google Ads i Analytics w BigQuery. Możesz to zrobić za pomocą przesyłania strumieniowego OWOX BI.

Strumieniowe przesyłanie tych informacji powoduje wysłanie niespróbkowanych danych o zachowaniu użytkowników w Twojej witrynie do GBQ. Trafienia są transmitowane w czasie rzeczywistym, a następnie na ich podstawie tworzone są sesje.

WYPRÓBUJ OWOX BI ZA DARMO

Informacje o źródle ruchu OWOX BI są pobierane z tagów reklamowych UTM. Tagi są ręczne i automatyczne.

Załóżmy, że oznaczyłeś reklamę ręcznie i masz ten adres URL:

https://example.com/?utm_source=facebook&utm_medium=cpc&utm_campaign=utm_tags

W takim przypadku, po połączeniu OWOX BI, będziesz mieć dostępne dane źródła, kanału i kampanii w tabeli GBQ:

  • trafficSource.source — google
  • trafficSource.medium — cpc
  • trafficSource.campaign — utm_tags

Przeczytaj więcej o tym, jak poprawnie tworzyć tagi UTM.

Jeśli włączyłeś automatyczne znaczniki w serwisie reklamowym, do każdej z Twoich reklam przypisywany jest specjalny parametr gclid. Jest dodawany do adresu URL strony docelowej, gdy użytkownik kliknie ogłoszenie.

Przykład takiego linku:

http://www.example.com/?gclid=TeSter-123

Jeśli używasz znaczników automatycznych, nie możesz pobrać źródła, medium ani kampanii z gclid bez nieprzetworzonych danych – te pola będą puste w tabelach BigQuery zbieranych przez OWOX BI.

Co możesz zrobić w takim przypadku i jak uzyskać nazwę kampanii i inne parametry, mając tylko gclid? Skonfiguruj automatyczne przesyłanie z Google Ads do GBQ.

Uwaga: jeśli ogłoszenie nie jest w ogóle zaznaczone, OWOX BI przypisze link w następujący sposób:

  • dla źródeł innych niż Google jako ruch odsyłający (np. facebook/referral)
  • dla źródła Google jako ruch bezpośredni (bezpośredni/brak)

Jeśli w raportach jest dużo bezpośredniego ruchu lub brak ruchu, możliwe, że nie masz włączonej funkcji filtrowania botów lub masz dużą liczbę nieotagowanych reklam.

Dwa sposoby przesyłania nieprzetworzonych danych z Google Ads do BigQuery

Używamy i zalecamy dwie metody przesyłania nieprzetworzonych danych z Google Ads: Data Transfer Connector i Ads Script.

Wybór drogi zależy od Twoich celów i budżetu.

Funkcje przesyłania danych

  • Natywna integracja z GBQ.
  • Pobieraj dane historyczne z dowolnego okresu bez ograniczeń.
  • Darmo.

Funkcje skryptu Google Ads

  • Wolny.
  • Nie możesz przesyłać danych historycznych. Pobierane są tylko informacje z poprzedniego dnia.
  • Wymaga więcej, jeśli chcesz skonfigurować przesyłanie z dużej liczby kont. Będziesz musiał ręcznie wprowadzić zmiany w skrypcie dla każdego konta. Jednocześnie istnieje duże ryzyko popełnienia błędu.

Czego potrzebujesz do ustawień

Aktywne projekty i konta w:

  • Platforma Google Cloud (GCP)
  • Google BigQuery (GBQ)
  • OWOX BI
  • Reklamy Google

Dostęp:

  • Właściciel w GCP
  • Administrator w GBQ
  • Edycja w OWOX BI. Ważne: tylko użytkownik, który utworzył Google Analytics → Google BigQuery streaming potok, może włączyć pobieranie z Google Ads.
  • Czytanie w Google Ads

Jak przyznać prawa dostępu w GBQ

Otwórz konsolę GCP i wybierz IAM i admin — Zarządzaj zasobami z menu bocznego. Następnie wybierz projekt i kliknij Dodaj członka. Wpisz adres e-mail użytkownika, wybierz rolę administratora BigQuery i zapisz zmiany.

Dostęp do BigQuery

Jak skonfigurować przesyłanie za pomocą przenoszenia danych

Krok 1. Utwórz projekt w Google Cloud Platform

Jeśli masz już projekt w GCP, pomiń ten krok. Jeśli nie, otwórz konsolę GCP i wybierz IAM and admin — Manage Resource z bocznego menu. Kliknij przycisk Utwórz projekt. Następnie wprowadź nazwę projektu, określ organizację i kliknij Utwórz:

Google Cloud Platform - zakładanie projektu

Pamiętaj, aby włączyć płatności. W tym celu w menu bocznym otwórz zakładkę Rozliczenia — Zarządzanie kontem, wybierz projekt i podlinkuj Konto rozliczeniowe:

Rozliczenia w Google Cloud Platform

Następnie wypełnij wszystkie pola, wpisując swoje kontakty i dane karty płatniczej. Jeśli jest to Twój pierwszy projekt w GCP, otrzymasz 300 USD, które możesz wykorzystać przez 12 miesięcy. Projekty, które mają 1-2 konta Google Ads i do 100 000 unikalnych użytkowników miesięcznie, wystarczą na rok. Po wyczerpaniu tego limitu nie musisz zwracać pieniędzy. W celu dalszego wykorzystania wystarczy uzupełnić saldo na karcie połączonej z projektem.

Krok 2. Włącz API BigQuery

Po utworzeniu projektu musisz aktywować BigQuery API. Aby to zrobić, przejdź do APIs & Services — Dashboard z bocznego menu GCP, wybierz projekt i kliknij Enable APIs and Services:

Aktywacja API BigQuery

W bibliotece API wyszukaj „BigQuery API” i kliknij Włącz:

włącz API BigQuery

Aby użyć interfejsu API, kliknij Utwórz poświadczenia:

Utwórz dane logowania

Z listy wybierz BigQuery API i kliknij Jakich danych logowania potrzebuję?

Wybór poświadczeń

Utwórz nazwę konta usługi i określ poziom dostępu do roli BigQuery. Wybierz typ klucza JSON i kliknij Kontynuuj:

Określ poświadczenia

Krok 3. Aktywuj interfejs API przesyłania danych

Następnie musisz aktywować usługę danych w BigQuery. Aby to zrobić, otwórz GBQ i wybierz Przelewy z bocznego menu po lewej stronie. Następnie włącz interfejs BigQuery Data Transfer API:

włączyć BigQuery Data Transfer API

Krok 4. Przygotuj zestaw danych w GBQ

W BigQuery wybierz projekt i kliknij przycisk Utwórz zbiór danych po prawej stronie. Wypełnij wszystkie wymagane pola dla nowego zbioru danych (nazwa, lokalizacja, przechowywanie):

utwórz zbiór danych w GBQ

Krok 5. Skonfiguruj transfer danych z Google Ads

Kliknij kartę Przelewy w menu bocznym, a następnie kliknij Utwórz przelew. Następnie wybierz Google Ads (dawniej AdWords) jako źródło i wpisz nazwę przesyłanego pliku, np. Przenoszenie danych.

W opcjach harmonogramu możesz pozostawić domyślne ustawienie Rozpocznij teraz lub ustawić datę i godzinę rozpoczęcia pobierania. W polu Powtórzenia wybierz częstotliwość przesyłania: codziennie, co tydzień, co miesiąc na żądanie itp.

Ustawienia przesyłania danych

Następnie musisz określić zbiór danych GBQ, do którego mają być wczytywane raporty z Google Ads. Wpisz identyfikator klienta (jest to identyfikator Twojego konta Google Ads lub identyfikator MCK) i kliknij Dodaj. Identyfikator klienta na koncie Google Ads możesz wyświetlić w prawym górnym rogu, obok e-maila.

Określ ustawienia przesyłania danych

Następnie musisz autoryzować używane konto Gmail. Następnego dnia informacje pojawią się w zbiorze danych określonym podczas konfigurowania przelewu.

W rezultacie otrzymasz dużą ilość surowych danych w GBQ, z którymi możesz pracować: tabele według kampanii, odbiorców, wspólne (niestandardowe) tabele, słowa kluczowe i konwersje. Na przykład, jeśli chcesz zbudować niestandardowy pulpit nawigacyjny, możesz pobrać niezagregowane dane z tych tabel.

Jak skonfigurować przesyłanie za pomocą skryptu reklam

Otwórz konto Google Ads, kliknij Narzędzia i ustawienia w prawym górnym rogu, wybierz Działania zbiorcze — Skrypty i kliknij symbol Plus:

Skrypt w Google Ads

Następnie w prawym górnym rogu kliknij przycisk Zaawansowane interfejsy API, wybierz BigQuery i zapisz zmiany:

Zapisywanie zmian

Pamiętaj, aby zarejestrować się na konto, z którego logowałeś się w Google Ads za pomocą:

Autoryzacja Google Ads

Skopiuj poniższy skrypt. W wierszach BIGQUERY_PROJECT_ID, BIGQUERY_DATASET_ID i Twoja poczta e-mail zastąp wartości własnymi informacjami: nazwą projektu, zestawem danych GBQ i adresem e-mail. Wklej tekst skryptu do edytora tekstu.

    /** * @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 [[email protected]] * * @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'); }
/** * @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 [[email protected]] * * @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'); }

Przed uruchomieniem skryptu kliknij przycisk Podgląd w prawym dolnym rogu, aby sprawdzić wynik. Jeśli są w nim błędy, system ostrzeże Cię i wskaże, w której linii wystąpiły, jak na tym zrzucie ekranu:

Uruchom skrypt

Jeśli nie ma błędów, kliknij przycisk Uruchom:

skonfigurować przesyłanie z Google Ads

W rezultacie otrzymasz nowy raport CLICK_PERFORMANCE_REPORT w swoim GBQ, który będzie dostępny następnego dnia:

wyniki w GBQ

Przypomnij sobie, że gdy korzystasz z przenoszenia danych, otrzymujesz dużą ilość nieprzetworzonych, niezagregowanych danych. Dzięki Ads Script będziesz mieć informacje tylko o niektórych polach.

Następujące pola z tego przesłania znajdują się w powiązanych z sesją tabelach OWOX BI:

  • Identyfikator Gcl
  • Identyfikator kampanii
  • Nazwa kampanii
  • Identyfikator grupy reklam
  • Nazwa grupy reklam
  • Identyfikator kryteriów
  • KryteriaParametry
  • Typ dopasowania słowa kluczowego

Jak połączyć pobieranie danych z Google Ads z OWOX BI

Teraz musisz połączyć informacje z Google Ads z danymi witryny, aby zrozumieć, przez które kampanie użytkownicy dotarli do Twojej witryny. Tabele wyświetlane w BigQuery, takie jak Przenoszenie danych, nie mają parametru identyfikatora klienta. Możesz tylko określić, który klient kliknął reklamy, łącząc dane gclid z danymi przepływu OWOX BI.

Jeśli nie masz jeszcze potoku strumieniowego Google Analytics → Google BigQuery w OWOX BI, przeczytaj instrukcję, jak go utworzyć.

Następnie przejdź do swojego projektu OWOX BI i otwórz ten potok. Kliknij kartę Ustawienia, a następnie w sekcji Zbieranie danych sesji kliknij opcję Edytuj ustawienia:

Ustawienia potoku OWOX BI

Za pomocą suwaka włącz zbieranie danych w kampaniach Google Ads z etykietami automatycznymi i kliknij Zmień ustawienia:

włącz zbieranie danych dla Google Ads

Wybierz typ znacznika AutoLabel, określ sposób wczytywania skryptów przenoszenia danych lub reklam do BigQuery. Określ projekt i zbiór danych, z którego będą pobierane dane Google Ads, i zapisz ustawienia:

Zapisz ustawienia

Przydatne porady

Wskazówka 1. Dzięki Przenoszeniu danych możesz przesyłać dane historyczne z Google Ads do GBQ. Jednocześnie nie ma ograniczeń co do całkowitego okresu ładowania (albo na rok, albo na trzy), ale z danymi tylko 180 dni na raz.

Możesz aktywować przesyłanie i określić okres za pomocą przycisku Zaplanuj uzupełnianie na karcie Przelewy, wybierając żądany przelew:

prześlij dane historyczne

Wskazówka 2. Jeśli chcesz sprawdzić liczbę kont Google Ads, za które GCP będzie pobierać opłaty, musisz określić liczbę ExternalCustomerID w tabeli Customer za pomocą zapytania:

    SELECT ExternalCustomerId FROM `project_name.dataset_name.Customer_*` WHERE _PARTITIONTIME >= "2020-01-01 00:00:00" AND _PARTITIONTIME < "2020-07-10 00:00:00" group by 1
SELECT ExternalCustomerId FROM `project_name.dataset_name.Customer_*` WHERE _PARTITIONTIME >= "2020-01-01 00:00:00" AND _PARTITIONTIME < "2020-07-10 00:00:00" group by 1

Możesz edytować daty w zapytaniu.

Wskazówka 3. Możesz samodzielnie uzyskać dostęp do przesłanych danych za pomocą zapytań SQL. Oto na przykład zapytanie określające skuteczność kampanii z tabel „Campaign” i „CampaignBasicStats” pochodzących z przenoszenia danych:

    SELECT {source language="sql"} c.ExternalCustomerId, c.CampaignName, c.CampaignStatus, SUM(cs.Impressions) AS Impressions, SUM(cs.Interactions) AS Interactions, {/source} (SUM(cs.Cost) / 1000000) AS Cost FROM `[DATASET].Campaign_[CUSTOMER_ID]` c LEFT JOIN {source language="sql"} {source language="sql"} `[DATASET].CampaignBasicStats_[CUSTOMER_ID]` cs ON (c.CampaignId = cs.CampaignId AND cs._DATA_DATE BETWEEN DATE_ADD(CURRENT_DATE(), INTERVAL -31 DAY) AND DATE_ADD(CURRENT_DATE(), INTERVAL -1 DAY)) WHERE c._DATA_DATE = c._LATEST_DATE GROUP BY 1, 2, 3 ORDER BY Impressions DESC
SELECT {source language="sql"} c.ExternalCustomerId, c.CampaignName, c.CampaignStatus, SUM(cs.Impressions) AS Impressions, SUM(cs.Interactions) AS Interactions, {/source} (SUM(cs.Cost) / 1000000) AS Cost FROM `[DATASET].Campaign_[CUSTOMER_ID]` c LEFT JOIN {source language="sql"} {source language="sql"} `[DATASET].CampaignBasicStats_[CUSTOMER_ID]` cs ON (c.CampaignId = cs.CampaignId AND cs._DATA_DATE BETWEEN DATE_ADD(CURRENT_DATE(), INTERVAL -31 DAY) AND DATE_ADD(CURRENT_DATE(), INTERVAL -1 DAY)) WHERE c._DATA_DATE = c._LATEST_DATE GROUP BY 1, 2, 3 ORDER BY Impressions DESC

PS Jeśli potrzebujesz pomocy przy przesyłaniu i łączeniu danych do Google BigQuery, jesteśmy gotowi Ci pomóc. Zapisz się na demo — a my omówimy szczegóły.

ZAPISZ SIĘ NA DEMO