Kampagnenentwürfe und -tests

Haben Sie sich jemals gefragt: "Würde ich mehr Conversions erhalten, wenn ich die Zielseite meiner Anzeigen ändern würde?" oder "Was passiert, wenn ich den Anzeigentext anders formuliere? Würde das die Anzahl der Zugriffe erhöhen?" Wenn Sie einen Test ausarbeiten müssten, um diese Fragen zu klären, wäre das sehr aufwendig. Mit Kampagnenentwürfen und -tests entfallen jedoch die mühseligen Arbeiten wie Kopieren von Daten, Konfiguration usw.

Mit den Diensten DraftService und TrialService lassen sich neue Kampagnentests schnell und einfach einrichten. Dazu sind die folgenden Schritte erforderlich:

  1. Erstellen Sie einen Entwurf aus einer vorhandenen Basiskampagne. Ein Entwurf ist das Spiegelbild einer vorhandenen Kampagne. Anzeigen werden jedoch nicht ausgeliefert.
  2. Ändern Sie den Kampagnenentwurf entsprechend den Anforderungen Ihres Tests.
  3. Erstellen Sie einen Test aus dem Entwurf. Mit dem Test erhalten Sie Zugriff auf eine neue Testkampagne, die als Kopie des Kampagnenentwurfs beginnt und Zugriffe zwischen der Testkampagne und der Basiskampagne aufteilt. Diese Testkampagnen werden auch nur als "Tests" bezeichnet.
  4. Vergleichen Sie die Statistiken Ihrer Testkampagne mit Ihrer Basiskampagne, um zu sehen, welche Kampagne zu besseren Ergebnissen führt.

Diese Schritte stellen den häufigsten Arbeitsablauf dar, doch sind DraftService und TrialService sehr flexibel und lassen sich auf andere Weise verwenden. So können Sie einen Entwurf beispielsweise verwenden, um Änderungen an einer Basiskampagne bereitzustellen und anschließend wieder in die Basiskampagne zu integrieren, ohne jemals einen Test zu verwenden. Or, if you do use a trial and like the performance, you have the option of promoting its attributes back to the base campaign or graduating it into its own full‒fledged campaign alongside the base campaign.

Dieses Flussdiagramm zeigt die Workflows, die mit Kampagnenentwürfen und -tests möglich sind:

Entwürfe

Ein Entwurf ist ein Objekt, das die Verknüpfung zwischen einem Kampagnenentwurf und einer Basiskampagne verwaltet. Sie erstellen einen Entwurf mit dem Dienst DraftService, indem Sie die ID einer vorhandenen Kampagne angeben, die als Basis dienen soll. Vom DraftService wird automatisch ein neuer Kampagnenentwurf erstellt und mit dem Entwurf verknüpft. Ein Entwurfsobjekt als solches ist nicht der Kampagnenentwurf; es bringt lediglich einen Kampagnenentwurf mit seiner Basiskampagne in Beziehung. Der Kampagnenentwurf existiert als eigenes Kampagnenobjekt. Die campaignId des Kampagnenentwurfs ist als draftCampaignId verfügbar. Sie können Kampagnenentwürfe wie eine echte Kampagne bearbeiten, also beispielsweise Kriterien, Anzeigengruppen, Gebote und Anzeigen ändern. Ein Kampagnenentwurf liefert jedoch keine Anzeigen aus.

Damit eine vorhandene Kampagne als Basis für einen Entwurf dienen kann, muss sie bestimmte Voraussetzungen erfüllen. Es muss sich um eine Kampagne vom Typ "Suchnetzwerk" oder "Suchnetzwerk mit Displayauswahl" handeln und sie darf kein gemeinsames Budget verwenden (isExplicitlyShared des Kampagnenbudgets muss "false" sein). Tests unterstützen die meisten Funktionen von Kampagnen. Es gibt aber auch Ausnahmen.

Entwurf erstellen

Legen Sie zum Erstellen eines Entwurfs eine baseCampaignId fest und geben Sie dem Entwurf einen Namen. Der Name darf bei den Entwürfen in Ihrem Konto nur einmal vorkommen. Hier ist ein Beispiel in Ruby:

Java

// Get the DraftService.
DraftServiceInterface draftService = adWordsServices.get(session, DraftServiceInterface.class);
Draft draft = new Draft();
draft.setBaseCampaignId(baseCampaignId);
draft.setDraftName("Test Draft #" + System.currentTimeMillis());

DraftOperation draftOperation = new DraftOperation();
draftOperation.setOperator(Operator.ADD);
draftOperation.setOperand(draft);

draft = draftService.mutate(new DraftOperation[] {draftOperation}).getValue(0);

VB

Dim draft As New Draft()
draft.baseCampaignId = baseCampaignId
draft.draftName = "Test Draft #" + ExampleUtilities.GetRandomString()

Dim draftOperation As New DraftOperation()
draftOperation.operator = [Operator].ADD
draftOperation.operand = draft

C#

Draft draft = new Draft() {
  baseCampaignId = baseCampaignId,
  draftName = "Test Draft #" + ExampleUtilities.GetRandomString()
};

DraftOperation draftOperation = new DraftOperation() {
  @operator = Operator.ADD,
  operand = draft
};

PHP

// Get the DraftService, which loads the required classes.
$draftService = $user->GetService('DraftService', ADWORDS_VERSION);

// Create a draft.
$draft = new Draft();
$draft->baseCampaignId = $baseCampaignId;
$draft->draftName = 'Test Draft #' . uniqid();

// Create an operation.
$operation = new DraftOperation();
$operation->operand = $draft;
$operation->operator = 'ADD';
$operations[] = $operation;

// Make the mutate request.
$result = $draftService->mutate($operations);
$draft = $result->value[0];

Perl

my $draft = Google::Ads::AdWords::v201609::Draft->new({
    baseCampaignId => $base_campaign_id,
    draftName      => sprintf("Test Draft #%s", uniqid())});

# Create operation.
my $draft_operation = Google::Ads::AdWords::v201609::DraftOperation->new({
    operator => "ADD",
    operand  => $draft
});

