404 script voor Google AdWords

In dit blog leggen we uit hoe je eenvoudig een 404 script kunt maken voor een Google AdWords campagne. Een 404 script wordt gebruikt om vroegtijdig te herkennen wanneer een advertentie verwijst naar een 404 pagina. Het komt soms voor dat de URL van een pagina wijzigt of een pagina offline gehaald wordt. Het is zonde van het geld en de gebruikerservaring op de website wanneer er per ongeluk nog een advertentie gelinkt staat naar deze landingspagina.

Door een 404 script toe te voegen aan het account, heb je altijd toegang tot de laatste gegevens in een Google Spreadsheet, en krijg je een e-mailmelding wanneer er een 404 melding is.

  1. Google Spreadsheet aanmakenadwords404script_1

Open dit Google Spreadsheet bestand en maak een kopie. (Bestand -> Een kopie maken). Er opent zich dan een nieuw tabblad met een nieuwe spreadsheet. Verander de nummers 555-555-5555 naar het accountnummer van jouw AdWords account. Let goed op dat je dit ook op het tabblad Report doet, te vinden onderaan het document.

Vul ook het e-mailadres in waar je de meldingen kan ontvangen. Voor de zekerheid kun je hier beter het e-mailadres opgeven dat je veel gebruikt en het account mee beheert.

2. Google AdWords 404 aanmaken in het AdWords account

Log in bij Google AdWords en ga naar het gewenste account. In het linkermenu, vind je onder Bulkbewerkingen -> Scripts.

Maak hier een nieuw script aan. Je ziet hier dan een leeg veld waar je code kunt invullen. Er staat ook een verzoek voor machtiging. Deze dient geaccepteerd te worden.

Kopieer onderstaand script en plakdeze in het veld.

// Copyright 2015, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License./**
* @name Link Checker
*
* @overview The Link Checker script iterates through all the ads and keywords
* in your account and makes sure their URLs do not produce “Page not found”
* or other types of error responses. See
* https://developers.google.com/adwords/scripts/docs/solutions/link-checker#adwordsapp
* for more details.
*
* @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
*
* @version 1.2
*//**
* The URL of the tracking spreadsheet. This should be a copy of
* https://goo.gl/bMrbW5
*/
var SPREADSHEET_URL = ‘https://docs.google.com/spreadsheets/xxxxxxxxxxxxxxxx’;/**
* Status code for items to be checked.
*/
var STATUSES_TO_CHECK = [‘ENABLED’];/**
* Limit the number of ad and keyword URLs checked by this script per run.
*/
var MAX_URLS_TO_CHECK = 800;var LABEL_NAME = ‘link_checked’;

var shelper = new SHelper();
var badUrls = 0;
var numUrlsChecked = 0;

function main() {
var isFirstRun = isFirstRunOfTheDay();

if (isFirstRun) {
dealWithFirstRunOfTheDay();
}

if (shelper.config.email.length == 0 &&
shelper.config.emailPreference != ‘Never’) {
Logger.log(‘WARNING: no email specified, proceeding…’);
}
if (!shelper.config.checkAds && !shelper.config.checkKeywords) {
Logger.log(‘WARNING: requested no keywords and no ads checking. Exiting.’);
return;
}
createLinkCheckerLabel();

var anythingChanged = shelper.config.checkKeywords &&
checkKeywordUrls(AdWordsApp.keywords(), isFirstRun);
anythingChanged = (shelper.config.checkAds &&
checkAdUrls(AdWordsApp.ads(), isFirstRun)) || anythingChanged;

if (anythingChanged) {
shelper.flush();
if (badUrls > 0 && shelper.config.email.length > 0 &&
shelper.config.emailPreference == ‘As soon as an error is discovered’) {
var bad = shelper.spreadsheet.getRangeByName(‘bad’).getValue();
var good = shelper.spreadsheet.getRangeByName(‘good’).getValue();
sendReportWithErrors(good, bad);
}
} else {
shelper.spreadsheet.getRangeByName(‘finished’).setValue(
‘All done for the day!’);
}
Logger.log(‘Done’);
}

function isFirstRunOfTheDay() {
var date = new Date();
var lastCheckDate = shelper.dataSheet.getRange(1, 3).getValue();
return lastCheckDate.length == 0 ||
date.getYear() != lastCheckDate.getYear() ||
date.getMonth() != lastCheckDate.getMonth() ||
date.getDay() != lastCheckDate.getDay();
}

