Compartir a través de


Procedimientos recomendados para scripts de Microsoft Advertising

Para mejorar el rendimiento de los scripts y el de la plataforma, revise y siga los procedimientos recomendados que se describen a continuación.

Trabajar con selectores

Uso de filtros

Use los filtros de un selector en lugar de filtrar las entidades usted mismo. Los selectores permiten filtrar por identificadores y condiciones. Por ejemplo, puede filtrar por el rendimiento de una entidad (la campaña de devolución con un CPC medio superior a 10), su estado (campañas que están en pausa), el nombre del objeto primario de la entidad, etc.

Ventajas del uso de filtros:

  • Limita el número de entidades que el selector devuelve a solo las entidades que necesita.

  • Permite que el script se ejecute más rápido (menos entidades para devolver y procesar)

  • Reduce la posibilidad de que se tope con los límites de lectura de entidad (consulte Límites de ejecución de scripts).

Camino correcto

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

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

Manera incorrecta

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

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

No atravesar la jerarquía de entidades

Si desea obtener las entidades secundarias de una entidad o la entidad primaria de la entidad, no recorra la jerarquía de entidades para obtenerlas.

Para obtener entidades secundarias, use la colección de la entidad secundaria en el nivel que desee.

Camino correcto

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

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

O bien, si quieres anuncios de una campaña 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.
    }

O bien, obtener los anuncios de una campaña si tienes el objeto de campaña:

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

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

Manera incorrecta

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

Lo mismo se aplica si desea obtener el elemento primario de una entidad. En lugar de recorrer la jerarquía para obtener el elemento primario, use el método de descriptor de acceso primario de la entidad secundaria.

Camino correcto

    // 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();
    }

Manera incorrecta

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

Uso de identificadores de entidad cuando sea posible

El uso de identificadores para filtrar entidades proporciona el mejor rendimiento.

Éste

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

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

Proporciona un mejor rendimiento que este

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

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

Evitar bucles ajustados con selectores y un número innecesario de obtiene

Evite bucles con solicitudes get que obtienen una sola entidad. Por ejemplo, supongamos que ejecuta un informe de rendimiento de palabras clave y quiere actualizar las palabras clave en el informe. En lugar de obtener una fila del informe, obtener la palabra clave y, a continuación, actualizarla, debe crear una lista de los identificadores de palabra clave a medida que recorre en bucle cada fila del informe. A continuación, pase la lista de identificadores al selector para obtener todas las palabras clave en una única solicitud get. A continuación, puede iterar a través de la lista de palabras clave y actualizarlas.

Camino correcto

    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        
        }
    }

Manera incorrecta

    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        
    }

Incluya el método forDateRange solo si tiene previsto llamar al método getStats de la entidad.

Llamar al método de forDateRange un selector hace que el selector obtenga los datos de rendimiento de la entidad. Obtener los datos de rendimiento de una entidad es costoso, por lo que solo puede obtenerlos si tiene previsto llamar al método de getStats la entidad y usar los datos.

El intervalo de fechas que especifique para una entidad no se aplica a las entidades primarias o secundarias a las que tiene acceso desde esa entidad. Por ejemplo, si obtienes un grupo de anuncios y luego obtienes su campaña primaria e intentas acceder a las métricas de rendimiento de la campaña, se produce un error en la llamada.

Se campaignStats.getReturnOnAdSpend() produce un error en la llamada del ejemplo siguiente porque el intervalo de fechas se aplica al grupo de anuncios y no a la campaña.

    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 esto funcione, debe crear un selector para la campaña.

    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();
    }

No cambie la propiedad de una entidad que se usa como condición en el selector.

Los iteradores reducen la presión de memoria al cargar solo un elemento a la vez en lugar de todo el conjunto de elementos. Por este motivo, el cambio de una propiedad que usó como condición en el selector puede provocar un comportamiento inesperado.

Camino correcto

    var adGroups = []; 

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

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

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

Manera incorrecta

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

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

Actualizaciones por lotes

Para mejorar el rendimiento, Scripts procesa solicitudes de compilación en lotes. Si llama al método de operación de una solicitud de compilación, obliga a scripts a procesar las solicitudes de compilación en cola inmediatamente, lo que niega las mejoras de rendimiento. Si va a crear más de una entidad, no ejecute los métodos de operación en el mismo bucle que usa para compilar la entidad. Esto da lugar a un rendimiento deficiente porque solo se procesa una entidad a la vez. En su lugar, cree una matriz de las operaciones y procese después del bucle de compilación.

Camino correcto

    // 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();
    }

Manera incorrecta

    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();
    }

Lo mismo sucede si actualiza una entidad y, a continuación, obtiene la misma propiedad que ha actualizado. No haga esto:

    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()})`);
        }
    }

Uso de la palabra clave yield al obtener grandes conjuntos de entidades

Recuperar un gran número de entidades y cargarlas en una sola lista que se procesa en un bucle tiene un par de desventajas:

  1. En función del tamaño de la solicitud, puede tardar n número de solicitudes de back-end en capturar todas las entidades antes de que se inicie el bucle. Si no los procesa todos, se desperdicia el tiempo y la potencia informática que se usa para obtener las entidades sin procesar. Por ejemplo, si recupera palabras clave de 10 000 y el bucle se interrumpe después de procesar solo 2 000 palabras clave, se desperdicia el tiempo y la potencia informática que se usa para obtener las palabras clave 8K restantes.

  2. La creación de la lista requiere más memoria para contener todas las entidades al mismo tiempo.

Para solucionar estos problemas, use la palabra clave yield , que permite que el script capture entidades a petición o, en algún sentido, capturarlas "de forma diferenciosa" solo cuando sean necesarias. Esto significa que el script no realiza más llamadas de las que necesita en este momento y no pasa listas grandes de objetos.

En este ejemplo se incluye el registro para ilustrar el flujo de control cuando se usa la palabra clave yield .

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");
    }
}

Patrón de llamada para evitar límites de entidad

Hay un límite en el número de entidades que los scripts pueden devolver para una cuenta. Si la solicitud devuelve más de este límite, Scripts produce un error con el mensaje, Hay demasiadas entidades. En el ejemplo siguiente se muestra el patrón de llamada que debe usar al obtener un gran número de entidades. En el ejemplo se intenta capturar primero todas las palabras clave en el nivel de cuenta. Si se produce un error, intenta realizar varias llamadas para capturar palabras clave por campaña, ya que normalmente hay menos entidades en el nivel de campaña. Por lo general, puede continuar con este patrón hasta el nivel de grupo de anuncios si es necesario.

Para obtener información sobre el uso de la palabra clave yield , consulte Uso de la palabra clave yield al obtener 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();
            }
        }
    }
}