-
Notifications
You must be signed in to change notification settings - Fork 509
Adding a full example of how to create a Responsive Search Ad (with keywords and geo targeting) #715
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
Conversation
…cation target set.
'updating repo' git add .
…ords and geo targetting.
@@ -0,0 +1,541 @@ | |||
#!/usr/bin/env python | |||
# Copyright 2022 Google LLC |
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.
This should now be updated to 2023
|
||
|
||
# Keywords from user. | ||
keyword_text_exact_1 = "example of exact match" |
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.
As module-level variables, these should be capitalized with leading underscores, for example:
_KEYWORD_TEXT_EXACT_1 = ...
_KEYWORD_TEXT_PHRASE_1 = ...
_KEYWORD_TEXT_BROAD_1 = ...
Feel free to reference the Smart Campaign example.
# Geo targeting from user. | ||
location_1 = "Buenos aires" | ||
location_2 = "San Isidro" | ||
location_3 = "Mar del Plata" |
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.
Same as above for these variables, capitalize with leading underscore.
|
||
# A list of country codes can be referenced here: | ||
# https://developers.google.com/google-ads/api/reference/data/geotargets | ||
COUNTRY_CODE = "AR" |
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.
The two above variables should have leading underscores.
business_profile_location: the ID of a Business Profile location. | ||
business_name: the name of a Business Profile. | ||
""" | ||
# [START Step 1 -- Create campaign and budget] |
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.
The [START xyz]
notation is used to port specific sections of the example to our docs, so there's no need to include them here since this example isn't accompanied by a guide.
So this comment can just be:
# Step 1 -- Create campaign and budget
campaign_budget.amount_micros = 500000 | ||
|
||
# Add budget. | ||
try: |
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.
If you wrap the call to main
in a try/catch at the bottom of the file then you don't need to handle any exceptions here or any where else in the file.
campaign_id = campaign_response.results[0].resource_name.split("/")[-1] | ||
except GoogleAdsException as ex: | ||
handle_googleads_exception(ex) | ||
# [END Step 1 -- Create campaign and budget] |
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.
As mentioned earlier, you can remove the comment formatted that looks like:
[START xyz]
[END xyz]
Just leave the xyz
part.
6 steps to create a Responsive Search Ad (RSA). | ||
''' | ||
|
||
def main( |
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.
Can you break this method into smaller methods where each is responsible for creating a single resource? Specifically the body in main
should look something like:
def main(...):
_create_campaign_budget(...)
_create_campaign(...)
_create_ad_group(...)
The smart campaign example does a good job of this in order to make the chunks of code more discernable for the reader.
Hi Ben, thank you for all your great recommendations. I have followed all of them and commit the changes. Please review and let me know what you think. Thank you! |
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.
Please only use an initial underbar when it conforms with PEP8.
_single_leading_underscore: weak “internal use” indicator. E.g. from M import * does not import objects whose names start with an underscore.
""" | ||
# Step 1.1 -- Create budget. | ||
# Create a budget, which can be shared by multiple campaigns. | ||
campaign_budget = _create_campaign_budget(client, customer_id) |
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.
See above. Why is the initial underbar used?
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.
See my response above - it's because I asked for them to be changed. I remember now that we stopped using this pattern so agree let's remove the leading underscore from all the method names in this example.
Hi Ben, I removed the leading underscores in method names as requested. |
|
||
Returns: | ||
A responsive search ad with all settings required to run campaign. | ||
""" |
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.
The above lines (75-83) should be indented by 4 spaces.
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.
TY!
# If the account does not meet the requirements, set below variable to False. | ||
_ADD_IMAGE_EXTENSION = True | ||
|
||
"""Support function""" |
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.
It's not necessary to have this comment here, the PyDocs in each function are sufficient.
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.
TY!
|
||
|
||
def create_ad_text_asset(client, text, pinned_field=None): | ||
"""Create an AdTextAsset.""" |
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.
This PyDoc needs Args
and Returns
sections.
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.
TY!
return ad_text_asset | ||
|
||
|
||
"""6 steps to create a Responsive Search Ad (RSA).""" |
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.
No need to have this either.
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.
TY!
# To add image extensions, the account has to follow these requirements: | ||
# https://support.google.com/google-ads/answer/9566341 | ||
# If the account does not meet the requirements, set below variable to False. | ||
_ADD_IMAGE_EXTENSION = True |
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.
This should be converted into a CLI input (like this) since it can be toggled. These module-level variables should be used for values that are static.
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.
TY!
# Create the campaign criterion for location targeting. | ||
campaign_criterion_operation = client.get_type("CampaignCriterionOperation") | ||
campaign_criterion = campaign_criterion_operation.create | ||
campaign_criterion.campaign = campaign_service.campaign_path( |
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.
Once you update the method to accept a campaign resource name you won't need to do this.
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.
TY!
for result in campaign_criterion_response.results: | ||
print(f'Added campaign criterion "{result.resource_name}".') | ||
|
||
return |
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.
No explicit return needed.
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.
TY!
# Step 6.1 - Add Image Asset. | ||
|
||
# Download image from URL. | ||
url = "https://img.cppng.com/download/2020-06/66912-logo-now-google-plus-search-free-transparent-image-hd.png" |
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.
We have images hosted specifically for this purpose, for example: https://gaagl.page.link/bjYi
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.
TY!
for row in mutate_asset_response.results: | ||
print(f"\tResource name: {row.resource_name}") | ||
|
||
image_asset_id = mutate_asset_response.results[0].resource_name.split("/")[-1] |
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.
No need to split out the ID here, you can just reuse the resource name as-is on line 507.
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.
TY!
"Created a campaign extension setting with resource name: " | ||
f"'{campaign_extension_response.results[0].resource_name}'" | ||
) | ||
return |
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.
No explicit return.
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.
TY!
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.
Just one small change then we're good to go.
) | ||
ad_resource_name = result.resource_name.split("/")[-1] | ||
|
||
return ad_resource_name |
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.
Looks like this value isn't used by the calling code, so no need to even return it at all, just remove everything below the print statement.
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.
TY!
# To add image extensions, the account has to follow these requirements: | ||
# https://support.google.com/google-ads/answer/9566341 | ||
# If the account meets the requirements, set below variable to True. | ||
if omit_image_extensions: |
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.
This should be if not omit_image_extensions
, correct?
On that note, could we make this include_image_extensions
and have it default to True
instead? That would make the logic more readable and avoid a double negative for the "do something" case. Then the code here would be:
if include_image_extensions:
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.
You are right! Let's change it to include_image_extensions, but I would still have it as False as a default because not all accounts can create them. Therefore, we avoid developers getting errors.
# The bidding strategy for Maximize Clicks is TargetSpend. | ||
# The target_spend_micros is deprecated so don't put any value. | ||
# See other bidding strategies you can select in the link below. | ||
# https://developers.google.com/google-ads/api/reference/rpc/v11/Campaign#campaign_bidding_strategy |
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.
Change v11
to latest
so this isn't version dependent.
|
||
|
||
def add_images(client, customer_id, campaign_resource_name): | ||
# Step 6.1 - Add Image Asset. |
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.
Do these numbered steps correspond to upcoming changes to documentation? I noticed they're only present in this method.
asset.image_asset.file_size = len(image_content) | ||
|
||
# MIME type of the image (IMAGE_JPEG, IMAGE_PNG, etc.). | ||
# See more types on the link below. | ||
# https://developers.google.com/google-ads/api/reference/rpc/v11/MimeTypeEnum.MimeType | ||
asset.image_asset.mime_type = client.enums.MimeTypeEnum.IMAGE_PNG | ||
# Use your favorite image library to determine dimensions | ||
asset.image_asset.full_size.height_pixels = 1200 | ||
asset.image_asset.full_size.width_pixels = 1200 | ||
asset.image_asset.full_size.url = url |
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.
Is setting file_size
and full_size
necessary? We don't set any of those attributes in https://developers.google.com/google-ads/api/samples/upload-image#python, but we do in https://developers.google.com/google-ads/api/samples/upload-image-asset#python.
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 just tested by taking them out, and I didn't get any errors. Therefore, we could comment them out or remove them.
This example shows how to create a complete Responsive Search ad.
Includes creation of: budget, campaign, ad group, ad group ad,
keywords, geo targeting, and image extensions.