Bewährte Methoden für Microsoft Advertising Scripts

Um die Leistung Ihrer Skripts und die der Plattform zu verbessern, überprüfen und befolgen Sie bitte die unten aufgeführten bewährten Methoden.

Arbeiten mit Selektoren

Verwenden von Filtern

Verwenden Sie die Filter eines Selektors, anstatt die Entitäten selbst zu filtern. Mit Selektoren können Sie nach IDs und Bedingungen filtern. Sie können beispielsweise nach der Leistung einer Entität filtern (zurückgegebene Kampagnen mit durchschnittlichen CPC von mehr als 10), ihrem Status (pausierte Kampagnen), dem Namen des übergeordneten Objekts der Entität und mehr.

Vorteile der Verwendung von Filtern:

  • Schränkt die Anzahl der Entitäten, die der Selektor zurückgibt, auf die Entitäten ein, die Sie benötigen.

  • Ermöglicht die schnellere Ausführung des Skripts (weniger Entitäten, die zurückgegeben und verarbeitet werden müssen)

  • Verringert die Wahrscheinlichkeit, dass Sie an Lesegrenzwerte für Objekte stoßen (siehe Skriptausführungslimits).

Richtiger Weg

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

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

Falscher Weg

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

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

Durchlaufen Sie nicht die Entitätshierarchie

Wenn Sie die untergeordneten Entitäten einer Entität oder die übergeordnete Entität der Entität abrufen möchten, durchlaufen Sie nicht die Entitätshierarchie, um sie abzurufen.

Um untergeordnete Entitäten abzurufen, verwende die Sammlung der untergeordneten Entität auf der gewünschten Ebene.

Richtiger Weg

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

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

Oder wenn Sie Anzeigen aus einer bestimmten Kampagne wünschen:

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

Oder Sie erhalten die Anzeigen einer Kampagne, wenn Sie über das Kampagnenobjekt verfügen:

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

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

Falscher Weg

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

Dasselbe gilt, wenn Sie das übergeordnete Element einer Entität abrufen möchten. Anstatt die Hierarchie zu durchlaufen, um das übergeordnete Element zu erhalten, verwenden Sie die übergeordnete Accessormethode der untergeordneten Entität.

Richtiger Weg

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

Falscher Weg

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

Entitäts-IDs verwenden, wenn möglich

Die Verwendung von IDs zum Filtern von Entitäten bietet die beste Leistung.

Das bedeutet,

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

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

Bietet eine bessere Leistung

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

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

Vermeiden Sie enge Schleifen mit Selektoren und eine unnötige Anzahl von Gets

Vermeiden Sie Schleifen bei Abrufen von Anforderungen, die eine einzelne Entität abrufen. Angenommen, Sie führen einen Bericht zur Schlüsselwort(keyword)-Performance aus und möchten die Keywords im Bericht aktualisieren. Anstatt eine Zeile aus dem Bericht zu erhalten, das Schlüsselwort (Keyword) abzurufen und es dann zu aktualisieren, sollten Sie eine Liste der Schlüsselwort (Schlüsselwort (Keyword)-IDs erstellen, während Sie die einzelnen Zeilen im Bericht durchlaufen. Übergeben Sie dann die Liste der IDs an den Selektor, um alle Schlüsselwörter in einer einzigen GET-Anforderung abzurufen. Anschließend können Sie die Liste der Schlüsselwörter durchlaufen und aktualisieren.

Richtiger Weg

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

Falscher Weg

    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        
    }

Schließen Sie die forDateRange-Methode nur ein, wenn Sie die getStats-Methode der Entität aufrufen möchten

Das Aufrufen der Methode eines Selektors forDateRange bewirkt, dass der Selektor die Leistungsdaten der Entität abruft. Das Abrufen der Leistungsdaten einer Entität ist teuer, daher sollten Sie sie nur abrufen, wenn Sie die Methode der getStats Entität aufrufen und die Daten verwenden möchten.

Der Datumsbereich, den Sie für eine Entität angeben, gilt nicht für die übergeordneten oder untergeordneten Entitäten, auf die Sie von dieser Entität aus zugreifen. Wenn Sie beispielsweise eine Anzeigengruppe und dann die übergeordnete Kampagne abrufen und versuchen, auf die Leistungsmetriken der Kampagne zuzugreifen, schlägt der Aufruf fehl.

Der campaignStats.getReturnOnAdSpend() Aufruf im folgenden Beispiel schlägt fehl, da der Datumsbereich für die Anzeigengruppe und nicht für die Kampagne gilt.

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

