Cara mengunggah data mentah dari Google Ads ke Google BigQuery
Diterbitkan: 2022-04-12Dengan menganalisis keefektifan kampanye iklan Google Ads di Google Analytics, Anda mungkin mengalami pembatasan pengambilan sampel, agregasi data, atau pembatasan antarmuka sistem lainnya. Untungnya, masalah ini mudah diselesaikan dengan mengunggah data mentah dari layanan periklanan Anda ke Google BigQuery.
Dalam artikel ini, Anda akan mempelajari cara mengupload data mentah dari akun Google Ads ke BigQuery dan mengidentifikasi semua tag UTM untuk kampanye berlabel otomatis.
Anda memerlukan OWOX BI untuk menautkan informasi kampanye ke aktivitas pengguna di situs. Daftar untuk demo dan kami akan merinci semua tantangan yang dapat Anda selesaikan dengan OWOX BI.
Daftar Isi
- Mengapa Anda membutuhkan data mentah dari Google Ads
- Dua cara untuk mengupload data mentah dari Google Ads ke BigQuery
- Cara mengonfigurasi unggahan menggunakan Transfer Data
- Cara mengatur unggahan menggunakan Skrip Iklan
- Bagaimana menghubungkan unduhan data dari Google Ads ke OWOX BI
- Kiat yang berguna
Cari tahu nilai sebenarnya dari kampanye
Impor data biaya secara otomatis ke Google Analytics dari semua layanan periklanan Anda. Bandingkan biaya kampanye, BPK, dan ROAS dalam satu laporan.

Mengapa Anda membutuhkan data mentah dari Google Ads
Data mentah dari Google Ads akan memungkinkan Anda menganalisis kampanye iklan dengan akurat hingga setiap kata kunci. Dengan mengupload data ke BigQuery, Anda dapat:
- Buat laporan sedetail yang Anda inginkan tanpa dibatasi oleh batasan GA.
- Tentukan efektivitas kampanye iklan di tingkat sesi dan pengguna.
- Hitung ROI, ROAS, CRR menurut wilayah, jenis pengguna (baru atau kembali), perangkat, dan parameter lainnya.
- Kelola tarif Anda secara efektif dan buat daftar pemasaran ulang.
- Gabungkan data dari Google Ads, Google Analytics, dan CRM untuk menilai efektivitas kampanye berdasarkan margin dan penukaran item Anda.
- Latih model ML Anda untuk perencanaan yang lebih akurat.
Untuk memahami kampanye, iklan, dan kata kunci mana yang membawa pengguna ke situs Anda, Anda perlu menggabungkan data dari Google Ads dan Analytics ke dalam BigQuery. Anda dapat melakukan ini menggunakan streaming OWOX BI.
Streaming informasi ini mengirimkan data perilaku pengguna tanpa sampel di situs Anda ke GBQ. Hit ditransmisikan secara real-time, kemudian sesi dibentuk berdasarkan hit ini.
Informasi sumber lalu lintas OWOX BI diambil dari markup iklan tag UTM. Tag bersifat manual dan otomatis.
Katakanlah Anda menandai iklan secara manual dan Anda mendapatkan URL ini:
https://example.com/?utm_source=facebook&utm_medium=cpc&utm_campaign=utm_tags
Dalam hal ini, setelah Anda menghubungkan OWOX BI, Anda akan memiliki sumber, saluran, dan data kampanye yang tersedia di tabel GBQ:
- trafficSource.source — google
- trafficSource.medium — cpc
- trafficSource.campaign — utm_tags
Baca selengkapnya tentang cara membuat tag UTM dengan benar.
Jika Anda telah mengaktifkan markup otomatis di layanan periklanan, parameter gclid khusus ditetapkan untuk setiap iklan Anda. Itu ditambahkan ke URL halaman arahan saat pengguna mengklik pengumuman.
Contoh tautan seperti itu:
http://www.example.com/?gclid=TeSter-123
Jika Anda menggunakan markup otomatis, Anda tidak bisa mendapatkan sumber, media, atau kampanye dari gclid tanpa data mentah - kolom ini akan kosong di tabel BigQuery yang dikumpulkan OWOX BI.
Apa yang dapat Anda lakukan dalam kasus seperti itu dan bagaimana Anda bisa mendapatkan nama kampanye dan parameter lainnya, yang hanya memiliki gclid? Konfigurasikan upload otomatis dari Google Ads ke GBQ.
Catatan: jika pengumuman tidak ditandai sama sekali, OWOX BI akan memberikan link sebagai berikut:
- untuk sumber non-Google sebagai lalu lintas rujukan (mis. facebook/rujukan)
- untuk sumber Google sebagai lalu lintas langsung (langsung/tidak ada)
Jika ada banyak lalu lintas langsung/tidak ada sama sekali dalam laporan Anda, Anda mungkin tidak mengaktifkan pemfilteran bot atau Anda mungkin memiliki sejumlah besar iklan yang tidak ditandai.
Dua cara untuk mengupload data mentah dari Google Ads ke BigQuery
Kami menggunakan dan merekomendasikan dua metode untuk mengunggah data mentah dari Google Ads: Konektor Transfer Data dan Skrip Iklan.
Cara mana yang Anda pilih tergantung pada tujuan dan anggaran Anda.
Fitur Transfer Data
- Integrasi asli dengan GBQ.
- Unduh data historis untuk periode apa pun tanpa batasan.
- Gratis.
Fitur Skrip Google Ads
- Gratis.
- Anda tidak dapat mengunggah data historis. Hanya informasi hari sebelumnya yang diunduh.
- Memerlukan lebih banyak jika Anda ingin mengatur unggahan dari sejumlah besar akun. Anda harus membuat perubahan skrip secara manual untuk setiap akun. Pada saat yang sama, ada risiko tinggi untuk membuat kesalahan.
Apa yang Anda butuhkan untuk pengaturan
Proyek dan akun aktif di:
- Google Cloud Platform (GCP)
- Google BigQuery (GBQ)
- OWOX BI
- Iklan Google
Mengakses:
- Pemilik di GCP
- Administrator di GBQ
- Mengedit di OWOX BI. Penting: hanya pengguna yang membuat Google Analytics → pipeline streaming Google BigQuery yang dapat mengaktifkan pengunduhan dari Google Ads.
- Membaca di Google Ads
Cara memberikan hak akses di GBQ
Buka konsol GCP dan pilih IAM dan admin — Kelola Sumber Daya dari menu samping. Kemudian pilih proyek dan klik Tambah Anggota. Masukkan email pengguna, pilih peran Administrator BigQuery, dan simpan perubahan Anda.