Python

draft_service = client.GetService('DraftService', version='v201609')

draft = {
    'baseCampaignId': base_campaign_id,
    'draftName': 'Test Draft #%s' % uuid.uuid4()
}

draft_operation = {'operator': 'ADD', 'operand': draft}
draft = draft_service.mutate([draft_operation])['value'][0]
draft_campaign_id = draft['draftCampaignId']

Ruby

draft_srv = adwords.service(:DraftService, API_VERSION)

draft = {
  :base_campaign_id => base_campaign_id,
  :draft_name => 'Test Draft #%d' % (Time.new.to_f * 1000).to_i
}
draft_operation = {:operator => 'ADD', :operand => draft}

draft_result = draft_srv.mutate([draft_operation])

draft = draft_result[:value].first
draft_id = draft[:draft_id]
draft_campaign_id = draft[:draft_campaign_id]

Der Entwurf enthält Details wie draftCampaignId, draftId und baseCampaignId. Diese sind gleichermaßen wichtig. draftCampaignId wird verwendet, um den Kampagnenentwurf und seine Anzeigengruppen, Kriterien und Anzeigen zu ändern. draftId und baseCampaignId dienen als Referenz für den Entwurf, wenn ein Test erstellt oder der Entwurf übernommen werden soll.

Kampagnenentwurf anpassen

Das Feld draftCampaignId kann von jedem Dienst, der eine Kampagnen-ID benötigt, als echte Kampagnen-ID verwendet werden (CampaignService, AdGroupService, CampaignCriterionService usw.). Um beispielsweise der Kampagne ein neues Sprachkriterium hinzuzufügen, verwenden Sie einfach die aus dem Entwurf abgerufene draftCampaignId als campaignId:

Java

CampaignCriterionServiceInterface campaignCriterionService =
    adWordsServices.get(session, CampaignCriterionServiceInterface.class);

Language language = new Language();
language.setId(1003L); // Spanish

// Make sure to use the draftCampaignId when modifying the virtual draft campaign.
CampaignCriterion campaignCriterion = new CampaignCriterion();
campaignCriterion.setCampaignId(draft.getDraftCampaignId());
campaignCriterion.setCriterion(language);

CampaignCriterionOperation criterionOperation = new CampaignCriterionOperation();
criterionOperation.setOperator(Operator.ADD);
criterionOperation.setOperand(campaignCriterion);

campaignCriterion =
    campaignCriterionService
        .mutate(new CampaignCriterionOperation[] {criterionOperation})
        .getValue(0);

VB

campaign_criterion_srv =
    adwords.service(:CampaignCriterionService, API_VERSION)

criterion = {
  :xsi_type => 'Language',
  :id => 1003 # Spanish
}

criterion_operation = {
  # Make sure to use the draft_campaign_id when modifying the virtual draft
  # campaign.
  :operator => 'ADD',
  :operand => {
    :campaign_id => draft_campaign_id,
    :criterion => criterion
  }
}

criterion_result = campaign_criterion_srv.mutate([criterion_operation])

C#

Language language = new Language() {
  id = 1003L // Spanish
};

// Make sure to use the draftCampaignId when modifying the virtual draft
// campaign.
CampaignCriterion campaignCriterion = new CampaignCriterion() {
  campaignId = draft.draftCampaignId,
  criterion = language
};

CampaignCriterionOperation criterionOperation = new CampaignCriterionOperation() {
  @operator = Operator.ADD,
  operand = campaignCriterion
};

campaignCriterion = campaignCriterionService.mutate(
    new CampaignCriterionOperation[] {criterionOperation}).value[0];

PHP

$campaignCriterionService =
    $user->GetService('CampaignCriterionService', ADWORDS_VERSION);

// Create a criterion.
$language = new Language();
$language->id = 1003; // Spanish
$campaignCriterion = new CampaignCriterion();
$campaignCriterion->campaignId = $draft->draftCampaignId;
$campaignCriterion->criterion = $language;

// Create an operation.
$operations = array();
$operation = new CampaignCriterionOperation();
$operation->operand = $campaignCriterion;
$operation->operator = 'ADD';
$operations[] = $operation;

// Make the mutate request.
$result = $campaignCriterionService->mutate($operations);
$campaignCriterion = $result->value[0];

Perl

my $criterion = Google::Ads::AdWords::v201609::Language->new({
    id => 1003    # Spanish
});

my $operation =
  Google::Ads::AdWords::v201609::CampaignCriterionOperation->new({
    operator => "ADD",
    operand  => Google::Ads::AdWords::v201609::CampaignCriterion->new({
        campaignId => $draft_campaign_id,
        criterion  => $criterion
      })});

$result =
  $client->CampaignCriterionService()->mutate({operations => [$operation]});

$criterion = $result->get_value()->[0];

Python

campaign_criterion_service = client.GetService('CampaignCriterionService',
                                               version='v201609')

criterion = {
    'xsi_type': 'Language',
    'id': 1003  # Spanish
}

criterion_operation = {
    # Make sure to use the draftCampaignId when modifying the virtual draft
    # campaign.
    'operator': 'ADD',
    'operand': {
        'campaignId': draft_campaign_id,
        'criterion': criterion
    }
}

criterion = campaign_criterion_service.mutate([criterion_operation])[
    'value'][0]

Ruby

campaign_criterion_srv =
    adwords.service(:CampaignCriterionService, API_VERSION)

criterion = {
  :xsi_type => 'Language',
  :id => 1003 # Spanish
}

criterion_operation = {
  # Make sure to use the draft_campaign_id when modifying the virtual draft
  # campaign.
  :operator => 'ADD',
  :operand => {
    :campaign_id => draft_campaign_id,
    :criterion => criterion
  }
}

criterion_result = campaign_criterion_srv.mutate([criterion_operation])

Ähnliche Vorgänge sind für Anzeigengruppen im Kampagnenentwurf möglich. Anzeigengruppen in einem Kampagnenentwurf können Sie beispielsweise folgendermaßen mit einem Filter abrufen:

Java

// Get the AdGroupService.
AdGroupServiceInterface adGroupService =
    adWordsServices.get(session, AdGroupServiceInterface.class);

// Create a selector that limits to ad groups in the draft campaign.
Selector selector =
    new SelectorBuilder()
        .fields(AdGroupField.Id)
        .equals(AdGroupField.CampaignId, Long.toString(draftCampaignId))
        .limit(100)
        .build();

// Make a 'get' request.
AdGroupPage adGroupPage = adGroupService.get(selector);

// Display the results.
if (adGroupPage.getEntries() != null && adGroupPage.getEntries().length > 0) {
  System.out.printf(
      "Found %d of %d ad groups.%n",
      adGroupPage.getEntries().length, adGroupPage.getTotalNumEntries());
} else {
  System.out.println("No ad groups found.");
}

VB

' Get the AdGroupService.
Dim adGroupService As AdGroupService = CType(user.GetService( _
    AdWordsService.v201609.AdGroupService), AdGroupService)

' Create a selector that limits to ad groups in the draft campaign.
Dim selector As New Selector
selector.fields = New String() {
  AdGroup.Fields.Id
}
selector.predicates = New Predicate() {
  Predicate.Equals(AdGroup.Fields.CampaignId, draftCampaignId)
}
selector.paging = Paging.Default

Dim page As AdGroupPage = adGroupService.get(selector)

' Display the results.
If ((Not page Is Nothing) AndAlso (Not page.entries Is Nothing)) Then
  Console.WriteLine("Fetched {0} of {1} ad groups.", page.entries.Length,
      page.totalNumEntries)
End If

PHP

// Get the AdGroupService.
$adGroupService = $user->GetService('AdGroupService', ADWORDS_VERSION);

// Create selector.
$selector = new Selector();
$selector->fields = array('Id');

// Create predicates.
$selector->predicates[] =
    new Predicate('CampaignId', 'EQUALS', $draftCampaignId);

// Create paging controls.
$selector->paging = new Paging(0, AdWordsConstants::RECOMMENDED_PAGE_SIZE);

// Make the get request.
$page = $adGroupService->get($selector);

// Display results.
if ($page->totalNumEntries !== null && $page->totalNumEntries > 0) {
  printf("Found %d ad groups.\n", $page->totalNumEntries);
} else {
  print "No ad groups were found.\n";
}

Perl

# Create predicates.
my $campaign_predicate = Google::Ads::AdWords::v201609::Predicate->new({
    field    => "CampaignId",
    operator => "EQUALS",
    values   => [$draft_campaign_id]});

# Create selector.
my $paging = Google::Ads::AdWords::v201609::Paging->new({
  startIndex    => 0,
  numberResults => PAGE_SIZE
});
my $selector = Google::Ads::AdWords::v201609::Selector->new({
  fields     => ["Id"],
  predicates => [$campaign_predicate],
  paging     => $paging
});

my $ad_group_page = $client->AdGroupService()->get({
  serviceSelector => $selector
});
if ($ad_group_page->get_entries()) {
  printf(
    "Found %d of %d ad groups.\n",
    scalar(@{$ad_group_page->get_entries()}),
    $ad_group_page->get_totalNumEntries());
} else {
  printf("No ad groups found.\n");
}

Python

ad_group_service = client.GetService('AdGroupService', version='v201609')

selector = {
    'fields': ['Id'],
    'paging': {
        'startIndex': str(0),
        'numberResults': str(PAGE_SIZE)
    },
    'predicates': [{
        'field': 'CampaignId',
        'operator': 'IN',
        'values': [draft_campaign_id]
    }]
}

response = ad_group_service.get(selector)
draft_ad_groups = response['entries'] if 'entries' in response else []

Ruby

ad_group_srv = adwords.service(:AdGroupService, API_VERSION)

selector = {
  :fields => ['Id'],
  :predicates => [{
    :field => 'CampaignId',
    :operator => 'IN',
    :values => [draft_campaign_id]
  }],
  :paging => {
    :start_index => 0,
    :number_results => 100
  }
}

ad_group_page = ad_group_srv.get(selector)

unless ad_group_page[:entries].nil?
  puts "Found %d of %d ad groups." %
      [ad_group_page[:entries].size, ad_group_page[:total_num_entries]]
else
  puts "No ad groups found."
end

Richtlinienprüfungen von Anzeigen erfolgen bei Kampagnenentwürfen wie bei normalen Kampagnen. Richtlinienverstöße, die erst bei der Auslieferung einer Anzeige auftreten, stammen nicht von hinzugefügten Entwurfsanzeigen. Sie führen jedoch in dem Augenblick zu einem Fehler, in dem Sie einen Test dieses Entwurfs durchführen.

Nachdem Sie den Kampagnenentwurf geändert haben, bieten sich Ihnen zwei Möglichkeiten:

  1. Übernehmen Sie die Änderungen am Kampagnenentwurf direkt in die Basiskampagne.
  2. Erstellen Sie eine Testkampagne, die gleichzeitig mit der Basiskampagne ausgeführt wird.

Kampagnenentwurf übernehmen

Sie können Entwürfe lediglich als Bereitstellungsmöglichkeit für ausstehende Änderungen an Ihren echten Kampagnen verwenden, ohne jemals einen Test zu erstellen. Wenn alle Änderungen am Kampagnenentwurf bereitgestellt sind, können Sie den Entwurf einfach übernehmen. Dann werden diese Änderungen in die Basiskampagne übernommen. Senden Sie dazu einen mutate-Vorgang, der ihren Status auf PROMOTING festlegt. Weil dabei alle Änderungen aus dem Kampagnenentwurf in die Basiskampagne kopiert werden müssen, erfolgt die Übernahme asynchron. Sie kann nicht rückgängig gemacht werden. Unmittelbar nach Festlegen des Status beginnt der asynchrone Prozess.

