Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * @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
- * for more details.
- *
- * @author AdWords Scripts Team [[email protected]]
- *
- * @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/d/1CaUSdtVeRW69jW4VFfdCx35w2_Opf0Em3Pe1B8sbmRw/edit#gid=0';
- /**
- * Status code for items to be checked.
- */
- var STATUSES_TO_CHECK = ['ENABLED', 'PAUSED'];
- /**
- * 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 + '"]')
- .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 + '"]')
- .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;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment