上傳指令碼

以指令碼為基礎的演算法是由指令碼使用基本 Python 語法定義。透過文字檔將指令碼上傳至 Display & Video 360。

如果使用以規則為主的演算法,請前往「上傳規則」頁面。

編寫腳本

以指令碼為基礎的自訂出價演算法會使用使用者提供的指令碼評估曝光價值,如需範例指令碼欄位清單,請參閱 Display & Video 360 說明中心

您可以在 Display & Video 360 使用者介面中執行指令碼,測試指令碼的成效。並提供抽樣曝光的分數分布情形。

將指令碼儲存至本機,並存為 TXT 檔案。

生成指令碼參照物件

參考物件用於將上傳的檔案與指令碼建立關聯。使用 uploadScript 要求,在演算法下產生參照物件。

以下說明如何產生指令碼參照物件:

Java

// Provide the ID of the advertiser that owns the algorithm.
long advertiserId = advertiser-id;

// Provide the ID of the algorithm.
long customBiddingAlgorithmId = algorithm-id;

// Generate the custom bidding script reference object.
CustomBiddingScriptRef response =
    service
        .customBiddingAlgorithms()
        .uploadScript(customBiddingAlgorithmId)
        .setAdvertiserId(advertiserId)
        .execute();

// Display the generated resource path.
System.out.printf(
    "The generated script reference object provided the following resource path: %s.",
    response.getResourceName());

Python

# Provide the ID of the advertiser that owns the algorithm.
advertiser_id = advertiser-id

# Provide the ID of the algorithm.
algorithm_id = algorithm-id

# Generate a script reference object under the algorithm.
custom_bidding_script_ref = (
    service.customBiddingAlgorithms()
    .uploadScript(
        customBiddingAlgorithmId=algorithm_id, advertiserId=advertiser_id
    )
    .execute()
)

# Print the resource path provided in the generated script reference object.
print(
    "The generated script reference object provided the following resource"
    " path: "
    f'{custom_bidding_script_ref["resourceName"]}.'
)

PHP

// Provide the ID of the advertiser that owns the algorithm.
$advertiserId = advertiser-id;

// Provide the ID of the algorithm.
$algorithmId = algorithm-id;

$uploadScriptOptParams = array(
    'advertiserId' => $advertiserId
);

try {
  // Retrieve a usable custom bidding script reference.
    $scriptRefResponse =
        $this->service->customBiddingAlgorithms->uploadScript(
            $customBiddingAlgorithmId,
            $uploadScriptOptParams
        );
} catch (\Exception $e) {
    $this->renderError($e);
    return;
}

// Print the retrieved resource path.
printf(
    '<p>The generated script reference object provided the following '
        . 'resource path: %s</p>',
    $scriptRefResponse->getResourceName()
);

上傳指令碼檔案

使用媒體 upload 要求,將指令碼檔案上傳至參照物件中的資源路徑。使用簡單上傳,並搭配查詢參數 uploadType=media

上傳指令碼檔案的方法如下:

Java

// Provide the local path to the script file to upload.
String scriptPath = script-path;

// Provide the resource path to upload the script file to.
String resourcePath = resource-path;

// Create media object.
GoogleBytestreamMedia media = new GoogleBytestreamMedia();
media.setResourceName(resourcePath);

// Create input stream for the script file.
InputStreamContent scriptFileStream =
    new InputStreamContent(null, new FileInputStream(scriptPath));

// Create media.upload request.
Media.Upload uploadRequest = service.media().upload(resourcePath, media, scriptFileStream);

// Retrieve uploader from the request and set it to us a simple upload
// request.
MediaHttpUploader uploader = uploadRequest.getMediaHttpUploader();
uploader.setDirectUploadEnabled(true);

// Execute the upload using an Upload URL with the destination resource
// name.
uploader.upload(
    new GenericUrl("https://displayvideo.googleapis.com/upload/media/" + resourcePath));

Python

# Import HTTP objects used for file upload.
from apiclient.http import HttpRequest
from apiclient.http import MediaFileUpload