Sie können die draftId und baseCampaignId des Entwurfs verwenden, um den Status abzufragen und zu überwachen. Wenn der Status nicht mehr PROMOTING ist, ist der Vorgang abgeschlossen. PROMOTED zeigt Erfolg an, PROMOTE_FAILED zeigt an, dass ein Fehler aufgetreten ist. Weitere Informationen zu Abrufschemata finden Sie im Abschnitt Tests.

Wenn bei der Übernahme des Entwurfs ein Fehler aufgetreten ist, erhalten Sie Details zu den jeweiligen Fehlern, indem Sie draftId und baseCampaignId an DraftAsyncErrorService übergeben. Im Abschnitt Fehler finden Sie ein Beispiel.

Kampagnenentwürfe sind leicht zu erkennen, weil ihr campaignTrialType den Wert DRAFT hat. Sie können auch die Basiskampagnen-ID aus einem Kampagnenentwurf abrufen. Verwenden Sie dazu das Feld baseCampaignId. Bei normalen Kampagnen (die also weder aus einem Entwurf noch aus einem Test erstellt wurden) hat campaignTrialType den Wert BASE und das Feld baseCampaignId ist die eigene ID der Kampagne.

Entwurfselemente sind standardmäßig nicht in get-Ergebnissen von anderen Diensten wie CampaignService oder AdGroupService enthalten. Um ein Entwurfselement wie einen Kampagnenentwurf oder eine Entwurfsanzeigengruppe abzurufen, müssen Sie entweder in den Prädikaten campaignTrialType explizit als DRAFT angeben oder nach der ID eines bekannten Entwurfselements wie draftCampaignId filtern.

Tests

Ein Test ist ein Element, das die Verwaltung einer Testkampagne unterstützt, die parallel zu einer Basiskampagne ausgeführt wird. Dabei wird ein Teil der Zugriffe und des Budgets verwendet, um die im Entwurf konfigurierten Einstellungen zu testen. Bei dem Test entsteht eine echte Testkampagne (auch "Test" genannt), die Anzeigen wie eine normale Kampagne ausliefert. Er erfasst Statistiken getrennt von Ihrer Basiskampagne. Allerdings wird ein Test auf Ihre Kontolimits bezüglich der Anzahl gleichzeitiger Kampagnen, Anzeigengruppen usw. angerechnet.

Test erstellen

Geben Sie beim Erstellen eines Tests die draftId und baseCampaignId an, um den Entwurf, mit dem Sie den Test starten, eindeutig zu bezeichnen. Geben Sie außerdem einen eindeutigen Namen und den Prozentsatz der Zugriffe an, die vom Test verwendet werden sollen. Beim Erstellen eines Tests wird automatisch eine neu verknüpfte Testkampagne erstellt.

Tests werden im Gegensatz zu Entwürfen durch ihre ID eindeutig identifiziert. Sobald also ein Test erstellt ist, benötigen Sie zum Verweis auf den Test die baseCampaignId nicht mehr.

Java

// Get the TrialService.
TrialServiceInterface trialService = adWordsServices.get(session, TrialServiceInterface.class);

Trial trial = new Trial();
trial.setDraftId(draftId);
trial.setBaseCampaignId(baseCampaignId);
trial.setName("Test Trial #" + System.currentTimeMillis());
trial.setTrafficSplitPercent(50);

TrialOperation trialOperation = new TrialOperation();
trialOperation.setOperator(Operator.ADD);
trialOperation.setOperand(trial);

long trialId = trialService.mutate(new TrialOperation[] {trialOperation}).getValue(0).getId();

VB

Dim newTrial As New Trial
newTrial.draftId = draftId
newTrial.baseCampaignId = baseCampaignId
newTrial.name = "Test Trial #" & ExampleUtilities.GetRandomString()
newTrial.trafficSplitPercent = 50

Dim trialOperation As New TrialOperation()
trialOperation.operator = [Operator].ADD
trialOperation.operand = newTrial

C#

Trial trial = new Trial() {
  draftId = draftId,
  baseCampaignId = baseCampaignId,
  name = "Test Trial #" + ExampleUtilities.GetRandomString(),
  trafficSplitPercent = 50
};

TrialOperation trialOperation = new TrialOperation() {
  @operator = Operator.ADD,
  operand = trial
};

PHP

// Get the TrialService, which loads the required classes.
$trialService = $user->GetService('TrialService', ADWORDS_VERSION);
$trialAsynErrorService =
    $user->GetService('TrialAsyncErrorService', ADWORDS_VERSION);

// Create a trial.
$trial = new Trial();
$trial->draftId = $draftId;
$trial->baseCampaignId = $baseCampaignId;
$trial->name = 'Test Trial #' . uniqid();
$trial->trafficSplitPercent = 50;

// Create an operation.
$operation = new TrialOperation();
$operation->operand = $trial;
$operation->operator = 'ADD';
$operations[] = $operation;

// Make the mutate request.
$result = $trialService->mutate($operations);
$trial = $result->value[0];

Perl

my $trial = Google::Ads::AdWords::v201609::Trial->new({
    draftId             => $draft_id,
    baseCampaignId      => $base_campaign_id,
    name                => sprintf("Test Trial #%s", uniqid()),
    trafficSplitPercent => 50,
});

# Create operation.
my $trial_operation = Google::Ads::AdWords::v201609::TrialOperation->new({
    operator => "ADD",
    operand  => $trial
});

# Add trial.
my $result =
  $client->TrialService()->mutate({operations => [$trial_operation]});

Python

trial_service = client.GetService('TrialService', version='v201609')
trial_async_error_service = client.GetService('TrialAsyncErrorService',
                                              version='v201609')

trial = {
    'draftId': draft_id,
    'baseCampaignId': base_campaign_id,
    'name': 'Test Trial #%d' % uuid.uuid4(),
    'trafficSplitPercent': 50
}

trial_operation = {'operator': 'ADD', 'operand': trial}
trial_id = trial_service.mutate([trial_operation])['value'][0]['id']

Ruby

trial_srv = adwords.service(:TrialService, API_VERSION)
trial_async_error_srv = adwords.service(:TrialAsyncErrorService, API_VERSION)

trial = {
  :draft_id => draft_id,
  :base_campaign_id => base_campaign_id,
  :name => 'Test Trial #%d' % (Time.new.to_f * 1000).to_i,
  :traffic_split_percent => 50
}
trial_operation = {:operator => 'ADD', :operand => trial}

trial_result = trial_srv.mutate([trial_operation])

trial_id = trial_result[:value].first[:id]

Notieren Sie wie beim Erstellen eines Entwurfs den Wert von trialCampaignId zur späteren Verwendung.

Das Erstellen eines Tests ist im Gegensatz zum Erstellen eines Entwurfs ein asynchroner Vorgang. Ein neuer Test erhält den Status CREATING. Bevor Sie fortfahren, müssen Sie daher immer wieder den Status abrufen, bis sich dieser geändert hat:

Java

Selector trialSelector =
    new SelectorBuilder()
        .fields(
            TrialField.Id,
            TrialField.Status,
            TrialField.BaseCampaignId,
            TrialField.TrialCampaignId)
        .equalsId(trialId)
        .build();

trial = null;
boolean isPending = true;
int pollAttempts = 0;
do {
  long sleepSeconds = (long) Math.scalb(30d, pollAttempts);
  System.out.printf("Sleeping for %d seconds.%n", sleepSeconds);
  Thread.sleep(sleepSeconds * 1000);
  trial = trialService.get(trialSelector).getEntries(0);

  System.out.printf("Trial ID %d has status '%s'.%n", trial.getId(), trial.getStatus());
  pollAttempts++;
  isPending = TrialStatus.CREATING.equals(trial.getStatus());
} while (isPending && pollAttempts < MAX_POLL_ATTEMPTS);

VB

' Since creating a trial is asynchronous, we have to poll it to wait
' for it to finish.
Dim trialSelector As New Selector()
trialSelector.fields = New string() {
    Trial.Fields.Id, Trial.Fields.Status, Trial.Fields.BaseCampaignId,
    Trial.Fields.TrialCampaignId
}
trialSelector.predicates = New Predicate() {
    Predicate.Equals(Trial.Fields.Id, trialId)
}
newTrial = Nothing
Dim isPending As Boolean = true
Dim pollAttempts As Integer = 0

Do
  Dim sleepMillis As Integer = CType(Math.Pow(2, pollAttempts) * _
      POLL_INTERVAL_SECONDS_BASE * 1000, Integer)
  Console.WriteLine("Sleeping {0} millis...", sleepMillis)
  Thread.Sleep(sleepMillis)

  newTrial = trialService.get(trialSelector).entries(0)

  Console.WriteLine("Trial ID {0} has status '{1}'.", newTrial.id, newTrial.status)
  pollAttempts = pollAttempts + 1
  isPending = (newTrial.status = TrialStatus.CREATING)
Loop while isPending AndAlso (pollAttempts <= MAX_RETRIES)

If newTrial.status = TrialStatus.ACTIVE Then
  ' The trial creation was successful.
  Console.WriteLine("Trial created with ID {0} and trial campaign ID {1}.",
      newTrial.id, newTrial.trialCampaignId)
Else If newTrial.status = TrialStatus.CREATION_FAILED Then
  ' The trial creation failed, and errors can be fetched from the
  ' TrialAsyncErrorService.
  Dim errorsSelector As New Selector()
  errorsSelector.fields = New string() {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
  }
  errorsSelector.predicates = New Predicate() {
    Predicate.Equals(TrialAsyncError.Fields.TrialId, newTrial.id)
  }

  Dim trialAsyncErrorService As TrialAsyncErrorService = _
      CType(user.GetService(AdWordsService.v201609.TrialAsyncErrorService),
          TrialAsyncErrorService)

  Dim trialAsyncErrorPage As TrialAsyncErrorPage = trialAsyncErrorService.get(
      errorsSelector)
  If trialAsyncErrorPage.entries Is Nothing OrElse _
      trialAsyncErrorPage.entries.Length = 0 Then
    Console.WriteLine("Could not retrieve errors for trial {0}.", newTrial.id)
  Else
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " & _
        "following errors:", trialId, draftId)
    Dim i As Integer = 1
    For Each err As TrialAsyncError In trialAsyncErrorPage.entries
      Dim asyncError As ApiError = err.asyncError
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," & _
        " fieldPath='{4}'", i, asyncError.ApiErrorType, asyncError.errorString, _
        asyncError.trigger, asyncError.fieldPath)
      i += 1
    Next
  End If
Else
  ' Most likely, the trial is still being created. You can continue
  ' polling, but we have limited the number of attempts in the
  ' example.
  Console.WriteLine("Timed out waiting to create trial from draft ID {0} with " +
      "base campaign ID {1}.", draftId, baseCampaignId)
End If

C#

// Since creating a trial is asynchronous, we have to poll it to wait
// for it to finish.
Selector trialSelector = new Selector() {
  fields = new string[] {
    Trial.Fields.Id, Trial.Fields.Status, Trial.Fields.BaseCampaignId,
    Trial.Fields.TrialCampaignId
  },
  predicates = new Predicate[] {
    Predicate.Equals(Trial.Fields.Id, trialId)
  }
};

trial = null;
bool isPending = true;
int pollAttempts = 0;

do {
  int sleepMillis = (int) Math.Pow(2, pollAttempts) *
      POLL_INTERVAL_SECONDS_BASE * 1000;
  Console.WriteLine("Sleeping {0} millis...", sleepMillis);
  Thread.Sleep(sleepMillis);

  trial = trialService.get(trialSelector).entries[0];

  Console.WriteLine("Trial ID {0} has status '{1}'.", trial.id, trial.status);
  pollAttempts++;
  isPending = (trial.status == TrialStatus.CREATING);
} while (isPending && pollAttempts <= MAX_RETRIES);

