PageSpeed Insights: análise para dispositivos móveis (conta única)

This script is for a single account. For operating on multiple accounts in a Manager Account, use the Manager Account version of the script.

Para garantir uma experiência positiva aos usuários de dispositivos móveis quando eles acessam as páginas de destino, os sites precisam ser criados pensando nos dispositivos móveis. Um site otimizado corretamente para dispositivos móveis garante que os usuários permaneçam envolvidos com ele, tornando seus anúncios mais eficazes.

A análise de dispositivos móveis por meio do PageSpeed Insights fornece um relatório que sugere maneiras de melhorar a experiência na página de destino em dispositivos móveis.

Como funciona

O script examina URLs da conta (de anúncios, palavras-chave e sitelinks) usando URLs finais para dispositivos móveis, se disponíveis. Caso contrário, ele usa URL padrão.

A primeira etapa do processo é criar um dicionário com o máximo possível de URLs no tempo disponível.

Quando essa etapa for concluída, o script identificará os URLs mais diferentes. É possível imaginar o cenário em que um grande número de URLs em uma conta retornam páginas muito parecidas. Por exemplo:

http://www.example.com/product?id=123 e http://www.example.com/product?id=456

muito provavelmente retornará páginas criadas a partir do mesmo modelo. Para esse fim, a diferença é classificada desta forma:

  • Maiores diferenças: os hosts são diferentes (por exemplo, http://www.example.com/path e http://www.test.com/path).
  • Mais diferenças: os hosts são os mesmos, mas os caminhos são diferentes (por exemplo, http://www.example.com/shop e http://www.example.com/blog).
  • Menos diferenças: os hosts e os caminhos são os mesmos, mas os parâmetros são diferentes (por exemplo, http://www.example.com/shop?product=1 e http://www.example.com/shop?product=2).

Um subconjunto menor de URLs, com o objetivo de serem o mais diferentes possível de acordo com a classificação acima, são selecionados e recuperados por meio da PageSpeed Insights API.

Os resultados são apresentados em uma Planilha do Google, que é enviada por e-mail.

Configuração

Código-fonte

// 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.

/**
 * @fileoverview Mobile Performance from PageSpeed Insights - Single Account
 *
 * Produces a report showing how well landing pages are set up for mobile
 * devices and highlights potential areas for improvement. See :
 * https://developers.google.com/adwords/scripts/docs/solutions/mobile-pagespeed
 * for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.3.2
 *
 * @changelog
 * - version 1.3.2
 *   - Bugfix to improve table sorting comparator.
 * - version 1.3.1
 *   - Bugfix for handling the absence of optional values in PageSpeed response.
 * - version 1.3
 *   - Removed the need for the user to take a copy of the spreadsheet.
 *   - Added the ability to customize the Campaign and Ad Group limits.
 * - version 1.2.1
 *   - Improvements to time zone handling.
 * - version 1.2
 *   - Bug fix for handling empty results from URLs.
 *   - Error reporting in spreadsheet for failed URL fetches.
 * - version 1.1
 *   - Updated the comments to be in sync with the guide.
 * - version 1.0
 *   - Released initial version.
 */

// See "Obtaining an API key" at
// https://developers.google.com/adwords/scripts/docs/solutions/mobile-pagespeed
var API_KEY = 'INSERT_PAGESPEED_API_KEY_HERE';
var EMAIL_RECIPIENTS = ['INSERT_EMAIL_ADDRESS_HERE'];

// If you wish to add extra URLs to checked, list them here as a
// comma-separated list eg. ['http://abc.xyz', 'http://www.google.com']
var EXTRA_URLS_TO_CHECK = [];

/**
 * The URL of the template spreadsheet for each report created.
 */
var SPREADSHEET_TEMPLATE =
    'https://docs.google.com/spreadsheets/d/1SKLXUiorvgs2VuPKX7NGvcL68pv3xEqD7ZcqsEwla4M/edit';

var PAGESPEED_URL =
    'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?';

/*
 * The maximum number of Campaigns to sample within the account.
 */
var CAMPAIGN_LIMIT = 50000;

/*
 * The maximum number of Ad Groups to sample within the account.
 */
var ADGROUP_LIMIT = 50000;

/**
 * These are the sampling limits for how many of each element will be examined
 * in each AdGroup.
 */
var KEYWORD_LIMIT = 20;
var SITELINK_LIMIT = 20;
var AD_LIMIT = 30;

/**
 * Specifies the amount of time in seconds required to do the URL fetching and
 * result generation. As this is the last step, entities in the account will be
 * iterated over until this point.
 */
var URL_FETCH_TIME_SECS = 8 * 60;

/**
 * Specifies the amount of time in seconds required to write to and format the
 * spreadsheet.
 */
var SPREADSHEET_PREP_TIME_SECS = 4 * 60;

/**
 * Represents the number of retries to use with the PageSpeed service.
 */
var MAX_RETRIES = 3;

/**
 * The main entry point for execution.
 */
function main() {
  if (!defaultsChanged()) {
    Logger.log('Please change the default configuration values and retry');
    return;
  }
  var accountName = AdWordsApp.currentAccount().getName();
  var urlStore = getUrlsFromAccount();
  var result = getPageSpeedResultsForUrls(urlStore);
  var spreadsheet = createPageSpeedSpreadsheet(accountName +
      ': PageSpeed Insights - Mobile Analysis', result);
  spreadsheet.addEditors(EMAIL_RECIPIENTS);
  sendEmail(spreadsheet.getUrl());
}

/**
 * Sends an email to the user with the results of the run.
 *
 * @param {string} url URL of the spreadsheet.
 */
function sendEmail(url) {
  var footerStyle = 'color: #aaaaaa; font-style: italic;';
  var scriptsLink = 'https://developers.google.com/adwords/scripts/';
  var subject = 'AdWords PageSpeed URL-Sampling Script Results - ' +
      getDateStringInTimeZone('dd MMM yyyy');
  var htmlBody = '<html><body>' +
      '<p>Hello,</p>' +
      '<p>An AdWords Script has run successfully and the output is available ' +
      'here:' +
      '<ul><li><a href="' + url +
      '">AdWords PageSpeed URL-Sampling Script Results</a></li></ul></p>' +
      '<p>Regards,</p>' +
      '<span style="' + footerStyle + '">This email was automatically ' +
      'generated by <a href="' + scriptsLink + '">AdWords Scripts</a>.<span>' +
      '</body></html>';
  var body = 'Please enable HTML to view this report.';
  var options = {htmlBody: htmlBody};
  MailApp.sendEmail(EMAIL_RECIPIENTS, subject, body, options);
}

/**
 * Checks to see that placeholder defaults have been changed.
 *
 * @return {boolean} true if placeholders have been changed, false otherwise.
 */
function defaultsChanged() {
  if (API_KEY == 'INSERT_PAGESPEED_API_KEY_HERE' ||
      SPREADSHEET_TEMPLATE == 'INSERT_SPREADSHEET_URL_HERE' ||
      JSON.stringify(EMAIL_RECIPIENTS) ==
      JSON.stringify(['INSERT_EMAIL_ADDRESS_HERE'])) {
    return false;
  }
  return true;
}

/**
 * Creates a new PageSpeed spreadsheet and populates it with result data.
 *
 * @param {string} name The name to give to the spreadsheet.
 * @param {Object} pageSpeedResult The result from PageSpeed, and the number of
 *     URLs that could have been chosen from.
 * @return {Spreadsheet} The newly-created spreadsheet.
 */
function createPageSpeedSpreadsheet(name, pageSpeedResult) {
  var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_TEMPLATE).copy(name);
  spreadsheet.setSpreadsheetTimeZone(AdWordsApp.currentAccount().getTimeZone());
  var data = pageSpeedResult.table;
  var activeSheet = spreadsheet.getActiveSheet();
  var rowStub = spreadsheet.getRangeByName('ResultRowStub');
  var top = rowStub.getRow();
  var left = rowStub.getColumn();
  var cols = rowStub.getNumColumns();
  if (data.length > 2) { // No need to extend the template if num rows <= 2
    activeSheet.insertRowsAfter(
          spreadsheet.getRangeByName('EmptyUrlRow').getRow(), data.length);
    rowStub.copyTo(activeSheet.getRange(top + 1, left, data.length - 2, cols));
  }
  // Extend the formulas and headings to accommodate the data.
  if (data.length && data[0].length > 4) {
    var metricsRange = activeSheet
        .getRange(top - 6, left + cols, data.length + 5, data[0].length - 4);
    activeSheet.getRange(top - 6, left + cols - 1, data.length + 5)
        .copyTo(metricsRange);
    // Paste in the data values.
    activeSheet.getRange(top - 1, left, data.length, data[0].length)
        .setValues(data);
    // Move the 'Powered by AdWords Scripts' to right corner of table.
    spreadsheet.getRangeByName('PoweredByText').moveTo(activeSheet.getRange(1,
      data[0].length + 1, 1, 1));
    // Set summary - date and number of URLs chosen from.
    var summaryDate = getDateStringInTimeZone('dd MMM yyyy');
    spreadsheet.getRangeByName('SummaryDate').setValue('Summary as of ' +
        summaryDate + '. Results drawn from ' + pageSpeedResult.totalUrls +
        ' URLs.');
  }
  // Add errors if they exist
  if (pageSpeedResult.errors.length) {
    var nextRow = spreadsheet.getRangeByName('FirstErrorRow').getRow();
    var errorRange = activeSheet.getRange(nextRow, 2,
        pageSpeedResult.errors.length, 2);
    errorRange.setValues(pageSpeedResult.errors);
  }
  return spreadsheet;
}

/**
 * This function takes a collection of URLs as provided by the UrlStore object
 * and gets results from the PageSpeed service. However, two important things :
 *     (1) It only processes a handful, as determined by URL_LIMIT.
 *     (2) The URLs returned from iterating on the UrlStore are in a specific
 *     order, designed to produce as much variety as possible (removing the need
 *     to process all the URLs in an account.
 *
 * @param {UrlStore} urlStore Object containing URLs to process.
 * @return {Object} An object with three properties: 'table' - the 2d table of
 *     results, 'totalUrls' - the number of URLs chosen from, and errors.
 */
function getPageSpeedResultsForUrls(urlStore) {
  var count = 0;
  // Associative array for column headings and contextual help URLs.
  var headings = {};
  var errors = {};
  // Record results on a per-URL basis.
  var pageSpeedResults = {};
  var urlTotalCount = 0;

  for (var url in urlStore) {
    if (hasRemainingTimeForUrlFetches()) {
      var result = getPageSpeedResultForSingleUrl(url);
      if (!result.error) {
        pageSpeedResults[url] = result.pageSpeedInfo;
        var columnsResult = result.columnsInfo;
        // Loop through each heading element; the PageSpeed Insights API
        // doesn't always return URLs for each column heading, so aggregate
        // these across each call to get the most complete list.
        var columnHeadings = Object.keys(columnsResult);
        for (var i = 0, lenI = columnHeadings.length; i < lenI; i++) {
          var columnHeading = columnHeadings[i];
          if (!headings[columnHeading] || (headings[columnHeading] &&
            headings[columnHeading].length <
            columnsResult[columnHeading].length)) {
            headings[columnHeading] = columnsResult[columnHeading];
          }
        }
      } else {
        errors[url] = result.error;
      }
      count++;
    }
    urlTotalCount++;
  }

  var tableHeadings = ['URL', 'Speed', 'Usability'];
  var headingKeys = Object.keys(headings);
  for (var y = 0, lenY = headingKeys.length; y < lenY; y++) {
    tableHeadings.push(headingKeys[y]);
  }

  var table = [];
  var pageSpeedResultsUrls = Object.keys(pageSpeedResults);
  for (var r = 0, lenR = pageSpeedResultsUrls.length; r < lenR; r++) {
    var resultUrl = pageSpeedResultsUrls[r];
    var row = [toPageSpeedHyperlinkFormula(resultUrl)];
    var data = pageSpeedResults[resultUrl];
    for (var j = 1, lenJ = tableHeadings.length; j < lenJ; j++) {
      row.push(data[tableHeadings[j]]);
    }
    table.push(row);
  }
  // Present the table back in the order worst-performing-first.
  table.sort(function(first, second) {
    var f1 = isNaN(parseInt(first[1])) ? 0 : parseInt(first[1]);
    var f2 = isNaN(parseInt(first[2])) ? 0 : parseInt(first[2]);
    var s1 = isNaN(parseInt(second[1])) ? 0 : parseInt(second[1]);
    var s2 = isNaN(parseInt(second[2])) ? 0 : parseInt(second[2]);

    if (f1 + f2 < s1 + s2) {
      return -1;
    } else if (f1 + f2 > s1 + s2) {
      return 1;
    }
    return 0;
  });

  // Add hyperlinks to all column headings where they are available.
  for (var h = 0, lenH = tableHeadings.length; h < lenH; h++) {
    // Sheets cannot have multiple links in a single cell at the moment :-/
    if (headings[tableHeadings[h]] &&
        typeof(headings[tableHeadings[h]]) === 'object') {
      tableHeadings[h] = '=HYPERLINK("' + headings[tableHeadings[h]][0] +
          '","' + tableHeadings[h] + '")';
    }
  }

  // Form table from errors
  var errorTable = [];
  var errorKeys = Object.keys(errors);
  for (var k = 0; k < errorKeys.length; k++) {
    errorTable.push([errorKeys[k], errors[errorKeys[k]]]);
  }
  table.unshift(tableHeadings);
  return {
    table: table,
    totalUrls: urlTotalCount,
    errors: errorTable
  };
}

/**
 * Given a URL, returns a spreadsheet formula that displays the URL yet links to
 * the PageSpeed URL for examining this.
 *
 * @param {string} url The URL to embed in the Hyperlink formula.
 * @return {string} A string representation of the spreadsheet formula.
 */
function toPageSpeedHyperlinkFormula(url) {
  return '=HYPERLINK("' +
         'https://developers.google.com/speed/pagespeed/insights/?url=' + url +
         '&tab=mobile","' + url + '")';
}

/**
 * Creates an object of results metrics from the parsed results of a call to
 * the PageSpeed service.
 *
 * @param {Object} parsedPageSpeedResponse The object returned from PageSpeed.
 * @return {Object} An associative array with entries for each metric.
 */
function extractResultRow(parsedPageSpeedResponse) {
  var urlScores = {};
  if (parsedPageSpeedResponse.ruleGroups) {
    var ruleGroups = parsedPageSpeedResponse.ruleGroups;
    // At least one of the SPEED or USABILITY properties will exist, but not
    // necessarily both.
    urlScores.Speed = ruleGroups.SPEED ? ruleGroups.SPEED.score : '-';
    urlScores.Usability = ruleGroups.USABILITY ?
        ruleGroups.USABILITY.score : '-';
  }
  if (parsedPageSpeedResponse.formattedResults &&
    parsedPageSpeedResponse.formattedResults.ruleResults) {
    var resultParts = parsedPageSpeedResponse.formattedResults.ruleResults;
    for (var partName in resultParts) {
      var part = resultParts[partName];
      urlScores[part.localizedRuleName] = part.ruleImpact;
    }
  }
  return urlScores;
}

/**
 * Extracts the headings for the metrics returned from PageSpeed, and any
 * associated help URLs.
 *
 * @param {Object} parsedPageSpeedResponse The object returned from PageSpeed.
 * @return {Object} An associative array used to store column-headings seen in
 *     the response. This can take two forms:
 *     (1) {'heading':'heading', ...} - this form is where no help URLs are
 *     known.
 *     (2) {'heading': [url1, ...]} - where one or more URLs is returned that
 *     provides help on the particular heading item.
 */
function extractColumnsInfo(parsedPageSpeedResponse) {
  var columnsInfo = {};
  if (parsedPageSpeedResponse.formattedResults &&
    parsedPageSpeedResponse.formattedResults.ruleResults) {
    var resultParts = parsedPageSpeedResponse.formattedResults.ruleResults;
    for (var partName in resultParts) {
      var part = resultParts[partName];
      if (!columnsInfo[part.localizedRuleName]) {
        columnsInfo[part.localizedRuleName] = part.localizedRuleName;
      }
      // Find help URLs in the response
      var summary = part.summary;
      if (summary && summary.args) {
        var argList = summary.args;
        for (var i = 0, lenI = argList.length; i < lenI; i++) {
          var arg = argList[i];
          if ((arg.type) && (arg.type == 'HYPERLINK') &&
              (arg.key) && (arg.key == 'LINK') &&
              (arg.value)) {
            columnsInfo[part.localizedRuleName] = [arg.value];
          }
        }
      }
      if (part.urlBlocks) {
        var blocks = part.urlBlocks;
        var urls = [];
        for (var j = 0, lenJ = blocks.length; j < lenJ; j++) {
          var block = blocks[j];
          if (block.header) {
            var header = block.header;
            if (header.args) {
              var args = header.args;
              for (var k = 0, lenK = args.length; k < lenK; k++) {
                var argument = args[k];
                if ((argument.type) &&
                    (argument.type == 'HYPERLINK') &&
                    (argument.key) &&
                    (argument.key == 'LINK') &&
                    (argument.value)) {
                  urls.push(argument.value);
                }
              }
            }
          }
        }
        if (urls.length > 0) {
          columnsInfo[part.localizedRuleName] = urls;
        }
      }
    }
  }
  return columnsInfo;
}

/**
 * Extracts a suitable error message to display for a failed URL. The error
 * could be passed in in the nested PageSpeed error format, or there could have
 * been a more fundamental error in the fetching of the URL. Extract the
 * relevant message in each case.
 *
 * @param {string} errorMessage The error string.
 * @return {string} A formatted error message.
 */
function formatErrorMessage(errorMessage) {
  var formattedMessage = null;
  if (!errorMessage) {
    formattedMessage = 'Unknown error message';
  } else {
    try {
      var parsedError = JSON.parse(errorMessage);
      // This is the nested structure expected from PageSpeed
      if (parsedError.error && parsedError.error.errors) {
        var firstError = parsedError.error.errors[0];
        formattedMessage = firstError.message;
      } else if (parsedError.message) {
        formattedMessage = parsedError.message;
      } else {
        formattedMessage = errorMessage.toString();
      }
    } catch (e) {
      formattedMessage = errorMessage.toString();
    }
  }
  return formattedMessage;
}

/**
 * Calls the PageSpeed API for a single URL, and attempts to parse the resulting
 * JSON. If successful, produces an object for the metrics returned, and an
 * object detailing the headings and help URLs seen.
 *
 * @param {string} url The URL to run PageSpeed for.
 * @return {Object} An object with pageSpeed metrics, column-heading info
 *     and error properties.
 */
function getPageSpeedResultForSingleUrl(url) {
  var parsedResponse = null;
  var errorMessage = null;
  var retries = 0;

  while ((!parsedResponse || parsedResponse.responseCode !== 200) &&
         retries < MAX_RETRIES) {
    errorMessage = null;
    var fetchResult = checkUrl(url);
    if (fetchResult.responseText) {
      try {
        parsedResponse = JSON.parse(fetchResult.responseText);
        break;
      } catch (e) {
        errorMessage = formatErrorMessage(e);
      }
    } else {
      errorMessage = formatErrorMessage(fetchResult.error);
    }
    retries++;
    Utilities.sleep(1000 * Math.pow(2, retries));
  }
  if (!errorMessage) {
    var columnsInfo = extractColumnsInfo(parsedResponse);
    var urlScores = extractResultRow(parsedResponse);
  }
  return {
    pageSpeedInfo: urlScores,
    columnsInfo: columnsInfo,
    error: errorMessage
  };
}

/**
 * Gets the most representative URL that would be used on a mobile device
 * taking into account Upgraded URLs.
 *
 * @param {Entity} entity An AdWords entity such as an Ad, Keyword or Sitelink.
 * @return {string} The URL.
 */
function getMobileUrl(entity) {
  var urls = entity.urls();
  var url = null;
  if (urls) {
    if (urls.getMobileFinalUrl()) {
      url = urls.getMobileFinalUrl();
    } else if (urls.getFinalUrl()) {
      url = urls.getFinalUrl();
    }
  }
  if (!url) {
    switch (entity.getEntityType()) {
      case 'Ad':
      case 'Keyword':
        url = entity.getDestinationUrl();
        break;
      case 'Sitelink':
      case 'AdGroupSitelink':
      case 'CampaignSitelink':
        url = entity.getLinkUrl();
        break;
      default:
        Logger.log('No URL found' + entity.getEntityType());
    }
  }
  if (url) {
    url = encodeURI(decodeURIComponent(url));
  }
  return url;
}

/**
 * Determines whether there is enough remaining time to continue iterating
 * through the account.
 *
 * @return {boolean} Returns true if there is enough time remaining to continue
 *     iterating.
 */
function hasRemainingTimeForAccountIteration() {
  var remainingTime = AdWordsApp.getExecutionInfo().getRemainingTime();
  return remainingTime > SPREADSHEET_PREP_TIME_SECS + URL_FETCH_TIME_SECS;
}

/**
 * Determines whether there is enough remaining time to continue fetching URLs.
 *
 * @return {boolean} Returns true if there is enough time remaining to continue
 *     fetching.
 */
function hasRemainingTimeForUrlFetches() {
  var remainingTime = AdWordsApp.getExecutionInfo().getRemainingTime();
  return remainingTime > SPREADSHEET_PREP_TIME_SECS;
}

/**
 * Iterates through all the available Campaigns and AdGroups, to a limit of
 * defined in CAMPAIGN_LIMIT and ADGROUP_LIMIT until the time limit is reached
 * allowing enough time for the post-iteration steps, e.g. fetching and
 * analysing URLs and building results.
 *
 * @return {UrlStore} An UrlStore object with URLs from the account.
 */
function getUrlsFromAccount() {
  var urlStore = new UrlStore(EXTRA_URLS_TO_CHECK);
  var campaigns = AdWordsApp.campaigns()
                      .forDateRange('LAST_30_DAYS')
                      .withCondition('Status = "ENABLED"')
                      .orderBy('Clicks DESC')
                      .withLimit(CAMPAIGN_LIMIT)
                      .get();
  while (campaigns.hasNext() && hasRemainingTimeForAccountIteration()) {
    var campaign = campaigns.next();
    var campaignUrls = getUrlsFromCampaign(campaign);
    urlStore.addUrls(campaignUrls);
  }
  var adGroups = AdWordsApp.adGroups()
                     .forDateRange('LAST_30_DAYS')
                     .withCondition('Status = "ENABLED"')
                     .orderBy('Clicks DESC')
                     .withLimit(ADGROUP_LIMIT)
                     .get();
  while (adGroups.hasNext() && hasRemainingTimeForAccountIteration()) {
    var adGroup = adGroups.next();
    var adGroupUrls = getUrlsFromAdGroup(adGroup);
    urlStore.addUrls(adGroupUrls);
  }
  return urlStore;
}

/**
 * Work through an ad group's members in the account, but only up to the maximum
 * specified by the SITELINK_LIMIT.
 *
 * @param {AdGroup} adGroup The adGroup to process.
 * @return {!Array.<string>} A list of URLs.
 */
function getUrlsFromAdGroup(adGroup) {
  var uniqueUrls = {};
  var sitelinks =
      adGroup.extensions().sitelinks().withLimit(SITELINK_LIMIT).get();
  while (sitelinks.hasNext()) {
    var sitelink = sitelinks.next();
    var url = getMobileUrl(sitelink);
    if (url) {
      uniqueUrls[url] = true;
    }
  }
  return Object.keys(uniqueUrls);
}

/**
 * Work through a campaign's members in the account, but only up to the maximum
 * specified by the AD_LIMIT, KEYWORD_LIMIT and SITELINK_LIMIT.
 *
 * @param {Campaign} campaign The campaign to process.
 * @return {!Array.<string>} A list of URLs.
 */
function getUrlsFromCampaign(campaign) {
  var uniqueUrls = {};
  var url = null;
  var sitelinks = campaign
      .extensions().sitelinks().withLimit(SITELINK_LIMIT).get();
  while (sitelinks.hasNext()) {
    var sitelink = sitelinks.next();
    url = getMobileUrl(sitelink);
    if (url) {
      uniqueUrls[url] = true;
    }
  }
  var ads = campaign.ads().forDateRange('LAST_30_DAYS')
      .withCondition('Status = "ENABLED"')
      .orderBy('Clicks DESC')
      .withLimit(AD_LIMIT)
      .get();
  while (ads.hasNext()) {
    var ad = ads.next();
    url = getMobileUrl(ad);
    if (url) {
      uniqueUrls[url] = true;
    }
  }
  var keywords = campaign.keywords().forDateRange('LAST_30_DAYS')
      .withCondition('Status = "ENABLED"')
      .orderBy('Clicks DESC')
      .withLimit(KEYWORD_LIMIT)
      .get();
  while (keywords.hasNext()) {
    var keyword = keywords.next();
    url = getMobileUrl(keyword);
    if (url) {
      uniqueUrls[url] = true;
    }
  }
  return Object.keys(uniqueUrls);
}

/**
 * Produces a formatted string representing a given date in a given time zone.
 *
 * @param {string} format A format specifier for the string to be produced.
 * @param {Date} date A date object. Defaults to the current date.
 * @param {string} timeZone A time zone. Defaults to the account's time zone.
 * @return {string} A formatted string of the given date in the given time zone.
 */
function getDateStringInTimeZone(format, date, timeZone) {
  date = date || new Date();
  timeZone = timeZone || AdWordsApp.currentAccount().getTimeZone();
  return Utilities.formatDate(date, timeZone, format);
}

/**
 * UrlStore - this is an object that takes URLs, added one by one, and then
 * allows them to be iterated through in a particular order, which aims to
 * maximise the variety between the returned URLs.
 *
 * This works by splitting the URL into three parts: host, path and params
 * In comparing two URLs, most weight is given if the hosts differ, then if the
 * paths differ, and finally if the params differ.
 *
 * UrlStore sets up a tree with 3 levels corresponding to the above. The full
 * URL exists at the leaf level. When a request is made for an iterator, a copy
 * is taken, and a path through the tree is taken, using the first host. Each
 * entry is removed from the tree as it is used, and the layers are rotated with
 * each call such that the next call will result in a different host being used
 * (where possible).
 *
 * Where opt_manualUrls are supplied at construction time, these will take
 * precedence over URLs added subsequently to the object.
 *
 * @param {?Array.<string>=} opt_manualUrls An optional list of URLs to check.
 * @constructor
 */
function UrlStore(opt_manualUrls) {
  this.manualUrls = opt_manualUrls || [];
  this.paths = {};
  this.re = /^(https?:\/\/[^\/]+)([^?#]*)(.*)$/;
}

/**
 * Adds a URL to the UrlStore.
 *
 * @param {string} url The URL to add.
 */
UrlStore.prototype.addUrl = function(url) {
  if (!url || this.manualUrls.indexOf(url) > -1) {
    return;
  }
  var matches = this.re.exec(url);
  if (matches) {
    var host = matches[1];
    var path = matches[2];
    var param = matches[3];
    if (!this.paths[host]) {
      this.paths[host] = {};
    }
    var hostObj = this.paths[host];
    if (!path) {
      path = '/';
    }
    if (!hostObj[path]) {
      hostObj[path] = {};
    }
    var pathObj = hostObj[path];
    pathObj[url] = url;
  }
};

/**
 * Adds multiple URLs to the UrlStore.
 *
 * @param {!Array.<string>} urls The URLs to add.
 */
UrlStore.prototype.addUrls = function(urls) {
  for (var i = 0; i < urls.length; i++) {
    this.addUrl(urls[i]);
  }
};

/**
 * Creates and returns an iterator that tries to iterate over all available
 * URLs return them in an order to maximise the difference between them.
 *
 * @return {UrlStoreIterator} The new iterator object.
 */
UrlStore.prototype.__iterator__ = function() {
  return new UrlStoreIterator(this.paths, this.manualUrls);
};

var UrlStoreIterator = (function() {
  function UrlStoreIterator(paths, manualUrls) {
    this.manualUrls = manualUrls.slice();
    this.urls = objectToArray_(paths);
  }
  UrlStoreIterator.prototype.next = function() {
    if (this.manualUrls.length) {
      return this.manualUrls.shift();
    }
    if (this.urls.length) {
      return pick_(this.urls);
    } else {
      throw StopIteration;
    }
  };
  function rotate_(a) {
    if (a.length < 2) {
      return a;
    } else {
      var e = a.pop();
      a.unshift(e);
    }
  }
  function pick_(a) {
    if (typeof a[0] === 'string') {
      return a.shift();
    } else {
      var element = pick_(a[0]);
      if (!a[0].length) {
        a.shift();
      } else {
        rotate_(a);
      }
      return element;
    }
  }

  function objectToArray_(obj) {
    if (typeof obj !== 'object') {
      return obj;
    }

    var a = [];
    for (var k in obj) {
      a.push(objectToArray_(obj[k]));
    }
    return a;
  }
  return UrlStoreIterator;
})();

/**
 * Runs the PageSpeed fetch.
 *
 * @param {string} url
 * @return {Object} An object containing either the successful response from the
 *     server, or an error message.
 */
function checkUrl(url) {
  var result = null;
  var error = null;
  var fullUrl = PAGESPEED_URL + 'key=' + API_KEY + '&url=' + encodeURI(url) +
                '&prettyprint=false&strategy=mobile';
  var params = {muteHttpExceptions: true};
  try {
    var pageSpeedResponse = UrlFetchApp.fetch(fullUrl, params);
    if (pageSpeedResponse.getResponseCode() === 200) {
      result = pageSpeedResponse.getContentText();
    } else {
      error = pageSpeedResponse.getContentText();
    }
  } catch (e) {
    error = e.message;
  }
  return {
    responseText: result,
    error: error
  };
}

Looking for the Manager Account (MCC) version? Click here

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.