Compartilhar via


Melhores práticas para Scripts de Publicidade da Microsoft

Para melhorar o desempenho dos scripts e o da plataforma, reveja e siga as melhores práticas descritas abaixo.

Trabalhar com seletores

Utilizar filtros

Utilize os filtros de um seletor em vez de filtrar as entidades. Os seletores permitem-lhe filtrar por IDs e condições. Por exemplo, pode filtrar pelo desempenho de uma entidade (campanhas de retorno com uma média DE CPC superior a 10), o respetivo estado (campanhas que estão em pausa), o nome do objeto principal da entidade e muito mais.

Vantagens da utilização de filtros:

  • Limita o número de entidades que o seletor devolve apenas às entidades de que precisa.

  • Permite que o script seja executado mais rapidamente (menos entidades a devolver e processar)

  • Reduz a probabilidade de aumentar face aos limites de leitura de entidades (veja Limites de execução de scripts).

Caminho certo

    var adGroups = AdsApp.adGroups()
        .withCondition('Status = PAUSED')
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        // Do something with paused ad group.
    }

Caminho errado

    var adGroups = AdsApp.adGroups().get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        
        if (adGroup.isPaused() == true) {
            // Do something with paused ad group.
        }
    }

Não percorra a hierarquia de entidades

Se quiser obter as entidades subordinadas de uma entidade ou a entidade principal da entidade, não percorre a hierarquia de entidades para as obter.

Para obter entidades subordinados, utilize a coleção da entidade subordinada no nível pretendido.

Caminho certo

    // Get all ads.
    var ads = AdsApp.ads().get();

    while (ads.hasNext()) {
        var ad = ads.next();
        // Do something with ad.
    }

Em alternativa, se quiser anúncios de uma campanha específica:

    // Get all ads in the campaign, 'mycampaign'.
    var ads = AdsApp.ads()
        .withCondition("CampaignName = 'mycampaign'")
        .get();

    while (ads.hasNext()) {
        var ad = ads.next();
        // Do something with ad.
    }

Em alternativa, obter anúncios de uma campanha se tiver o objeto de campanha:

    // Get all ads in the campaign.
    var ads = campaign.ads().get();

    while (ads.hasNext()) {
        var ad = ads.next();
        // Do something with ad.
    }

Caminho errado

    var campaigns = AdsApp.campaigns().get();

    while (campaigns.hasNext()) {
        var adGroups = campaigns.next().adGroups().get();
        
        while (adGroups.hasNext()) {
            var ads = adGroups.next().ads().get();

            while (ads.hasNext()) {
                var ad = ads.next();
                // Do something with ad.
            }
        }
    }

O mesmo se aplica se quiser obter o principal de uma entidade. Em vez de percorrer a hierarquia para obter o principal, utilize o método de acessório principal da entidade subordinada.

Caminho certo

    // Get all ads.
    var ads = AdsApp.ads()
        .withCondition('Clicks > 5')
        .forDateRange('LAST_7_DAYS')
        .get();

    while (ads.hasNext()) {
        var ad = ads.next();
        
        // Do something with campaign and adGroup.
        var adGroup = ad.adGroup();
        var campaign = ad.campaign();
    }

Caminho errado

    var campaigns = AdsApp.campaigns().get();

    while (campaigns.hasNext()) {
        var campaign = campaigns.next();
        var adGroups = campaign.adGroups().get();
        
        while (adGroups.hasNext()) {
            var adGroup = adGroups.next();
            var ads = adGroup.ads().get();

            while (ads.hasNext()) {
                var ad = ads.next();
                
                if ('<some condition is met>') {
                    // Do something with campaign and adGroup.
                }
            }
        }
    }

Utilizar IDs de entidade sempre que possível

A utilização de IDs para filtrar entidades proporciona o melhor desempenho.

Isto

    var adGroups = AdsApp.adGroups()
        .withIds(["123456"])
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        
        // Do something with adGroup.
    }

Proporciona um melhor desempenho do que este

    var adGroups = AdsApp.adGroups()
        .withCondition("Name = 'myadgroup'")
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        
        // Do something with adGroup.
    }

Evite ciclos apertados com seletores e um número desnecessário de gets

Evite ciclos com pedidos get que obtenham uma única entidade. Por exemplo, digamos que executa um relatório de desempenho de palavras-chave e quer atualizar as palavras-chave no relatório. Em vez de obter uma linha do relatório, obter a palavra-chave e, em seguida, atualizá-la, deve criar uma lista dos IDs de palavra-chave à medida que percorre cada linha no relatório. Em seguida, transmita a lista de IDs para o seletor para obter todas as palavras-chave num único pedido get. Em seguida, pode iterar através da lista de palavras-chave e atualizá-las.