if (trial.status == TrialStatus.ACTIVE) {
  // The trial creation was successful.
  Console.WriteLine("Trial created with ID {0} and trial campaign ID {1}.",
      trial.id, trial.trialCampaignId);
} else if (trial.status == TrialStatus.CREATION_FAILED) {
  // The trial creation failed, and errors can be fetched from the
  // TrialAsyncErrorService.
  Selector errorsSelector = new Selector() {
    fields = new string[] {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
    },
    predicates = new Predicate[] {
      Predicate.Equals(TrialAsyncError.Fields.TrialId, trial.id)
    }
  };

  TrialAsyncErrorService trialAsyncErrorService =
      (TrialAsyncErrorService) user.GetService(
          AdWordsService.v201609.TrialAsyncErrorService);

  TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector);
  if (trialAsyncErrorPage.entries == null || trialAsyncErrorPage.entries.Length == 0) {
    Console.WriteLine("Could not retrieve errors for trial {0}.", trial.id);
  } else {
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " +
        "following errors:", trial.id, draftId);
    int i = 0;
    foreach (TrialAsyncError error in trialAsyncErrorPage.entries) {
      ApiError asyncError = error.asyncError;
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," +
        " fieldPath='{4}'", i++, asyncError.ApiErrorType, asyncError.errorString,
        asyncError.trigger, asyncError.fieldPath);
    }
  }
} else {
    // Most likely, the trial is still being created. You can continue
    // polling, but we have limited the number of attempts in the
    // example.
    Console.WriteLine("Timed out waiting to create trial from draft ID {0} with " +
        "base campaign ID {1}.", draftId, baseCampaignId);
}

PHP

$selector = new Selector();
$selector->fields =
    array('Id', 'Status', 'BaseCampaignId', 'TrialCampaignId');
$selector->predicates = new Predicate('Id', 'IN', array($trial->id));

// Since creating a trial is asynchronous, we have to poll it to wait for it
// to finish.
$pollAttempts = 0;
$isPending = true;
$trial = null;
do {
  $sleepSeconds = POLL_FREQUENCY_SECONDS * pow(2, $pollAttempts);
  printf("Sleeping %d seconds...\n", $sleepSeconds);
  sleep($sleepSeconds);

  $trial = $trialService->get($selector)->entries[0];
  printf("Trial ID %d has status '%s'.\n", $trial->id, $trial->status);

  $pollAttempts++;
  $isPending = ($trial->status === 'CREATING') ? true : false;
} while ($isPending && $pollAttempts <= MAX_POLL_ATTEMPTS);

Perl

my $trial_id = $result->get_value()->[0]->get_id()->get_value();

my $predicate = Google::Ads::AdWords::v201609::Predicate->new({
    field    => "Id",
    operator => "IN",
    values   => [$trial_id]});
my $paging = Google::Ads::AdWords::v201609::Paging->new({
    startIndex    => 0,
    numberResults => 1
});
my $selector = Google::Ads::AdWords::v201609::Selector->new({
    fields => ["Id", "Status", "BaseCampaignId", "TrialCampaignId"],
    predicates => [$predicate],
    paging     => $paging
});

# Since creating a trial is asynchronous, we have to poll it to wait for
# it to finish.
my $poll_attempts = 0;
my $is_pending    = 1;
my $end_time      = time + JOB_TIMEOUT_IN_MILLISECONDS;
do {
  # Check to see if the trial is still in the process of being created.
  my $result = $client->TrialService()->get({selector => $selector});
  $trial = $result->get_entries()->[0];
  my $waittime_in_milliseconds =
    JOB_BASE_WAITTIME_IN_MILLISECONDS * (2**$poll_attempts);
  if (((time + $waittime_in_milliseconds) < $end_time)
    and $trial->get_status() eq 'CREATING')
  {
    printf("Sleeping %d milliseconds...\n", $waittime_in_milliseconds);
    sleep($waittime_in_milliseconds / 1000);    # Convert to seconds.
    $poll_attempts++;
  }
} while (time < $end_time
  and $trial->get_status() eq 'CREATING');

Python

selector = {
    'fields': ['Id', 'Status', 'BaseCampaignId', 'TrialCampaignId'],
    'predicates': [{
        'field': 'Id',
        'operator': 'IN',
        'values': [trial_id]
    }]
}

# Since creating a trial is asynchronous, we have to poll it to wait for it to
# finish.
poll_attempts = 0
is_pending = True
trial = None

while is_pending and poll_attempts < MAX_POLL_ATTEMPTS:
  trial = trial_service.get(selector)['entries'][0]
  print 'Trial ID %d has status "%s"' % (trial['id'], trial['status'])
  poll_attempts += 1
  is_pending = trial['status'] == 'CREATING'

  if is_pending:
    sleep_seconds = 30 * (2 ** poll_attempts)
    print 'Sleeping for %d seconds.' % sleep_seconds
    time.sleep(sleep_seconds)

Ruby

selector = {
  :fields => ['Id', 'Status', 'BaseCampaignId', 'TrialCampaignId'],
  :predicates => [
    :field => 'Id', :operator => 'IN', :values => [trial_id]
  ]
}

poll_attempts = 0
is_pending = true
trial = nil
begin
  sleep_seconds = 30 * (2 ** poll_attempts)
  puts "Sleeping for %d seconds" % sleep_seconds
  sleep(sleep_seconds)

  trial = trial_srv.get(selector)[:entries].first

  puts "Trial ID %d has status '%s'" % [trial[:id], trial[:status]]

  poll_attempts += 1
  is_pending = (trial[:status] == 'CREATING')
end while is_pending and poll_attempts < MAX_POLL_ATTEMPTS

Nachdem Sie einen Test erstellt haben, agiert die verknüpfte Testkampagne fast wie eine normale Kampagne. Sie können sie während der Ausführung des Tests optimieren. Eine Ausnahme bilden die folgenden Felder, die vom Test vorgegeben und in der Testkampagne unveränderlich sind (im Gegensatz zu einer normalen Kampagne):

  • status
  • name
  • startDate
  • endDate
  • budget

Ohne Angabe von startDate und endDate beim Erstellen eines Tests wird der Zeitraum der Basiskampagne verwendet. Änderungen an startDate, endDate und name im Test werden für die Testkampagne übernommen. Änderungen am Feld status der Basiskampagne werden ebenfalls für die Testkampagne übernommen.

