-
Notifications
You must be signed in to change notification settings - Fork 184
New GenerateKeywordIdeas example for the KeywordPlanService #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
861f3ce
cd9ae7d
8d698b4
9d9254d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| // Copyright 2018 Google LLC | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // https://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package com.google.ads.googleads.examples.planning; | ||
|
|
||
| import com.beust.jcommander.Parameter; | ||
| import com.google.ads.googleads.examples.utils.ArgumentNames; | ||
| import com.google.ads.googleads.examples.utils.CodeSampleParams; | ||
| import com.google.ads.googleads.lib.GoogleAdsClient; | ||
| import com.google.ads.googleads.lib.GoogleAdsException; | ||
| import com.google.ads.googleads.lib.utils.ResourceNames; | ||
| import com.google.ads.googleads.v0.enums.KeywordPlanNetworkEnum.KeywordPlanNetwork; | ||
| import com.google.ads.googleads.v0.errors.GoogleAdsError; | ||
| import com.google.ads.googleads.v0.services.GenerateKeywordIdeaResponse; | ||
| import com.google.ads.googleads.v0.services.GenerateKeywordIdeaResult; | ||
| import com.google.ads.googleads.v0.services.GenerateKeywordIdeasRequest; | ||
| import com.google.ads.googleads.v0.services.KeywordPlanIdeaServiceClient; | ||
| import com.google.protobuf.StringValue; | ||
| import java.io.FileNotFoundException; | ||
| import java.io.IOException; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| import javax.annotation.Nullable; | ||
|
|
||
| /** This example generates keyword ideas from a list of seed keywords. */ | ||
| public class GenerateKeywordIdeas { | ||
|
|
||
| private static class GenerateKeywordIdeasParams extends CodeSampleParams { | ||
|
|
||
| @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) | ||
| private Long customerId; | ||
|
|
||
| @Parameter( | ||
| names = ArgumentNames.LOCATION_ID, | ||
| required = true, | ||
| description = | ||
| "Location criteria IDs. For example, specify 21167 for New York. For more information" | ||
| + " on determining this value, see: " | ||
| + " https://developers.google.com/adwords/api/docs/appendix/geotargeting.") | ||
| private List<Long> locationIds; | ||
|
|
||
| @Parameter( | ||
| names = ArgumentNames.LANGUAGE_ID, | ||
| required = true, | ||
| description = | ||
| "A language criterion ID. For example, specify 1000 for English. For more information" | ||
| + " on determining this value, see: " | ||
| + " https://developers.google.com/adwords/api/docs/appendix/codes-formats#languages") | ||
| private Long languageId; | ||
|
|
||
| @Parameter(names = ArgumentNames.KEYWORD_TEXT) | ||
| private List<String> keywords = new ArrayList<>(); | ||
|
|
||
| @Parameter( | ||
| names = ArgumentNames.PAGE_URL, | ||
| description = "URL of a page related to your business") | ||
| private String pageUrl; | ||
| } | ||
|
|
||
| public static void main(String[] args) throws IOException { | ||
| GenerateKeywordIdeasParams params = new GenerateKeywordIdeasParams(); | ||
| if (!params.parseArguments(args)) { | ||
| // Either pass the required parameters for this example on the command line, or insert them | ||
| // into the code here. See the parameter class definition above for descriptions. | ||
| params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE"); | ||
| params.locationIds = | ||
| Arrays.asList( | ||
| Long.parseLong("INSERT_LOCATION_ID_1_HERE"), | ||
| Long.parseLong("INSERT_LOCATION_ID_2_HERE")); | ||
| params.languageId = Long.parseLong("INSERT_LANGUAGE_ID_HERE"); | ||
| params.keywords = Arrays.asList("INSERT_KEYWORD_1_HERE", "INSERT_KEYWORD_2_HERE"); | ||
| // Optional: Use a URL related to your business to generate ideas. | ||
| params.pageUrl = null; | ||
| } | ||
|
|
||
| GoogleAdsClient googleAdsClient; | ||
| try { | ||
| googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build(); | ||
| } catch (FileNotFoundException fnfe) { | ||
| System.err.printf( | ||
| "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe); | ||
| return; | ||
| } catch (IOException ioe) { | ||
| System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| new GenerateKeywordIdeas() | ||
| .runExample( | ||
| googleAdsClient, | ||
| params.customerId, | ||
| params.languageId, | ||
| params.locationIds, | ||
| params.keywords, | ||
| params.pageUrl); | ||
| } catch (GoogleAdsException gae) { | ||
| // GoogleAdsException is the base class for most exceptions thrown by an API request. | ||
| // Instances of this exception have a message and a GoogleAdsFailure that contains a | ||
| // collection of GoogleAdsErrors that indicate the underlying causes of the | ||
| // GoogleAdsException. | ||
| System.err.printf( | ||
| "Request ID %s failed due to GoogleAdsException. Underlying errors:%n", | ||
| gae.getRequestId()); | ||
| int i = 0; | ||
| for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) { | ||
| System.err.printf(" Error %d: %s%n", i++, googleAdsError); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Runs the example. | ||
| * | ||
| * @param googleAdsClient the Google Ads API client. | ||
| * @param customerId the client customer ID. | ||
| * @param languageId the language ID. | ||
| * @param locationIds the location IDs. | ||
| * @param keywords the list of keywords to use as a seed for ideas. | ||
| * @param pageUrl optional URL related to your business to use as a seed for ideas. | ||
| * @throws GoogleAdsException if an API request failed with one or more service errors. | ||
| * @throws IllegalArgumentException if {@code keywords} is empty and {@code pageUrl} is null. | ||
| * @throws Exception if the example failed due to other errors. | ||
| */ | ||
| private void runExample( | ||
| GoogleAdsClient googleAdsClient, | ||
| long customerId, | ||
| long languageId, | ||
| List<Long> locationIds, | ||
| List<String> keywords, | ||
| @Nullable String pageUrl) { | ||
| try (KeywordPlanIdeaServiceClient keywordPlanServiceClient = | ||
| googleAdsClient.getKeywordPlanIdeaServiceClient()) { | ||
| GenerateKeywordIdeasRequest.Builder requestBuilder = | ||
| GenerateKeywordIdeasRequest.newBuilder() | ||
| .setCustomerId(Long.toString(customerId)) | ||
| // Set the language resource using the provided language ID. | ||
| .setLanguage(StringValue.of(ResourceNames.languageConstant(languageId))) | ||
| // Set the network. To restrict to only Google Search, change the parameter below to | ||
| // KeywordPlanNetwork.GOOGLE_SEARCH. | ||
| .setKeywordPlanNetwork(KeywordPlanNetwork.GOOGLE_SEARCH_AND_PARTNERS); | ||
|
|
||
| // Add the resource name of each location ID to the request. | ||
| for (Long locationId : locationIds) { | ||
| requestBuilder.addGeoTargetConstants( | ||
| StringValue.of(ResourceNames.geoTargetConstant(locationId))); | ||
| } | ||
|
|
||
| // Make sure that keywords and/or page URL were specified. The request must have exactly one | ||
| // of urlSeed, keywordSeed, or keywordAndUrlSeed set. | ||
| if (keywords.isEmpty() && pageUrl == null) { | ||
| throw new IllegalArgumentException( | ||
| "At least one of keywords or page URL is required, but neither was specified."); | ||
| } | ||
|
|
||
| if (keywords.isEmpty()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be clearer to reduce the nesting here, as in if (keywords.isEmpty() && pageUrl != null) {
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also add the above error branch to bring the entire oneof handling into a single block.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reworked to reduce nesting. I did not include the error case here because if that's true, the example will throw an exception. I personally prefer separating out the error case, but LMK if you feel strongly about combining them.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that's a good point, thanks.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I read this correctly, it looks like the keyword texts will always be at least ['INSERT_KEYWORD_TEXT_1', 'INSERT_KEYWORD_TEXT_2']?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These values are only used when the flags fail to parse (e.g. missing required, invalid number formats etc.) Also this flag is optional, so specifying |
||
| // Only page URL was specified, so use a UrlSeed. | ||
| requestBuilder.getUrlSeedBuilder().setUrl(StringValue.of(pageUrl)); | ||
| } else if (pageUrl == null) { | ||
| // Only keywords were specified, so use a KeywordSeed. | ||
| requestBuilder | ||
| .getKeywordSeedBuilder() | ||
| .addAllKeywords(keywords.stream().map(StringValue::of).collect(Collectors.toList())); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicated code for the stream().map(), maybe make a local variable?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, if I do that I'd want to move back to the 2nd level of nesting. With the current nesting layout, creating the local variable is unnecessary for the I like the current nesting you suggested, so I'd prefer to keep it with the tradeoff of a bit of duplication. Also, the duplication means the @AnashOommen , what do you think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with Josh, I prefer the ease of copy-pasting over a bit of duplication here. |
||
| } else { | ||
| // Both page URL and keywords were specified, so use a KeywordAndUrlSeed. | ||
| requestBuilder | ||
| .getKeywordAndUrlSeedBuilder() | ||
| .setUrl(StringValue.of(pageUrl)) | ||
| .addAllKeywords(keywords.stream().map(StringValue::of).collect(Collectors.toList())); | ||
| } | ||
|
|
||
| // Send the keyword ideas request. | ||
| GenerateKeywordIdeaResponse response = | ||
| keywordPlanServiceClient.generateKeywordIdeas(requestBuilder.build()); | ||
| // Print each result in the response. | ||
| for (GenerateKeywordIdeaResult result : response.getResultsList()) { | ||
| System.out.printf( | ||
| "Keyword idea text '%s' has %d average monthly searches and '%s' competition.%n", | ||
| result.getText().getValue(), | ||
| result.getKeywordIdeaMetrics().getAvgMonthlySearches().getValue(), | ||
| result.getKeywordIdeaMetrics().getCompetition()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a comment here explaining the oneof semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I modified the existing comment on lines 162-163