Cara mengonfigurasi unggahan menggunakan Transfer Data
Langkah 1. Buat proyek di Google Cloud Platform
Jika Anda sudah memiliki proyek di GCP, lewati langkah ini. Jika tidak, buka GCP console dan pilih IAM dan admin — Kelola Sumber Daya dari menu samping. Klik tombol Buat Proyek. Kemudian masukkan nama proyek, tentukan organisasinya, dan klik Buat:

Pastikan untuk mengaktifkan penagihan. Untuk melakukannya, buka tab Penagihan — Manajemen Akun di menu samping, pilih proyek, dan tautkan Akun Penagihan:

Selanjutnya, lengkapi semua bidang dengan memasukkan kontak dan detail kartu pembayaran Anda. Jika ini adalah proyek pertama Anda di GCP, Anda akan menerima $300 yang dapat digunakan selama 12 bulan. Proyek yang memiliki 1-2 akun Google Ads dan hingga 100.000 pengguna unik per bulan akan cukup untuk satu tahun. Ketika Anda menghabiskan batas ini, Anda tidak perlu mengembalikan uangnya. Untuk penggunaan lebih lanjut, Anda cukup mengisi ulang saldo pada kartu yang Anda tautkan ke proyek.
Langkah 2. Aktifkan API BigQuery
Setelah membuat proyek, Anda harus mengaktifkan BigQuery API. Untuk melakukannya, buka API & Layanan — Dasbor dari menu samping GCP, pilih proyek, dan klik Aktifkan API dan Layanan:

Di perpustakaan API, telusuri "BigQuery API" dan klik Aktifkan:

Untuk menggunakan API, klik Buat Kredensial:

Dari daftar tarik-turun, pilih BigQuery API dan klik Kredensial apa yang saya perlukan?

Buat nama akun layanan dan tentukan tingkat akses Peran BigQuery. Pilih jenis kunci JSON dan klik Lanjutkan:

Langkah 3. Aktifkan API Transfer Data
Selanjutnya, Anda perlu mengaktifkan layanan data di BigQuery. Untuk melakukan ini, buka GBQ dan pilih Transfer dari menu samping di sebelah kiri. Kemudian aktifkan BigQuery Data Transfer API:

Langkah 4. Siapkan kumpulan data dalam GBQ
Di BigQuery, pilih proyek dan klik tombol Buat Set Data di sebelah kanan. Lengkapi semua bidang wajib untuk kumpulan data baru (nama, lokasi, retensi):

Langkah 5. Siapkan transfer data dari Google Ads
Klik tab Transfer di menu samping, lalu klik Buat Transfer. Kemudian pilih Google Ads (sebelumnya AdWords) untuk sumber dan masukkan nama unggahan, misalnya Transfer Data.
Di bawah opsi Jadwal, Anda dapat membiarkan pengaturan default ke Mulai sekarang atau mengatur tanggal dan waktu yang Anda inginkan untuk mulai mengunduh. Di bidang Berulang, pilih seberapa sering mengunggah: harian, mingguan, bulanan sesuai permintaan, dll.

Kemudian Anda harus menentukan kumpulan data GBQ untuk memuat laporan dari Google Ads ke dalam. Masukkan ID Pelanggan (ini adalah ID akun Google Ads atau ID MCC Anda) dan klik Tambahkan. Anda dapat melihat ID Pelanggan di akun Google Ads Anda di sudut kanan atas, di samping email Anda.