Caminho certo

    var report = AdsApp.report('<report query goes here>');

    var rows = report.rows();
    var idLists = []; // an array where each element contains an array of IDs.
    var idList = [];  // array of IDs that's limited to maxCount.
    var maxCount = 10000;

    while (rows.hasNext()) {
        var row = rows.next();

        if (idList.length < maxCount) {
            idList.push(row['id']);
        }
        else {
            idLists.push(idList);
            idList = [];
        }
    }

    for (idList of idLists) {
        var keywords = AdsApp.keywords()
            .withIds(idList)
            .get();

        while (keywords.hasNext()) {
            var keyword = keywords.next();
            // update the keyword        
        }
    }

Caminho errado

    var report = AdsApp.report('<report query goes here>');

    var rows = report.rows();

    while (rows.hasNext()) {
        var row = rows.next();

        var keyword = AdsApp.keywords()
            .withIds([row['id']])
            .get()
            .next();

        // update the keyword        
    }

Inclua o método forDateRange apenas se planear chamar o método getStats da entidade

Chamar o método de forDateRange um seletor faz com que o seletor obtenha os dados de desempenho da entidade. Obter os dados de desempenho de uma entidade é dispendioso, pelo que só os obtém se planear chamar o método da getStats entidade e utilizar os dados.

O intervalo de datas que especificar para uma entidade não se aplica às entidades principais ou subordinadas a que acede a partir dessa entidade. Por exemplo, se receber um grupo de anúncios e, em seguida, obter a campanha principal e tentar aceder às métricas de desempenho da campanha, a chamada falha.

A campaignStats.getReturnOnAdSpend() chamada no exemplo seguinte falha porque o intervalo de datas se aplica ao grupo de anúncios e não à campanha.

    var myAdGroups = AdsApp.adGroups().
        .withCondition("CampaignName CONTAINS 'gen'")
        .forDateRange("LAST_7_DAYS")
        .get();

    while (myAdGroups.hasNext()) {
        var adGroup = myAdGroups.next();
        var campaign = adGroup.getCampaign();
        var campaignStats = campaign.getStats();
        var campaignROAS = campaignStats.getReturnOnAdSpend();
    }

Para que isto funcione, tem de criar um seletor para a campanha.

    var myAdGroups = AdsApp.adGroups().
        .withCondition("CampaignName CONTAINS 'gen'")
        .forDateRange("LAST_7_DAYS")
        .get();

    while (myAdGroups.hasNext()) {
        var adGroup = myAdGroups.next();
        var campaign = AdsApp.campaigns()
            .withIds([adGroup.getCampaign().getId()])
            .forDateRange("LAST_7_DAYS")
            .get()
            .next();
        var campaignStats = campaign.getStats();
        var campaignROAS = campaignStats.getReturnOnAdSpend();
    }

Não altere a propriedade de uma entidade utilizada como condição no seletor

Os iteradores reduzem a pressão da memória ao carregar apenas um único item de cada vez em vez de todo o conjunto de itens. Por este motivo, alterar uma propriedade que utilizou como condição no seletor pode causar um comportamento inesperado.

Caminho certo

    var adGroups = []; 

    var iterator = AdsApp.adGroups()
        .withCondition('Status = ENABLED')
        .get();

    while (iterator.hasNext()) {
        adGroups.push(iterator.next());
    }

    for (var adGroup of adGroups) {
        adGroup.pause();
    }

Caminho errado

    var adGroups = AdsApp.adGroups()
        .withCondition('Status = ENABLED')
        .get();

    while (adGroups.hasNext()) {
        var adGroup = adGroups.next();
        adGroup.pause();
    }

Atualizações de criação de batches

Para melhorar o desempenho, os Scripts processam pedidos de compilação em lotes. Se chamar o método de operação de um pedido de compilação, força os Scripts a processar imediatamente os pedidos de compilação em fila, negando quaisquer ganhos de desempenho. Se estiver a criar mais do que uma entidade, não execute os métodos de operação no mesmo ciclo que utiliza para criar a entidade. Isto leva a um fraco desempenho porque apenas uma entidade de cada vez é processada. Em vez disso, crie uma matriz das operações e processe-as após o ciclo de compilação.

Caminho certo

    // An array to hold the operations, so you 
    // can process them after all the entities are queued.
    var operations = []; 

    // Create all the new entities.
    for (var i = 0; i < keywords.length; i++) {
        var keywordOperation = AdsApp.adGroups().get().next()
          .newKeywordBuilder()
          .withText(keywords[i])
          .build();
        operations.push(keywordOperation);
    }

    // Now call the operation method so the build requests
    // get processed in batches.
    for (var i = 0; i < operations.length; i++) {
        var newKeyword = operations[i].getResult();
    }