Testvorgänge

Für Tests gibt es zwei getrennte asynchrone Vorgänge: Erstellen und Übernehmen. Wenn beim Erstellen oder Übernehmen eines Tests ein Fehler aufgetreten ist, erhalten Sie Details zu den jeweiligen Fehlern, indem Sie die Test-ID an DraftAsyncErrorService übermitteln. Im Abschnitt Fehler finden Sie ein Beispiel.

Sie können einen Test auch sofort komplett anhalten, indem Sie ihn in den Status HALTED versetzen. Es ist nicht möglich, einen angehaltenen Test zu veranlassen, Anzeigen wieder zur gleichen Zeit wie die Basiskampagne auszuliefern. Allerdings können Sie einen angehaltenen Test übernehmen, erweitern oder archivieren.

Testkampagnen teilen sich implizit immer ein Budget mit ihrer Basiskampagne. Dieses Budget darf nicht mit anderen Kampagnen geteilt werden. Außerdem muss es durch Festlegen von isExplicitlyShared auf "false" als nicht gemeinsam gekennzeichnet werden. Die Testkampagne erhält im Verlauf des Tests den angegebenen Prozentsatz des Budgets der Basiskampagne. Sobald der Test abgelaufen ist (weil das Ende erreicht ist oder sie manuell angehalten wird), kehren 100 % der Zugriffe zur Basiskampagne zurück.

Testkampagnen sind leicht zu erkennen, weil ihr campaignTrialType den Wert TRIAL hat. Wie bei Entwürfen identifiziert baseCampaignId die Basiskampagne, die als Kopiervorlage diente.

Übernahme, Erweiterung und Archivierung

Wenn Sie mit den Testergebnissen zufrieden sind, gibt es mehrere Möglichkeiten zur weiteren Vorgehensweise. Dazu versetzen Sie den Test in einen der unten beschriebenen Status.

Wenn Sie mit der Leistung des Tests nicht zufrieden sind, können Sie ihn archivieren, indem Sie ihn in den Status ARCHIVED versetzen. Dadurch wird die Testkampagne sofort als REMOVED (entfernt) gekennzeichnet und der Test wird beendet.

Wenn Sie jedoch mit dem Testergebnis zufrieden sind und der Test noch ACTIVE (aktiv) ist, haben Sie zwei Möglichkeiten, um die Testkampagne dauerhaft zu implementieren. Übernehmen Sie alle Änderungen in die Basiskampagne. Dies wird als Übernahme bezeichnet. Oder lassen Sie die vorhandene Testkampagne unabhängig vom Test selbst bestehen, sodass sie als eigenständige Kampagne funktioniert. Damit sind auch die bei einem Test normalerweise unveränderlichen Felder wieder änderbar. Dieser Prozess wird als Erweiterung bezeichnet.

Die Übernahme ist ein asynchroner Prozess und verläuft ähnlich wie die Übernahme eines Entwurfs. Versetzen Sie den Test zunächst in den Status PROMOTING und fragen Sie den Status ab, bis er entweder PROMOTED oder PROMOTE_FAILED ist. Mit dem Dienst TrialAsyncErrorService können Sie auf asynchrone Fehler prüfen.

Eine Erweiterung ist ein synchroner Prozess. Wenn Sie den Test in den Status GRADUATED versetzen, kann die Kampagne sofort geändert und ausgeführt werden. Beim Erweitern einer Testkampagne müssen Sie auch eine budgetId für ein neues von der Kampagne verwendetes Budget angeben. Nach der Erweiterung kann kein gemeinsames Budget mit der Basiskampagne mehr verwendet werden.

Berichte

Für die Testkampagne werden frühere Statistiken ihrer Basiskampagne nicht kopiert. Alle Statistiken beginnen bei null. Während des Tests werden Statistiken für die Basiskampagne und die Testkampagne separat ermittelt. Jede hat eigene Werte für Impressionen, Klicks usw. Dies ändert sich auch nicht bei einer Übernahme oder Erweiterung. Statistiken bleiben an Ort und Stelle und werden niemals in ein anderes Element kopiert.

Nach der Übernahme enthält die Basiskampagne noch sämtliche statistischen Werte der Vergangenheit, verwendet jedoch ab diesem Zeitpunkt die in die Basiskampagne kopierten Änderungen. Nach der Übernahme befinden sich die Statistiken der Testkampagne weiterhin in der Testkampagne.

Nach der Erweiterung existieren die Basiskampagne und die Testkampagne weiterhin als separate Elemente und beide führen eigene Statistiken für Berichte.

Sie können für diese Elemente alle gängigen Berichte wie den Bericht "Kampagnenleistung" verwenden. Das Feld baseCampaignId einer Kampagne steht für die Basiskampagne, während mit dem Feld campaignTrialType zwischen normalen und Testkampagnen unterschieden werden kann.

Fehler

Sowohl Entwürfe als auch Tests ermöglichen bestimmte Arten von Aktionen (Übernahme für Entwürfe sowie Erstellung und Übernahme für Tests), die asynchron verlaufen. In diesen Fällen sollten Sie den Dienst abfragen und warten, bis die Aktion vollständig abgeschlossen ist oder ein Fehler auftritt. Wenn ein Fehler auftritt, geht dies aus dem Status des Elements hervor. Nähere Informationen zum Fehler können Sie mit dem entsprechenden AsyncErrorService ermitteln.

Wenn beispielsweise ein Test nicht übernommen werden kann, können Sie eine detaillierte Fehlerbeschreibung wie folgt anfordern:

Java

Selector errorsSelector =
    new SelectorBuilder()
        .fields(TrialAsyncErrorField.TrialId, TrialAsyncErrorField.AsyncError)
        .equals(TrialAsyncErrorField.TrialId, trial.getId().toString())
        .build();

TrialAsyncErrorServiceInterface trialAsyncErrorService =
    adWordsServices.get(session, TrialAsyncErrorServiceInterface.class);
TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector);
if (trialAsyncErrorPage.getEntries() == null
    || trialAsyncErrorPage.getEntries().length == 0) {
  System.out.printf(
      "Could not retrieve errors for trial ID %d for draft ID %d.%n", trial.getId(), draftId);
} else {
  System.out.printf(
      "Could not create trial ID %d for draft ID %d due to the following errors:%n",
      trial.getId(),
      draftId);
  int i = 0;
  for (TrialAsyncError error : trialAsyncErrorPage.getEntries()) {
    ApiError asyncError = error.getAsyncError();
    System.out.printf(
        "Error #%d: errorType='%s', errorString='%s', trigger='%s', fieldPath='%s'%n",
        i++,
        asyncError.getApiErrorType(),
        asyncError.getErrorString(),
        asyncError.getTrigger(),
        asyncError.getFieldPath());
  }

VB

Else If newTrial.status = TrialStatus.CREATION_FAILED Then
  ' The trial creation failed, and errors can be fetched from the
  ' TrialAsyncErrorService.
  Dim errorsSelector As New Selector()
  errorsSelector.fields = New string() {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
  }
  errorsSelector.predicates = New Predicate() {
    Predicate.Equals(TrialAsyncError.Fields.TrialId, newTrial.id)
  }

  Dim trialAsyncErrorService As TrialAsyncErrorService = _
      CType(user.GetService(AdWordsService.v201609.TrialAsyncErrorService),
          TrialAsyncErrorService)

  Dim trialAsyncErrorPage As TrialAsyncErrorPage = trialAsyncErrorService.get(
      errorsSelector)
  If trialAsyncErrorPage.entries Is Nothing OrElse _
      trialAsyncErrorPage.entries.Length = 0 Then
    Console.WriteLine("Could not retrieve errors for trial {0}.", newTrial.id)
  Else
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " & _
        "following errors:", trialId, draftId)
    Dim i As Integer = 1
    For Each err As TrialAsyncError In trialAsyncErrorPage.entries
      Dim asyncError As ApiError = err.asyncError
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," & _
        " fieldPath='{4}'", i, asyncError.ApiErrorType, asyncError.errorString, _
        asyncError.trigger, asyncError.fieldPath)
      i += 1
    Next
  End If

C#

} else if (trial.status == TrialStatus.CREATION_FAILED) {
  // The trial creation failed, and errors can be fetched from the
  // TrialAsyncErrorService.
  Selector errorsSelector = new Selector() {
    fields = new string[] {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
    },
    predicates = new Predicate[] {
      Predicate.Equals(TrialAsyncError.Fields.TrialId, trial.id)
    }
  };

  TrialAsyncErrorService trialAsyncErrorService =
      (TrialAsyncErrorService) user.GetService(
          AdWordsService.v201609.TrialAsyncErrorService);

  TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector);
  if (trialAsyncErrorPage.entries == null || trialAsyncErrorPage.entries.Length == 0) {
    Console.WriteLine("Could not retrieve errors for trial {0}.", trial.id);
  } else {
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " +
        "following errors:", trial.id, draftId);
    int i = 0;
    foreach (TrialAsyncError error in trialAsyncErrorPage.entries) {
      ApiError asyncError = error.asyncError;
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," +
        " fieldPath='{4}'", i++, asyncError.ApiErrorType, asyncError.errorString,
        asyncError.trigger, asyncError.fieldPath);
    }
  }

PHP

$selector = new Selector();
$selector->fields = array('TrialId', 'AsyncError');
$selector->predicates = new Predicate('TrialId', 'IN', array($trial->id));

$errors = $trialAsynErrorService->get($selector)->entries;

if (count($errors) === 0) {
  printf("Could not retrieve errors for the trial with ID %d\n",
      $trial->id);
} else {
  printf("Could not create trial due to the following errors:\n");
  $i = 0;
  foreach ($errors as $error) {
    printf("Error #%d: %s\n", $i++, $error->asyncError);
  }
}

Perl

my $error_selector = Google::Ads::AdWords::v201609::Selector->new({
    fields     => ["TrialId", "AsyncError"],
    predicates => [
      Google::Ads::AdWords::v201609::Predicate->new({
          field    => "TrialId",
          operator => "IN",
          values   => [$trial_id]})]});

my $errors =
  $client->TrialAsyncErrorService->get({selector => $error_selector})
  ->get_entries();
if (!$errors) {
  printf("Could not retrieve errors for trial %d", $trial->get_id());
} else {
  printf("Could not create trial due to the following errors:");
  my $index = 0;
  for my $error ($errors) {
    printf("Error %d: %s", $index, $error->get_asyncError()
    ->get_errorString());
    $index++;
  }
}

Python

selector = {
    'fields': ['TrialId', 'AsyncError'],
    'predicates': [{
        'field': 'TrialId',
        'operator': 'IN',
        'values': [trial['id']]
    }]
}

errors = trial_async_error_service.get(selector)['entries']

if not errors:
  print 'Could not retrieve errors for trial %d' % trial['id']
else:
  print 'Could not create trial due to the following errors:'
  for error in errors:
    print 'Error: %s' % error['asyncError']

Ruby

selector = {
  :fields => ['TrialId', 'AsyncError'],
  :predicates => [
    {:field => 'TrialId', :operator => 'IN', :values => [trial[:id]]}
  ]
}

errors = trial_async_error_srv.get(selector)[:entries]

if errors.nil?
  puts "Could not retrieve errors for trial %d" % trial[:id]
else
  puts "Could not create trial due to the following errors:"
  errors.each_with_index do |error, i|
    puts "Error #%d: %s" % [i, error[:async_error]]
  end
end

Von asynchronen Vorgängen werden Änderungen im Allgemeinen auch dann noch durchgeführt, wenn ein Fehler auftritt. Das Fehlerprotokoll könnte also schnell anwachsen, wenn sehr viele inkompatible Änderungen vorhanden sind. Mögliche Fehler sind beispielsweise doppelte Namen von Anzeigengruppen, inkompatible Gebotsstrategien oder überschrittene Kontolimits.

Feedback geben zu...