แนวทางปฏิบัติสําหรับสคริปต์ Microsoft Advertising

เพื่อปรับปรุงประสิทธิภาพของสคริปต์ของคุณและของแพลตฟอร์ม โปรดตรวจสอบและปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดที่ระบุไว้ด้านล่าง

การทํางานกับตัวเลือก

ใช้ตัวกรอง

ใช้ตัวกรองของตัวเลือกแทนการกรองเอนทิตีด้วยตัวคุณเอง ตัวเลือกช่วยให้คุณกรองตาม ID และเงื่อนไขได้ ตัวอย่างเช่น คุณสามารถกรองตามประสิทธิภาพของเอนทิตี (แคมเปญผลตอบแทนที่มี CPC เฉลี่ยมากกว่า 10) สถานะของเอนทิตี (แคมเปญที่หยุดชั่วคราว) ชื่อของวัตถุแม่ของเอนทิตี และอื่นๆ

ประโยชน์ของการใช้ตัวกรอง:

  • จํากัดจํานวนเอนทิตีที่ ตัวเลือก ส่งคืนไปยังเอนทิตีที่คุณต้องการเท่านั้น

  • ให้สคริปต์ทํางานได้เร็วขึ้น (เอนทิตีที่จะส่งคืนและประมวลผลน้อยลง)

  • ลดโอกาสที่คุณจะพบกับขีดจํากัดการอ่านเอนทิตี (ดู ขีดจํากัดการดําเนินการสคริปต์)

วิธีที่ถูกต้อง

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

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

ทางที่ผิด

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

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

ไม่ตัดผ่านลําดับชั้นเอนทิตี

ถ้าคุณต้องการรับเอนทิตีลูกของเอนทิตีหรือเอนทิตีแม่ของเอนทิตี อย่าเลื่อนผ่านลําดับชั้นของเอนทิตีเพื่อรับรายการเหล่านั้น

เมื่อต้องการรับเอนทิตีลูก ให้ใช้คอลเลกชันของเอนทิตีลูกในระดับที่คุณต้องการ

วิธีที่ถูกต้อง

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

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

หรือหากคุณต้องการโฆษณาจากแคมเปญเฉพาะ ให้ทําดังนี้

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

หรือรับโฆษณาของแคมเปญ ถ้าคุณมีวัตถุแคมเปญ:

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

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

ทางที่ผิด

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

เช่นเดียวกันหากคุณต้องการรับรายการแม่ของเอนทิตี แทนที่จะเคลื่อนผ่านลําดับชั้นเพื่อรับไซต์แม่ ให้ใช้เมธอดตัวเข้าใช้งานหลักของเอนทิตีลูก

วิธีที่ถูกต้อง

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

ทางที่ผิด

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

ใช้รหัสเอนทิตีเมื่อเป็นไปได้

การใช้ ID เพื่อกรองเอนทิตีให้ประสิทธิภาพที่ดีที่สุด

นี้

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

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

ให้ประสิทธิภาพที่ดีกว่านี้

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

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

หลีกเลี่ยงการวนรอบที่แน่นหนาด้วยตัวเลือกและจํานวน GET ที่ไม่จําเป็น

หลีกเลี่ยงการวนซ้ําด้วยคําขอ get ที่ได้รับเอนทิตีเดียว ตัวอย่างเช่น สมมติว่าคุณเรียกใช้รายงานประสิทธิภาพของคําสําคัญและต้องการอัปเดตคําสําคัญในรายงาน แทนที่จะรับแถวจากรายงาน รับคําสําคัญ แล้วอัปเดตแถวนั้น คุณควรสร้างรายการของ ID คําสําคัญขณะที่คุณวนรอบแต่ละแถวในรายงาน จากนั้น ส่งผ่าน ID ไปยังตัวเลือกเพื่อรับคําสําคัญทั้งหมดในคําขอรับครั้งเดียว จากนั้น คุณสามารถคํานวณซ้ําผ่านรายการคําสําคัญและอัปเดตคําสําคัญเหล่านั้นได้

วิธีที่ถูกต้อง

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

ทางที่ผิด

    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        
    }

ใส่เมธอด forDateRange ก็ต่อเมื่อคุณวางแผนที่จะเรียกใช้เมธอด getStats ของเอนทิตีเท่านั้น

การเรียกวิธีการของ forDateRange ตัวเลือกทําให้ตัวเลือกได้รับข้อมูลประสิทธิภาพของเอนทิตี การรับข้อมูลประสิทธิภาพของเอนทิตีมีค่าใช้จ่ายสูง ดังนั้นรับก็ต่อเมื่อคุณวางแผนที่จะเรียกใช้เมธอดของ getStats เอนทิตีและใช้ข้อมูลเท่านั้น

ช่วงวันที่ที่คุณระบุสําหรับเอนทิตีจะไม่นําไปใช้กับเอนทิตีหลักหรือลูกที่คุณเข้าถึงจากเอนทิตีนั้น ตัวอย่างเช่น ถ้าคุณได้รับกลุ่มโฆษณา จากนั้นได้รับแคมเปญหลัก และพยายามเข้าถึงเมตริกประสิทธิภาพของแคมเปญ การโทรจะล้มเหลว

campaignStats.getReturnOnAdSpend()การโทรในตัวอย่างต่อไปนี้ล้มเหลวเนื่องจากช่วงวันที่ใช้กับกลุ่มโฆษณา ไม่ใช่แคมเปญ

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