Damit dies funktioniert, müssen Sie einen Selektor für die Kampagne erstellen.

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

Ändern Sie nicht die Eigenschaft einer Entität, die als Bedingung im Selektor verwendet wird

Iteratoren reduzieren die Arbeitsspeicherauslastung, indem immer nur ein einzelnes Element und nicht der gesamte Satz von Elementen geladen wird. Aus diesem Grund kann das Ändern einer Eigenschaft, die Sie als Bedingung im Selektor verwendet haben, zu unerwartetem Verhalten führen.

Richtiger Weg

    var adGroups = []; 

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

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

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

Falscher Weg

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

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

Batchverarbeitung von Updates

Um die Leistung zu verbessern, verarbeitet Scripts Build-Anforderungen in Batches. Wenn Sie die Operationsmethode einer Buildanforderung aufrufen, werden Skripts gezwungen, die Buildanforderungen in der Warteschlange sofort zu verarbeiten, wodurch Leistungsgewinne zunichte gemacht werden. Wenn du mehr als eine Entität erstellst, führe die Vorgangsmethoden nicht in derselben Schleife aus, die du zum Erstellen der Entität verwendest. Dies führt zu schlechter Leistung, da jeweils nur eine Entität verarbeitet wird. Erstellen Sie stattdessen ein Array der Vorgänge, und verarbeiten Sie sie nach der Buildschleife.

Richtiger Weg

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

Falscher Weg

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

Dasselbe gilt, wenn Sie eine Entität aktualisieren und dann dieselbe Eigenschaft erhalten, die Sie aktualisiert haben. Gehen Sie nicht so vor:

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

Verwenden Sie das Schlüsselwort (Schlüsselwort Keyword) für das Abrufen großer Mengen von Entitäten

Das Abrufen einer großen Anzahl von Entitäten und deren Laden in eine einzige Liste, die Sie in einer Schleife verarbeiten, hat einige Nachteile:

  1. Abhängig von der Größe der Anforderung kann es eine Anzahl von Back-End-Anforderungen dauern, bis alle Entitäten abgerufen sind, bevor die Schleife beginnt. Wenn du sie nicht alle bearbeitest, ist die Zeit und Rechenleistung, die benötigt werden, um die unverarbeiteten Entitäten zu erhalten, verschwendet. Wenn Sie z. B. 10.000 Keywords abrufen und die Schleife nach der Verarbeitung von nur 2.000 Keywords unterbrochen wird, wird die Zeit und Rechenleistung, die für die Generierung der verbleibenden 8.000 Keywords benötigt werden, verschwendet.

  2. Das Erstellen der Liste erfordert mehr Speicherplatz, um alle Entitäten gleichzeitig zu speichern.

Um diese Probleme zu beheben, verwenden Sie das Schlüsselwort yield(keyword), mit dem Ihr Skript Entitäten bei Bedarf abrufen kann, oder in gewisser Weise "träge" sie nur abruft, wenn sie benötigt werden. Dies bedeutet, dass Ihr Skript nicht mehr Aufrufe tätigt, als es im Moment benötigt, und keine großen Listen von Objekten weiterleitet.

Dieses Beispiel enthält die Protokollierung, um den Kontrollfluss bei Verwendung des Schlüsselworts (Schlüsselwort) "Ertrag" zu veranschaulichen.

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

Anrufmuster zur Vermeidung von Entitätslimits

Die Anzahl der Entitäten, die Skripts für ein Konto zurückgeben können, ist begrenzt. Wenn die Anforderung mehr als diesen Grenzwert zurückgeben würde, gibt Scripts einen Fehler mit der Meldung "Es gibt zu viele Entitäten" aus. Das folgende Beispiel zeigt das Aufrufmuster, das Sie verwenden sollten, wenn Sie eine große Anzahl von Entitäten abrufen. Im Beispiel wird versucht, alle Schlüsselwörter zuerst auf Kontoebene abzurufen. Wenn dies fehlschlägt, wird versucht, mehrere Aufrufe zum Abrufen von Schlüsselwörtern nach Kampagne durchzuführen, da es in der Regel weniger Entitäten auf Kampagnenebene gibt. Im Allgemeinen können Sie dieses Muster bei Bedarf bis auf Anzeigengruppenebene fortsetzen.

Informationen zur Verwendung des Schlüsselworts yield) finden Sie unter Verwenden des Schlüsselworts (Schlüsselwort (Schlüsselwort) yield beim Abrufen großer Mengen von Entitäten.

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