Maka Anda perlu mengotorisasi akun Gmail yang Anda gunakan. Hari berikutnya, informasi tersebut akan muncul di kumpulan data yang Anda tentukan saat mengatur transfer.
Akibatnya, Anda akan menerima sejumlah besar data mentah dalam GBQ yang dapat Anda gunakan: tabel menurut kampanye, audiens, tabel umum (khusus), kata kunci, dan konversi. Misalnya, jika Anda ingin membuat dasbor khusus, Anda dapat menarik data yang tidak teragregasi dari tabel ini.
Cara mengatur unggahan menggunakan Skrip Iklan
Buka akun Google Ads Anda, klik Alat dan Pengaturan di sudut kanan atas, pilih Tindakan Massal — Skrip, dan klik simbol Plus:

Kemudian, di pojok kanan atas, klik tombol API Lanjutan, pilih BigQuery, dan simpan perubahan Anda:

Pastikan untuk mendaftar dengan akun yang Anda gunakan untuk masuk ke Google Ads:

Salin skrip di bawah ini. Di BIGQUERY_PROJECT_ID, BIGQUERY_DATASET_ID, dan baris email Anda, ganti nilainya dengan informasi Anda sendiri: nama proyek, kumpulan data GBQ, dan email. Rekatkan teks skrip ke editor teks.
/** * @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'); }
Sebelum menjalankan skrip, pastikan untuk mengklik tombol Pratinjau di sudut kanan bawah untuk memeriksa hasilnya. Jika ada kesalahan di dalamnya, sistem akan memperingatkan Anda dan menunjukkan di baris mana kesalahan itu terjadi, seperti pada tangkapan layar ini:


Jika tidak ada kesalahan, klik tombol Jalankan:

Akibatnya, Anda akan menerima laporan CLICK_PERFORMANCE_REPORT baru di GBQ Anda yang akan tersedia pada hari berikutnya:

Ingatlah bahwa ketika Anda menggunakan Transfer Data, Anda mendapatkan sejumlah besar data mentah non-agregat. Dengan Script Iklan, Anda hanya akan memiliki informasi tentang bidang tertentu.
Bidang berikut dari unggahan ini disertakan dalam tabel OWOX BI terkait sesi:
- GclId
- ID Kampanye
- Nama Kampanye
- IdGrupIklan
- NamaGrupIklan
- Id Kriteria
- KriteriaParameter
- Jenis Pencocokan Kata Kunci
Bagaimana menghubungkan unduhan data dari Google Ads ke OWOX BI
Sekarang Anda perlu menggabungkan informasi dari Google Ads dengan data situs untuk memahami kampanye mana yang telah dilalui pengguna untuk mencapai situs Anda. Tabel yang Anda dapatkan di BigQuery, seperti Transfer Data, tidak memiliki parameter ID Klien. Anda hanya dapat menentukan pelanggan mana yang mengklik iklan dengan menautkan data gclid ke data alur OWOX BI.
Jika Anda belum memiliki Google Analytics → saluran streaming Google BigQuery di OWOX BI, baca petunjuk tentang cara membuatnya.
Lalu buka proyek OWOX BI Anda dan buka jalur pipa ini. Klik tab Pengaturan, dan di bawah Pengumpulan Data Sesi, klik Edit Pengaturan:

Gunakan penggeser untuk mengaktifkan pengumpulan data untuk kampanye berlabel otomatis Google Ads dan klik Ubah Setelan:

Pilih jenis markup AutoLabel, tentukan cara memuat Transfer Data atau skrip Iklan ke BigQuery. Tentukan proyek dan set data tempat data Google Ads akan diunduh dan simpan setelan Anda:

Kiat yang berguna
Tips 1. Dengan Transfer Data, Anda dapat mengunggah data historis dari Google Ads ke GBQ. Pada saat yang sama, tidak ada batasan pada total periode pemuatan (baik selama satu tahun, atau selama tiga tahun), tetapi dengan data hanya 180 hari dalam satu waktu.
Anda dapat mengaktifkan unggahan dan menentukan jangka waktu menggunakan tombol Jadwal Isi Ulang pada tab Transfer dengan memilih transfer yang Anda inginkan:

Tips 2. Jika Anda ingin memeriksa jumlah akun Google Ads yang akan ditagih oleh GCP, Anda perlu menentukan jumlah ExternalCustomerID di tabel Pelanggan menggunakan kueri:
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
Anda dapat mengedit tanggal dalam kueri.
Tip 3. Anda dapat mengakses sendiri data yang diunggah menggunakan kueri SQL. Di sini, misalnya, adalah kueri untuk menentukan keefektifan kampanye dari tabel "Campaign" dan "CampaignBasicStats" yang diturunkan dari Transfer Data:
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 Jika Anda memerlukan bantuan dalam mengupload dan menggabungkan data ke Google BigQuery, kami siap membantu. Daftar untuk demo — dan kami akan membahas detailnya.