เพื่อให้ใช้งานได้ คุณต้องสร้างตัวเลือกสําหรับแคมเปญ

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

Don't change an entities 's property that is used as a condition in the selector

Iterator จะลดแรงกดของหน่วยความจําด้วยการโหลดครั้งละรายการเดียวแทนที่จะโหลดรายการทั้งชุด ด้วยเหตุนี้ การเปลี่ยนแปลงคุณสมบัติที่คุณใช้เป็นเงื่อนไขในตัวเลือกอาจทําให้เกิดลักษณะการทํางานที่ไม่คาดคิดได้

วิธีที่ถูกต้อง

    var adGroups = []; 

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

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

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

ทางที่ผิด

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

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

การอัปเดตเป็นชุด

เพื่อปรับปรุงประสิทธิภาพการทํางาน สคริปต์จะประมวลผลคําขอรุ่นเป็นชุด ถ้าคุณเรียกใช้วิธีการดําเนินการของคําขอรุ่น ระบบจะบังคับให้สคริปต์ประมวลผลคําขอรุ่นที่จัดคิวทันที ซึ่งลบล้างการเพิ่มประสิทธิภาพการทํางาน ถ้าคุณกําลังสร้างเอนทิตีมากกว่าหนึ่งรายการ อย่าเรียกใช้วิธีการดําเนินการในลูปเดียวกันกับที่คุณใช้เพื่อสร้างเอนทิตี ซึ่งนําไปสู่ประสิทธิภาพการทํางานที่ไม่ดีเนื่องจากมีการประมวลผลเพียงหนึ่งเอนทิตีในแต่ละครั้ง แต่ให้สร้างอาร์เรย์ของการดําเนินการและประมวลผลหลังจากการสร้างวนซ้ํา

วิธีที่ถูกต้อง

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

ทางที่ผิด

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

เช่นเดียวกันถ้าคุณอัปเดตเอนทิตี แล้วรับคุณสมบัติเดียวกันกับที่คุณอัปเดต อย่าทําเช่นนี้:

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

ใช้คําสําคัญ yield เมื่อรับชุดเอนทิตีขนาดใหญ่

การดึงข้อมูลเอนทิตีจํานวนมากและโหลดไปยังรายการเดียวที่คุณประมวลผลในลูปมีข้อเสียอยู่สองสามประการ:

  1. อาจต้องใช้คําขอแบ็คเอนด์จํานวน n ครั้ง ในการดึงข้อมูลเอนทิตีทั้งหมดก่อนที่การวนรอบจะเริ่มต้นขึ้น ทั้งนี้ขึ้นอยู่กับขนาดของคําขอ หากคุณไม่ประมวลผลทั้งหมดเวลาและพลังการประมวลผลที่ใช้ในการรับเอนทิตีที่ยังไม่ได้ประมวลผลจะสูญเปล่า ตัวอย่างเช่น หากคุณดึงข้อมูลคําสําคัญ 10,000 คําและวนซ้ําหยุดทํางานหลังจากประมวลผลคําสําคัญเพียง 2,000 คํา เวลาและพลังการประมวลผลที่ใช้เพื่อรับคําสําคัญ 8,000 คําที่เหลือจะสูญเปล่า

  2. การสร้างรายการต้องใช้หน่วยความจําเพิ่มเติมเพื่อเก็บเอนทิตีทั้งหมดในเวลาเดียวกัน

เมื่อต้องการแก้ไขปัญหาเหล่านี้ ให้ใช้คําสําคัญ yield ซึ่งอนุญาตให้สคริปต์ของคุณดึงข้อมูลเอนทิตีได้ตามต้องการ หรือในแง่หนึ่ง "ขี้เกียจ" ดึงข้อมูลเฉพาะเมื่อจําเป็นเท่านั้น ซึ่งหมายความว่าสคริปต์ของคุณไม่ได้เรียกมากเกินความจําเป็นในขณะนี้ และไม่ส่งผ่านรายการวัตถุขนาดใหญ่

ตัวอย่างนี้มีการบันทึกเพื่อแสดงขั้นตอนการควบคุมเมื่อใช้คําสําคัญ 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");
    }
}

รูปแบบการโทรเพื่อหลีกเลี่ยงขีดจํากัดเอนทิตี

มีขีดจํากัดจํานวนเอนทิตีที่สคริปต์สามารถส่งคืนสําหรับบัญชี ถ้าคําขอจะส่งกลับมากกว่าขีดจํากัดนี้ สคริปต์จะส่งกลับข้อผิดพลาดพร้อมกับข้อความว่า มีเอนทิตีมากเกินไป ตัวอย่างต่อไปนี้แสดงรูปแบบการเรียกที่คุณควรใช้เมื่อได้รับเอนทิตีจํานวนมาก ตัวอย่างจะพยายามดึงข้อมูลคําสําคัญทั้งหมดที่ระดับบัญชีก่อน หากล้มเหลว ระบบจะพยายามเรียกหลายครั้งเพื่อดึงข้อมูลคําสําคัญตามแคมเปญ เนื่องจากโดยทั่วไปแล้วจะมีเอนทิตีที่ระดับแคมเปญน้อยกว่า โดยทั่วไป คุณสามารถดําเนินการรูปแบบนี้ต่อไปจนถึงระดับกลุ่มโฆษณาได้ หากจําเป็น

สําหรับข้อมูลเกี่ยวกับการใช้คําสําคัญ yield ให้ดู ใช้คําสําคัญ yield เมื่อรับชุดของเอนทิตีขนาดใหญ่

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