# Provide the local path to the script file to upload.
script_path = script-path

# Provide the resource path to upload the script file to.
resource_path = resource-path

# Create a media upload object.
media = MediaFileUpload(script_path)

# Create upload request.
upload_request = service.media().upload(
    resourceName=resource_path, media_body=media
)

# Override response handler to expect null response.
upload_request.postproc = HttpRequest.null_postproc

# Upload script to given resource path.
upload_request.execute()

PHP

// Provide the local path to the script file to upload.
$scriptPath = script-path;

// Provide the resource path to upload the script file to.
$resourcePath = resource-path;

// Create the media body.
$mediaBody = new Google_Service_DisplayVideo_GoogleBytestreamMedia();
$mediaBody->setResourceName($resourceName);

// Build params array for the upload request.
$mediaUploadOptParams = array(
    'data' => file_get_contents($scriptPath),
    'uploadType' => 'media',
    'resourceName' => $resourceName
);

try {
    // Call the API, uploading the script file to Display & Video 360.
    $this->service->media->upload(
        $resourceName,
        $mediaBody,
        $mediaUploadOptParams
    );
} catch (\Exception $e) {
    $this->renderError($e);
}

建立指令碼物件

使用 create 要求,在演算法下建立指令碼物件。

如果指令碼檔案有語法錯誤,create 要求就會失敗。回覆內容會包含錯誤的詳細資料。修正指令碼,然後產生新的參照物件,重新啟動程序。

建立指令碼物件的方法如下:

Java

// Provide the ID of the advertiser that owns the parent algorithm.
long advertiserId = advertiser-id;

// Provide the ID of the parent algorithm.
long algorithmId = algorithm-id;

// Provide the resource path the script file was uploaded to.
String resourcePath = resource-path;

// Create the custom bidding script structure.
CustomBiddingScript customBiddingScript =
    new CustomBiddingScript()
        .setScript(new CustomBiddingScriptRef().setResourceName(resourcePath));

// Create the custom bidding script.
CustomBiddingScript response =
    service
        .customBiddingAlgorithms()
        .scripts()
        .create(customBiddingAlgorithmId, customBiddingScript)
        .setAdvertiserId(advertiserId)
        .execute();

// Print ID of new custom bidding script.
System.out.printf("Script was created with ID %s.", response.getCustomBiddingScriptId());

Python

# Provide the ID of the advertiser that owns the parent algorithm.
advertiser_id = advertiser-id

# Provide the ID of the parent algorithm.
algorithm_id = algorithm-id

# Provide the resource path the script file was uploaded to.
resource_path = resource-path

# Build script object.
script_obj = {"script": {"resourceName": resource_path}}

# Build and execute request.
script_response = (
    service.customBiddingAlgorithms()
    .scripts()
    .create(
        customBiddingAlgorithmId=algorithm_id,
        advertiserId=advertiser_id,
        body=script_obj,
    )
    .execute()
)

# Print ID of new custom bidding algorithm.
print(
    f'Script was created with ID {script_response["customBiddingScriptId"]}.'
)

PHP

// Provide the ID of the advertiser that owns the parent algorithm.
$advertiserId = advertiser-id;

// Provide the ID of the parent algorithm.
$algorithmId = algorithm-id;

// Provide the resource path the script file was uploaded to.
$resourcePath = resource-path;

// Build Script object.
$customBiddingScript = new Google_Service_DisplayVideo_CustomBiddingScript();
$scriptRef = new Google_Service_DisplayVideo_CustomBiddingScriptRef();
$scriptRef->setResourceName($resourcePath);
$customBiddingScript->setScript($scriptRef);

$createScriptOptParams = array(
    'advertiserId' => $advertiserId
);

// Call the API, creating the custom bidding script using the script
// file and under the advertiser and custom bidding algorithm given.
try {
    $result = $this->service->customBiddingAlgorithms_scripts->create(
        $customBiddingAlgorithmId,
        $algorithmId,
        $createScriptOptParams
    );
} catch (\Exception $e) {
    $this->renderError($e);
    return;
}

// Print ID of new custom bidding script.
printf('<p>Script was created with ID %s.</p>', $result['customBiddingScriptId']);

輪詢指令碼狀態

Display & Video 360 會在指令碼建立後進行處理。Display & Video 360 接著會接受或拒絕指令碼,並為曝光次數評分。

指令碼建立完成後,請輪詢指令碼,確認系統已接受。檢查結果 scriptstate 欄位:

  • 如果設為 PENDING,請繼續輪詢指令碼。
  • 如果 ACCEPTED,指令碼有效。請繼續檢查演算法是否已準備就緒
  • 如果為 REJECTED,則指令碼有錯誤。在 errors 欄位中擷取,即可取得腳本中發現的特定錯誤清單。請修正錯誤,然後產生新的指令碼參照物件,重新啟動這個程序,上傳修正後的指令碼檔案。

以下說明如何使用指數輪詢方式輪詢指令碼狀態:

Java

// Provide the ID of the advertiser that owns the parent algorithm.
long advertiserId = advertiser-id;

// Provide the ID of the parent algorithm.
long algorithmId = algorithm-id;

// Provide the ID of the script.
long scriptId = script-id;

// Configure the Operations.get request.
Scripts.Get scriptRequest =
    service
        .customBiddingAlgorithms()
        .scripts()
        .get(algorithmId, scriptId)
        .setAdvertiserId(advertiserId);

// Configure exponential backoff for checking the status of our operation.
ExponentialBackOff backOff =
    new ExponentialBackOff.Builder()
        .setInitialIntervalMillis(5000) // setting initial interval to five seconds
        .setMaxIntervalMillis(300000) // setting max interval to five minutes
        .setMaxElapsedTimeMillis(18000000) // setting max elapsed time to five hours
        .build();

long backoffMillis = 0;

CustomBiddingScript script = null;

do {
  // Sleep before retrieving operation again.
  Thread.sleep(backoffMillis);

  // Retrieve script.
  script = scriptRequest.execute();

  // If operation is not done, calculate next sleep period.
  if (script.getState() == null || script.getState().equals("PENDING")) {
    backoffMillis = backOff.nextBackOffMillis();
    if (backoffMillis == ExponentialBackOff.STOP) {
      System.out.printf("Polling deadline exceeded.%n");
      break;
    }
    System.out.printf(
        "Script with ID %s is still pending, sleeping for %s milliseconds.%n",
        scriptId, backoffMillis);
  }
} while (script.getState() == null || script.getState().equals("PENDING"));

// Check whether operation finished with an error, or is completed and ready to download.
if (script.getState().equals("ACCEPTED")) {
  System.out.printf("Script with ID %s has been accepted.%n", scriptId);
} else {
  System.out.printf("Script with ID %s has been rejected.%n", scriptId);
  if (script.getErrors() != null && script.getErrors().size() > 0) {
    System.out.println("The following errors were identified:");
    for (ScriptError error : script.getErrors()) {
      System.out.printf(
          "Line %s, Column %s (%s): %s%n",
          error.getLine(), error.getColumn(), error.getErrorCode(), error.getErrorMessage());
    }
  } else {
    System.out.println("No specific errors were identified.");
  }
}

Python

# Provide the ID of the advertiser that owns the parent algorithm.
advertiser_id = advertiser-id

# Provide the ID of the parent algorithm.
algorithm_id = algorithm-id

# Provide the ID of the script.
script_id = script-id

# Set the following values that control retry behavior while the script is
# being processed.
# Minimum amount of time between polling requests. Defaults to 5 seconds.
min_retry_interval = 5
# Maximum amount of time between polling requests. Defaults to 5 minutes.
max_retry_interval = 5 * 60
# Maximum amount of time to spend polling. Defaults to 5 hours.
max_retry_elapsed_time = 5 * 60 * 60

# Generate the next sleep interval using exponential backoff logic.
def next_sleep_interval(previous_sleep_interval):
    """Calculates the next sleep interval based on the previous."""
    min_interval = previous_sleep_interval or min_retry_interval
    max_interval = previous_sleep_interval * 3 or min_retry_interval
    return min(max_retry_interval, random.randint(min_interval, max_interval))

# Configure the script GET request.
get_request = (
    service.customBiddingAlgorithms()
    .scripts()
    .get(
        customBiddingAlgorithmId=algorithm_id,
        customBiddingScriptId=script_id,
        advertiserId=advertiser_id,
    )
)

# Poll the script until the script is finished being processed or the max
# time has fully elapsed.
sleep = 0
start_time = time.time()
while True:
  # Get current status of the script.
  script = get_request.execute()

  # Check whether script was accepted or rejected and print any relevant
  # errors.
  if script["state"] == "ACCEPTED":
    print(f"Script with ID {script_id} has been accepted.")
    break
  elif script["state"] == "REJECTED":
    print(f"Script with ID {script_id} has been rejected.")
    if "errors" in script and len(script["errors"]) > 0:
      print("The following errors were identified:")
      for err in script["errors"]:
        print(
            f'Line {err["line"]}, Column'
            f' {err["column"]} ({err["errorCode"]}): {err["errorMessage"]}'
        )
    else:
      print("No specific errors were identified.")
    break
  elif time.time() - start_time > max_retry_elapsed_time:
    print("Polling deadline exceeded.")
    break

  sleep = next_sleep_interval(sleep)
  print(
      f"Script with ID {script_id} is still pending, sleeping for"
      f" {sleep} seconds."
  )
  time.sleep(sleep)

PHP

// Provide the ID of the advertiser that owns the parent algorithm.
$advertiserId = advertiser-id;

// Provide the ID of the parent algorithm.
$algorithmId = algorithm-id;

// Provide the ID of the script.
$scriptId = script-id;

// The following values control retry behavior while the task is
// processing.
// Minimum amount of time between polling requests. Defaults to 5 seconds.
$minRetryInterval = 5;
// Maximum amount of time between polling requests. Defaults to 5 minutes.
$maxRetryInterval = 300;
// Maximum amount of time to spend polling. Defaults to 5 hours.
$maxRetryElapsedTime = 18000;

$sleep = 0;
$startTime = time();

$getScriptOptParams = array(
    'advertiserId' => $advertiserId
);

do {
    try {
        // Call the API, retrieving the script.
        $script = $this->service->customBiddingAlgorithms_scripts->get(
            $algorithmId,
            $scriptId,
            $getScriptOptParams
        );
    } catch (\Exception $e) {
        $this->renderError($e);
        return;
    }

    if ($script->getState() == "ACCEPTED") {
        printf("<p>Script with ID %s has been accepted.</p>", $scriptId);
        break;
    } elseif ($script->getState() == "REJECTED") {
        printf("Script with ID %s has been rejected.", $scriptId);
        if ($script->getErrors() !== null) {
            print("<p>The following errors were identified:</p><ul>");
            $errors = $script->getErrors();
            foreach ($errors as $error) {
                printf(
                    "<li>Line %s, Column %s (%s): %s</li>",
                    $error->getLine(),
                    $error->getColumn(),
                    $error->getErrorCode(),
                    $error->getErrorMessage());
            }
            print("</ul>");
        } else {
            print("<p>No specific errors were identified.</p>");
        }
        break;
    } elseif (time() - $startTime > $maxRetryElapsedTime) {
        print '<p>Script polling deadline exceeded.</p>';
        break;
    }

    // Generate next sleep interval.
    $minInterval = max($minRetryInterval, $sleep);
    $maxInterval = max(
        $minRetryInterval,
        $sleep * 3
    );
    $sleep = min($maxRetryInterval, rand($minInterval, $maxInterval));

    printf(
        '<p>Script with ID %s is still pending, sleeping for %d seconds</p>',
        $scriptId,
        $sleep
    );
    sleep($sleep);
} while (true);