Caminho errado

    for (var i = 0; i < keywords.length; i++) {
        var keywordOperation = AdsApp.adGroups().get().next()  // Get the first ad group
          .newKeywordBuilder()  // Add the keyword to the ad group
          .withText(keywords[i])
          .build();

        // Don't get results in the same loop that creates
        // the entity because Scripts then only processes one
        // entity at a time.
        var newKeyword = keywordOperation.getResult();
    }

O mesmo acontece se atualizar uma entidade e, em seguida, obter a mesma propriedade que atualizou. Não faça isto:

    var bidAmount = 1.2;

    while (keywords.hasNext()) {
        var keyword = keywords.next();

        keyword.bidding().setCpc(bidAmount);

        if (keyword.bidding().getCpc() != bidAmount) {
            Logger.log(`Failed to update bid amount for keyword, ${keyword.getText()} (${keyword.getId()})`);
        }
    }

Utilizar a palavra-chave de lucro ao obter grandes conjuntos de entidades

Obter um grande número de entidades e carregá-las numa única lista que processa num ciclo tem algumas desvantagens:

  1. Consoante o tamanho do pedido, pode ser necessário n número de pedidos de back-end para obter todas as entidades antes de o ciclo começar. Se não processar todos, o tempo e o poder de computação utilizados para obter as entidades não processadas são desperdiçados. Por exemplo, se obter palavras-chave de 10 000 e o ciclo falhar após o processamento de apenas 2K palavras-chave, o tempo e o poder de computação utilizados para obter as restantes palavras-chave 8K são desperdiçados.

  2. A criação da lista requer mais memória para conter todas as entidades ao mesmo tempo.

Para resolver estes problemas, utilize a palavra-chave de lucro, que permite ao script obter entidades a pedido ou, de certa forma, "preguiçosamente" ocultá-las apenas quando forem necessárias. Isto significa que o script não está a fazer mais chamadas do que o necessário neste momento e a não transmitir grandes listas de objetos.

Este exemplo inclui o registo para ilustrar o fluxo de controlo ao utilizar a palavra-chave de lucro .

function main() {
    const keywords = getKeywords();

    //@ts-ignore <-- suppresses iterator error
    for (const keyword of keywords) {
        Logger.log("in for loop\n\n");
    }
}

// Note that you must use the yield keyword in a generator function - see the
// '*' at the end of the function keyword.

function* getKeywords() {
    const keywords = AdsApp.keywords()
        .withCondition("Status = ENABLED")
        .withCondition("CombinedApprovalStatus = APPROVED")
        .withLimit(10)
        .get();

    Logger.log(`total keywords in account: ${keywords.totalNumEntities()} \n\n`);

    while (keywords.hasNext()) {
        Logger.log("before next()\n\n");
        yield keywords.next();
        Logger.log("after next\n\n");
    }
}

Padrão de chamada para evitar limites de entidades

Existe um limite para o número de entidades que os Scripts podem devolver para uma conta. Se o pedido devolver mais do que este limite, os Scripts geram um erro com a mensagem. Existem demasiadas entidades. O exemplo seguinte mostra o padrão de chamada que deve utilizar ao obter um grande número de entidades. O exemplo tenta obter primeiro todas as palavras-chave ao nível da conta. Se isso falhar, tenta fazer várias chamadas para obter palavras-chave por campanha, uma vez que normalmente existem menos entidades ao nível da campanha. Geralmente, pode continuar este padrão até ao nível do grupo de anúncios, se necessário.

Para obter informações sobre como utilizar a palavra-chave de lucro , veja Utilizar a palavra-chave de lucro ao obter grandes conjuntos de entidades.

function* getEntities() {
    const applyConditions = _ => _
        .withCondition('CampaignStatus = ENABLED')
        .withCondition('AdGroupStatus = ENABLED')
        .withCondition('Status = ENABLED')
        .withCondition("CombinedApprovalStatus = DISAPPROVED");

    try {
        // Get the account's keywords. 
        const keywords = applyConditions(AdsApp.keywords()).get();

        while (keywords.hasNext()) {
            yield keywords.next();
        }
    } catch (e) {
        if (!e.message.startsWith('There are too many entities')) {
            throw e;
        }

        // If there are too many keywords at the account level,
        // get keywords by campaigns under the account.
        const campaigns = AdsApp.campaigns().get();

        while (campaigns.hasNext()) {
            const campaign = campaigns.next();

            const keywords = applyConditions(campaign.keywords()).get();

            while (keywords.hasNext()) {
                yield keywords.next();
            }
        }
    }
}