function dealWithFirstRunOfTheDay() {
var date = new Date();
Logger.log(‘The script is running for the first time today…’);

// kill the label.
var labels = AdWordsApp.labels().withCondition(
“Name='” + LABEL_NAME + “‘”).get();
if (labels.hasNext()) {
if (AdWordsApp.getExecutionInfo().isPreview()) {
Logger.log(‘WARNING: This script needs to remove a label named “%s” ‘ +
‘to work correctly, but this action cannot be performed in ‘ +
‘preview mode. Please run this script, or remove the label ‘ +
‘manually if you wish to preview the script.’, LABEL_NAME);
}

labels.next().remove();
}
// send out yesterday’s report
if (shelper.config.email.length > 0 &&
(shelper.config.emailPreference == ‘Once a day’ ||
shelper.config.emailPreference == ‘Once a day if there are errors’)) {
var bad = shelper.spreadsheet.getRangeByName(‘bad’).getValue();
var good = shelper.spreadsheet.getRangeByName(‘good’).getValue();
if (shelper.config.emailPreference == ‘Once a day’) {
if (bad == 0) {
MailApp.sendEmail(shelper.config.email,
‘AdWords Link Checker verified ‘ + good +
‘ URLs on account ‘ +
AdWordsApp.currentAccount().getCustomerId() +
‘, all looking good!’, ”);
} else {
sendReportWithErrors(good, bad);
}
} else if (shelper.config.emailPreference ==
‘Once a day if there are errors’ && bad > 0) {
sendReportWithErrors(good, bad);
}
}
// reset the spreadsheet
shelper.spreadsheet.getRangeByName(‘account_id_dashboard’).setValue(
AdWordsApp.currentAccount().getCustomerId());
shelper.spreadsheet.getRangeByName(‘account_id_report’).setValue(
AdWordsApp.currentAccount().getCustomerId());
shelper.spreadsheet.getRangeByName(‘date’).setValue(date);
shelper.spreadsheet.getRangeByName(‘finished’).setValue(
‘Checking links…’);
shelper.dataSheet.getRange(
4, 1, shelper.dataSheet.getMaxRows() – 3, 6).clear();
}

function sendReportWithErrors(good, bad) {
var emailBody = [];
emailBody.push(‘Summary for account ‘ +
AdWordsApp.currentAccount().getCustomerId() +
‘: ‘ + good + ‘ good URLs, ‘ + bad + ‘ bad ones\n’);
emailBody.push(‘Full report available at ‘ + shelper.spreadsheet.getUrl() +
‘\n’);
shelper.reset();
var row = shelper.readRow();
while (row != null && emailBody.length < 200) { if (row[1] >= 300) {
var entityType = row[4].length > 0 ? ‘Keyword: ‘ : ‘Ad: ‘;
var entityText = row[4].length > 0 ? row[4] : row[5];
emailBody.push(‘Campaign: ‘ + row[2] + ‘, Ad Group: ‘ + row[3] + ‘, ‘ +
entityType + entityText);
emailBody.push(row[0] + ‘ – ‘ + row[1] + ‘ response code.\n’);
}
row = shelper.readRow();
}
if (emailBody.length >= 200) {
emailBody.push(‘Further URLs omitted. Check the report at ‘ +
shelper.spreadsheet.getUrl());
}
shelper.reset();
MailApp.sendEmail(shelper.config.email,
‘AdWords Link Checker verified found ‘ + bad +
‘ bad URLs on account ‘ + AdWordsApp.currentAccount().getCustomerId() + ”,
emailBody.join(‘\n’));
}

function checkAdUrls(selector, isFirstRun) {

var iterator = selector
.withCondition(‘CreativeFinalUrls STARTS_WITH_IGNORE_CASE “h”‘)
.withCondition(‘Status IN [‘ + STATUSES_TO_CHECK.join(‘,’) + ‘]’)
.withCondition(‘CampaignStatus IN [‘ + STATUSES_TO_CHECK.join(‘,’) + ‘]’)
.withCondition(‘AdGroupStatus IN [‘ + STATUSES_TO_CHECK.join(‘,’) + ‘]’)
.withCondition(‘LabelNames CONTAINS_NONE [“‘ + LABEL_NAME + ‘”]’)
.withCondition(“Status = ‘ENABLED'”)
.get();
Logger.log(‘Checking %s ads…’, iterator.totalNumEntities());

if (iterator.totalNumEntities() == 0 && isFirstRun) {
Logger.log(‘WARNING: The script is not checking any ad URLs. If this is ‘ +
‘not expected, then ensure that you have enabled ads in your ‘ +
‘account. Also check the logs for any earlier errors.’);
}

return checkUrls(iterator);
}

function checkKeywordUrls(selector, isFirstRun) {
var iterator = selector
.withCondition(‘FinalUrls STARTS_WITH_IGNORE_CASE “h”‘)
.withCondition(‘Status IN [‘ + STATUSES_TO_CHECK.join(‘,’) + ‘]’)
.withCondition(‘CampaignStatus IN [‘ + STATUSES_TO_CHECK.join(‘,’) + ‘]’)
.withCondition(‘AdGroupStatus IN [‘ + STATUSES_TO_CHECK.join(‘,’) + ‘]’)
.withCondition(‘LabelNames CONTAINS_NONE [“‘ + LABEL_NAME + ‘”]’)
.withCondition(“Status = ‘ENABLED'”)
.get();
Logger.log(‘Checking %s keywords…’, iterator.totalNumEntities());

if (iterator.totalNumEntities() == 0 && isFirstRun) {
Logger.log(‘WARNING: The script is not checking any keyword URLs. This ‘ +
‘may happen if none of your keywords have a final URL. If this not ‘ +
‘expected, then ensure that you have enabled keywords in your ‘ +
‘account. Also check the logs for any earlier errors.’);
}

return checkUrls(iterator);
}

function checkUrls(iterator) {
if (!iterator.hasNext()) {
return false;
}

var urlMap = {};

while (iterator.hasNext()) {
var entity = iterator.next();

if (numUrlsChecked > MAX_URLS_TO_CHECK) {
Logger.log(‘Checked %s urls. Will resume in next run.’, numUrlsChecked);
break;
}

var urls = [entity.urls().getFinalUrl(), entity.urls().getMobileFinalUrl()];
for (var i = 0; i < urls.length; i++) {
if (urls[i] == null) {
continue;
}
var lastUrl = encodeURI(urls[i]);
if (lastUrl in urlMap) {
continue;
}
urlMap[lastUrl] = true;

var now = new Date().getTime();
var responseCode = 0;
try {
numUrlsChecked++;
var response = UrlFetchApp.fetch(lastUrl, {muteHttpExceptions: true});
responseCode = response.getResponseCode();
} catch (e) {
// Something went wrong. Since this a script error, let’s mark it as
// 500.
Logger.log(‘Could not fetch %s due to an internal error : “%s”. ‘ +
‘Marking this URL as failed, with an error code 500.’, lastUrl, e);
responseCode = 500;
}
var then = new Date().getTime();
Utilities.sleep(then – now);
if (responseCode < 300) { shelper.writeRow(lastUrl, responseCode); } else { badUrls++; if (typeof(entity[‘getHeadline’]) != ‘undefined’) { var adText = entity.getType() == ‘TEXT_AD’ ? entity.getHeadline() + ‘\n’ + entity.getDescription1() + ‘\n’ + entity.getDescription2() : entity.getType(); shelper.writeRow(lastUrl, responseCode, entity.getCampaign().getName(), entity.getAdGroup().getName(), null, adText); } else { shelper.writeRow(lastUrl, responseCode, entity.getCampaign().getName(), entity.getAdGroup().getName(), entity.getText()); } } } entity.applyLabel(LABEL_NAME); } return true; } function createLinkCheckerLabel() { var labels = AdWordsApp.labels().withCondition( “Name='” + LABEL_NAME + “‘”).get(); if (!labels.hasNext()) { if (AdWordsApp.getExecutionInfo().isPreview()) { Logger.log(‘WARNING: This script needs to create a label named “%s” to ‘ + ‘work correctly, but this action cannot be performed in preview ‘ + ‘mode. Please run this script, or create the label manually if you ‘ + ‘wish to preview the script.’, LABEL_NAME); } AdWordsApp.createLabel(LABEL_NAME, “Managed by Link Checker, please don’t modify!”, ‘#60e020’); } } // Spreadsheet helper function SHelper() { this.MAX_ROWS = 20000; this.BATCH_SIZE = 50; Logger.log(‘Using spreadsheet – %s.’, SPREADSHEET_URL); this.spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL); this.dataSheet = this.spreadsheet.getSheets()[1]; this.config = { checkAds: this.spreadsheet.getRangeByName(‘check_ads’).getValue() == ‘Yes’, checkKeywords: this.spreadsheet.getRangeByName(‘check_keywords’). getValue() == ‘Yes’, email: this.spreadsheet.getRangeByName(‘email_address’).getValue(), emailPreference: this.spreadsheet.getRangeByName(‘email_preference’). getValue() }; this.globalRow = 4; this.cells = null; this.localRow = 0; this.reset = function() { this.globalRow = 4; this.cells = null; this.localRow = 0; }; this.readRow = function() { initCells(this); if (this.localRow == this.cells.length) { this.globalRow += this.cells.length; if (this.globalRow >= this.dataSheet.getMaxRows()) {
return null;
}
this.cells = this.dataSheet.getRange(
this.globalRow, 2, this.BATCH_SIZE, 6).getValues();
this.localRow = 0;
}
if (this.cells[this.localRow][0].length > 0) {
return this.cells[this.localRow++];
} else {
return null;
}
};
this.writeRow = function() {
fetchCells(this);
for (var i = 0; i < arguments.length; i++) {
this.cells[this.localRow][i] = arguments[i];
}
};
this.flush = function() {
if (this.cells) {
this.dataSheet.getRange(this.globalRow, 2, this.cells.length, 6).
setValues(this.cells);
this.dataSheet.getRange(1, 1).copyFormatToRange(
this.dataSheet,
3,
3,
this.globalRow,
this.globalRow + this.cells.length);
}
};
function initCells(instance) {
if (instance.cells == null) {
instance.globalRow = 4;
instance.cells = instance.dataSheet.getRange(
instance.globalRow, 2, instance.BATCH_SIZE, 6).getValues();
instance.localRow = 0;
}
}
function fetchCells(instance) {
initCells(instance);
while (!findEmptyRow(instance) && instance.globalRow < instance.MAX_ROWS) {
if (instance.dataSheet.getMaxRows() < instance.globalRow + this.BATCH_SIZE) { instance.dataSheet.insertRowsAfter( instance.dataSheet.getMaxRows(), instance.BATCH_SIZE); } instance.flush(); instance.globalRow += instance.cells.length; instance.cells = instance.dataSheet.getRange( instance.globalRow, 2, instance.BATCH_SIZE, 6).getValues(); instance.localRow = 0; } if (instance.globalRow >= instance.MAX_ROWS) {
Logger.log(‘WARNING: maximum length of the spreadsheet exceeded. ‘ +
‘Exiting.’);
throw ”;
}
}
function findEmptyRow(instance) {
for (; instance.localRow < instance.cells.length &&
!(instance.cells[instance.localRow][0] == null ||
instance.cells[instance.localRow][0].length == 0); instance.localRow++);
return instance.localRow < instance.cells.length;
}
}

Zoek in het bestand naar deze code:
var SPREADSHEET_URL = ‘https://docs.google.com/spreadsheets/xxxxxxxxxxxxxxxx’;

Vervang vervolgens de URL met de URL van de eerder aangemaakte spreadsheet.

Sla het script op en bekijk het voorbeeld. Draai vervolgens het script.

adwords404script_3

 

 

3. Schema instellen voor het 404 AdWords script

Dit is iets wat vaak over het hoofd gezien wordt, maar erg belangrijk!adwords404script_2

Achter het script kun je een frequentie aangeven wanneer het script draait. Doe je dit niet, dien je het script steeds handmatig te draaien. In het spreadsheet document kun je vervolgens aangeven wanneer je een adwords404script_4e-mail wil ontvangen. Met de instellingen zoals in de afbeeldingen in dit blog, draait het script elke dag en stuurt een e-mail zodra er een foutcode gevonden wordt.

Het is ook mogelijk een script op MCC niveau toe te voegen. Echter zijn er weinig scripts die dan ook de sitelinks meenemen, houd hier rekening mee.

Succes!

 

1 thought on “404 script voor Google AdWords”

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *