上传脚本

基于脚本的算法由使用基本 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 会接受脚本以对展示机会进行评分,或者拒绝脚本。

创建脚本后,请轮询该脚本以确保其被接受。检查生成的脚本state 字段:

  • 如果为 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);