diff --git a/examples/advanced_operations/add_responsive_search_ad_full.py b/examples/advanced_operations/add_responsive_search_ad_full.py index c25aed401..70c81a591 100644 --- a/examples/advanced_operations/add_responsive_search_ad_full.py +++ b/examples/advanced_operations/add_responsive_search_ad_full.py @@ -16,7 +16,7 @@ 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. +keywords, and geo targeting. More details on Responsive Search ads can be found here: https://support.google.com/google-ads/answer/7684791 @@ -26,45 +26,39 @@ import sys import uuid -import requests - from google.ads.googleads.client import GoogleAdsClient from google.ads.googleads.errors import GoogleAdsException # Keywords from user. -_KEYWORD_TEXT_EXACT_1 = "example of exact match" -_KEYWORD_TEXT_PHRASE_1 = "example of phrase match" -_KEYWORD_TEXT_BROAD_1 = "example of broad match" +KEYWORD_TEXT_EXACT = "example of exact match" +KEYWORD_TEXT_PHRASE = "example of phrase match" +KEYWORD_TEXT_BROAD = "example of broad match" # Geo targeting from user. -_GEO_LOCATION_1 = "Buenos aires" -_GEO_LOCATION_2 = "San Isidro" -_GEO_LOCATION_3 = "Mar del Plata" +GEO_LOCATION_1 = "Buenos aires" +GEO_LOCATION_2 = "San Isidro" +GEO_LOCATION_3 = "Mar del Plata" -# _LOCALE and _COUNTRY_CODE are used for geo targeting. -# _LOCALE is using ISO 639-1 format. If an invalid _LOCALE is given, +# LOCALE and COUNTRY_CODE are used for geo targeting. +# LOCALE is using ISO 639-1 format. If an invalid LOCALE is given, # 'es' is used by default. -_LOCALE = "es" +LOCALE = "es" # A list of country codes can be referenced here: # https://developers.google.com/google-ads/api/reference/data/geotargets -_COUNTRY_CODE = "AR" +COUNTRY_CODE = "AR" -def main( - client, customer_id, omit_image_extensions, customizer_attribute_name=None -): +def main(client, customer_id, customizer_attribute_name=None): """ The main method that creates all necessary entities for the example. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. - - Returns: - A responsive search ad with all settings required to run campaign. + customizer_attribute_name: The name of the customizer attribute to be + created """ - if customizer_attribute_name: customizer_attribute_resource_name = create_customizer_attribute( client, customer_id, customizer_attribute_name @@ -93,15 +87,7 @@ def main( add_geo_targeting(client, customer_id, campaign_resource_name) - # This is optional but recommended for RSA. - # 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: - add_images(client, customer_id, campaign_resource_name) - -# [START add_responsive_search_ad_full_customizer_1] def create_customizer_attribute(client, customer_id, customizer_attribute_name): """Creates a customizer attribute with the given customizer attribute name. @@ -119,7 +105,7 @@ def create_customizer_attribute(client, customer_id, customizer_attribute_name): # Create a customizer attribute with the specified name. customizer_attribute = operation.create customizer_attribute.name = customizer_attribute_name - # Specifie the type to be 'PRICE' so that we can dynamically customize the + # Specify the type to be 'PRICE' so that we can dynamically customize the # part of the ad's description that is a price of a product/service we # advertise. customizer_attribute.type_ = client.enums.CustomizerAttributeTypeEnum.PRICE @@ -139,10 +125,6 @@ def create_customizer_attribute(client, customer_id, customizer_attribute_name): return resource_name -# [END add_responsive_search_ad_full_customizer_1] - - -# [START add_responsive_search_full_customizer_2] def link_customizer_attribute_to_customer( client, customer_id, customizer_attribute_resource_name ): @@ -184,11 +166,9 @@ def link_customizer_attribute_to_customer( ) -# [END add_responsive_search_full_customizer_2] - - def create_ad_text_asset(client, text, pinned_field=None): """Create an AdTextAsset. + Args: client: an initialized GoogleAdsClient instance. text: text for headlines and descriptions. @@ -285,7 +265,7 @@ def create_campaign(client, customer_id, campaign_budget): # 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 + # https://developers.google.com/google-ads/api/reference/rpc/latest/Campaign#campaign_bidding_strategy campaign.target_spend.target_spend_micros = 0 campaign.campaign_budget = campaign_budget @@ -369,7 +349,7 @@ def create_ad_group_ad( ad_group_ad.ad_group = ad_group_resource_name # Set responsive search ad info. - # https://developers.google.com/google-ads/api/reference/rpc/v11/ResponsiveSearchAdInfo + # https://developers.google.com/google-ads/api/reference/rpc/latest/ResponsiveSearchAdInfo # The list of possible final URLs after all cross-domain redirects for the ad. ad_group_ad.ad.final_urls.append("https://www.example.com/") @@ -450,7 +430,7 @@ def add_keywords(client, customer_id, ad_group_resource_name): ad_group_criterion = ad_group_criterion_operation.create ad_group_criterion.ad_group = ad_group_resource_name ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED - ad_group_criterion.keyword.text = _KEYWORD_TEXT_EXACT_1 + ad_group_criterion.keyword.text = KEYWORD_TEXT_EXACT ad_group_criterion.keyword.match_type = ( client.enums.KeywordMatchTypeEnum.EXACT ) @@ -469,7 +449,7 @@ def add_keywords(client, customer_id, ad_group_resource_name): ad_group_criterion = ad_group_criterion_operation.create ad_group_criterion.ad_group = ad_group_resource_name ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED - ad_group_criterion.keyword.text = _KEYWORD_TEXT_PHRASE_1 + ad_group_criterion.keyword.text = KEYWORD_TEXT_PHRASE ad_group_criterion.keyword.match_type = ( client.enums.KeywordMatchTypeEnum.PHRASE ) @@ -488,7 +468,7 @@ def add_keywords(client, customer_id, ad_group_resource_name): ad_group_criterion = ad_group_criterion_operation.create ad_group_criterion.ad_group = ad_group_resource_name ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED - ad_group_criterion.keyword.text = _KEYWORD_TEXT_BROAD_1 + ad_group_criterion.keyword.text = KEYWORD_TEXT_BROAD ad_group_criterion.keyword.match_type = ( client.enums.KeywordMatchTypeEnum.BROAD ) @@ -502,14 +482,11 @@ def add_keywords(client, customer_id, ad_group_resource_name): # Add operation operations.append(ad_group_criterion_operation) - # Set all operations together. - campaign_criterion_operations = operations - # Add keywords ad_group_criterion_response = ( ad_group_criterion_service.mutate_ad_group_criteria( customer_id=customer_id, - operations=[*campaign_criterion_operations], + operations=operations, ) ) for result in ad_group_criterion_response.results: @@ -533,12 +510,12 @@ def add_geo_targeting(client, customer_id, campaign_resource_name): # GeoTargetConstantService.suggest_geo_target_constants() and directly # apply GeoTargetConstant.resource_name. gtc_request = client.get_type("SuggestGeoTargetConstantsRequest") - gtc_request.locale = _LOCALE - gtc_request.country_code = _COUNTRY_CODE + gtc_request.locale = LOCALE + gtc_request.country_code = COUNTRY_CODE # The location names to get suggested geo target constants. gtc_request.location_names.names.extend( - [_GEO_LOCATION_1, _GEO_LOCATION_2, _GEO_LOCATION_3] + [GEO_LOCATION_1, GEO_LOCATION_2, GEO_LOCATION_3] ) results = geo_target_constant_service.suggest_geo_target_constants( @@ -548,8 +525,9 @@ def add_geo_targeting(client, customer_id, campaign_resource_name): operations = [] for suggestion in results.geo_target_constant_suggestions: print( - f"geo_target_constant: {suggestion.geo_target_constant} " - f"is found in _LOCALE ({suggestion.locale}) " + "geo_target_constant: " + f"{suggestion.geo_target_constant.resource_name} " + f"is found in LOCALE ({suggestion.locale}) " f"with reach ({suggestion.reach}) " f"from search term ({suggestion.search_term})." ) @@ -575,89 +553,6 @@ def add_geo_targeting(client, customer_id, campaign_resource_name): print(f'Added campaign criterion "{result.resource_name}".') -def add_images(client, customer_id, campaign_resource_name): - # Step 6.1 - Add Image Asset. - - # Download PNG image from URL. - url = "https://gaagl.page.link/bjYi" - image_content = requests.get(url).content - - asset_service = client.get_service("AssetService") - asset_operation = client.get_type("AssetOperation") - asset = asset_operation.create - asset.type_ = client.enums.AssetTypeEnum.IMAGE - - # Data field is the raw bytes data of an image. - asset.image_asset.data = image_content - 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 - # Provide a unique friendly name to identify your asset. - # When there is an existing image asset with the same content but a different - # name, the new name will be dropped silently. - asset.name = f"Testing Image via API {uuid.uuid4()}" - - mutate_asset_response = asset_service.mutate_assets( - customer_id=customer_id, operations=[asset_operation] - ) - print("Uploaded file(s):") - for row in mutate_asset_response.results: - print(f"\tResource name: {row.resource_name}") - - image_asset_resource_name = mutate_asset_response.results[0].resource_name - - # Step 6.2 - Create Image Extension - extension_feed_item_service = client.get_service("ExtensionFeedItemService") - extension_feed_item_operation = client.get_type( - "ExtensionFeedItemOperation" - ) - extension_feed_item = extension_feed_item_operation.create - extension_feed_item.image_feed_item.image_asset = image_asset_resource_name - - extension_feed_response = ( - extension_feed_item_service.mutate_extension_feed_items( - customer_id=customer_id, operations=[extension_feed_item_operation] - ) - ) - image_resource_name = extension_feed_response.results[0].resource_name - - print( - "Created an image extension with resource name: " - f"'{image_resource_name}'" - ) - - # Step 6.3 - Link Image Extension to RSA - campaign_extension_setting_service = client.get_service( - "CampaignExtensionSettingService" - ) - campaign_extension_setting_operation = client.get_type( - "CampaignExtensionSettingOperation" - ) - ces = campaign_extension_setting_operation.create - ces.campaign = campaign_resource_name - ces.extension_type = client.enums.ExtensionTypeEnum.IMAGE - ces.extension_feed_items.append(image_resource_name) - - campaign_extension_response = ( - campaign_extension_setting_service.mutate_campaign_extension_settings( - customer_id=customer_id, - operations=[campaign_extension_setting_operation], - ) - ) - - print( - "Created a campaign extension setting with resource name: " - f"'{campaign_extension_response.results[0].resource_name}'" - ) - - if __name__ == "__main__": # GoogleAdsClient will read the google-ads.yaml configuration file in the # home directory if none is specified. @@ -674,13 +569,6 @@ def add_images(client, customer_id, campaign_resource_name): required=True, help="The Google Ads customer ID.", ) - parser.add_argument( - "-o", - "--omit_image_extensions", - type=bool, - default=False, - help="Whether or not the campaign will use image extensions.", - ) # The name of the customizer attribute used in the ad customizer, which # must be unique for a given customer account. To run this example multiple @@ -706,7 +594,6 @@ def add_images(client, customer_id, campaign_resource_name): main( googleads_client, args.customer_id, - args.omit_image_extensions, args.customizer_attribute_name, ) except GoogleAdsException as ex: diff --git a/examples/assets/add_sitelinks.py b/examples/assets/add_sitelinks.py index 1c96604a3..8175ed710 100755 --- a/examples/assets/add_sitelinks.py +++ b/examples/assets/add_sitelinks.py @@ -39,13 +39,12 @@ def main(client, customer_id, campaign_id): link_sitelinks_to_campaign(client, customer_id, campaign_id, resource_names) -def create_sitelink_assets(client, customer_id, campaign_id): +def create_sitelink_assets(client, customer_id): """Creates sitelink assets, which can be added to campaigns. Args: client: The Google Ads client. customer_id: The customer ID for which to add the keyword. - campaign_id: The campaign to which sitelinks will be added. Returns: a list of sitelink asset resource names. diff --git a/examples/recommendations/detect_and_apply_recommendations.py b/examples/recommendations/detect_and_apply_recommendations.py index 42fa65905..d9aefa540 100755 --- a/examples/recommendations/detect_and_apply_recommendations.py +++ b/examples/recommendations/detect_and_apply_recommendations.py @@ -12,31 +12,29 @@ # 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. -"""This example shows how to retrieve recommendations and apply them. +"""This example shows how to retrieve recommendations and apply them in a batch. -The auto-apply feature, which automatically applies recommendations as they -become eligible, is supported by the Google Ads UI but not by the Google Ads -API. See https://support.google.com/google-ads/answer/10279006 for more -information on using auto-apply in the Google Ads UI. +Recommendations should be applied shortly after they're retrieved. Depending on +the recommendation type, a recommendation can become obsolete quickly, and +obsolete recommendations throw an error when applied. For more details, see: +https://developers.google.com/google-ads/api/docs/recommendations#take_action -This example demonstrates how an alternative can be implemented with the -features that are currently supported by the Google Ads API. It periodically -retrieves and applies `KEYWORD` recommendations with default parameters. +As of Google Ads API v15 users can subscribe to certain recommendation types to +apply them automatically. For more details, see: +https://developers.google.com/google-ads/api/docs/recommendations#auto-apply + +As of Google Ads API v16 users can proactively generate certain recommendation +types during the campaign construction process. For more details see: +https://developers.google.com/google-ads/api/docs/recommendations#recommendations-in-campaign-construction """ import argparse import sys -from time import sleep from google.ads.googleads.client import GoogleAdsClient from google.ads.googleads.errors import GoogleAdsException -MAX_RESULT_SIZE = 2 -NUMBER_OF_RUNS = 2 -SECONDS_TO_SLEEP = 5 -PAGE_SIZE = 1000 - def main(client, customer_id): """The main method that creates all necessary entities for the example. @@ -62,53 +60,46 @@ def detect_and_apply_recommendations(client, customer_id): recommendation.keyword_recommendation FROM recommendation WHERE - recommendation.type = KEYWORD - LIMIT {MAX_RESULT_SIZE}""" - - for i in range(NUMBER_OF_RUNS): - request = client.get_type("SearchGoogleAdsRequest") - request.customer_id = customer_id - request.query = query - request.page_size = PAGE_SIZE - - response = googleads_service.search(request=request) - - for row in response.results: - recommendation = row.recommendation - print( - f"Keyword recommendation ('{recommendation.resource_name}') " - f"was found for campaign '{recommendation.campaign}" - ) - - if "keyword_recommendation" in recommendation: - keyword = recommendation.keyword_recommendation.keyword - print( - f"\tKeyword = '{keyword.text}'\n" - f"\tType = '{keyword.match_type}'" - ) - - apply_recommendation( - client, customer_id, recommendation.resource_name - ) + recommendation.type = KEYWORD""" + # Detects keyword recommendations that exist for the customer account. + response = googleads_service.search(customer_id=customer_id, query=query) + + operations = [] + for row in response.results: + recommendation = row.recommendation print( - f"Waiting {SECONDS_TO_SLEEP} seconds before applying more " - "recommendations." + f"Keyword recommendation ('{recommendation.resource_name}') " + f"was found for campaign '{recommendation.campaign}." ) - sleep(SECONDS_TO_SLEEP) + keyword = recommendation.keyword_recommendation.keyword + print( + f"\tKeyword = '{keyword.text}'\n" f"\tType = '{keyword.match_type}'" + ) -# [START apply_recommendation] -def apply_recommendation(client, customer_id, recommendation): - """Applies a recommendation. + # Create an ApplyRecommendationOperation that will be used to apply + # this recommendation, and add it to the list of operations. + operations.append( + build_recommendation_operation(client, recommendation.resource_name) + ) + + # If there are operations present, send a request to apply the + # recommendations. + if operations: + apply_recommendations(client, customer_id, operations) + + +def build_recommendation_operation(client, recommendation): + """Creates a ApplyRecommendationOperation to apply the given recommendation. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. recommendation: a resource name for the recommendation to be applied. """ - # If you have a recommendation_id instead of the resournce_name - # you can create a resource name from it like this: + # If you have a recommendation ID instead of a resource name, you can create + # a resource name like this: # # googleads_service = client.get_service("GoogleAdsService") # resource_name = googleads_service.recommendation_path( @@ -116,28 +107,41 @@ def apply_recommendation(client, customer_id, recommendation): # ) operation = client.get_type("ApplyRecommendationOperation") - operation.resource_name = recommendation # Each recommendation type has optional parameters to override the - # recommended values. This is an example to override a recommended ad when a - # TextAdRecommendation is applied. - # For details, please read: - # https://developers.google.com/google-ads/api/reference/rpc/google.ads.google_ads.v1.services#google.ads.google_ads.v1.services.ApplyRecommendationOperation + # recommended values. Below is an example showing how to override a + # recommended ad when a TextAdRecommendation is applied. + # + # operation.text_ad.ad.resource_name = "INSERT_AD_RESOURCE_NAME" # - # operation.text_ad.ad = "INSERT_AD_ID_AS_INTEGER_HERE" + # For more details, see: + # https://developers.google.com/google-ads/api/reference/rpc/latest/ApplyRecommendationOperation#apply_parameters + + operation.resource_name = recommendation + return operation + + +# [START apply_recommendation] +def apply_recommendations(client, customer_id, operations): + """Applies a batch of recommendations. - # Issues a mutate request to apply the recommendation. + Args: + client: an initialized GoogleAdsClient instance. + customer_id: a client customer ID. + operations: a list of ApplyRecommendationOperation messages. + """ + # Issues a mutate request to apply the recommendations. recommendation_service = client.get_service("RecommendationService") response = recommendation_service.apply_recommendation( - customer_id=customer_id, operations=[operation] + customer_id=customer_id, operations=operations ) - applied_recommendation = response.results[0].resource_name - - print( - "Applied recommendation with resource name: '{applied_recommendation}'." - ) - # [END apply_recommendation] + for result in response.results: + print( + "Applied a recommendation with resource name: " + f"'{result[0].resource_name}'." + ) + # [END apply_recommendation] if __name__ == "__main__": @@ -146,7 +150,10 @@ def apply_recommendation(client, customer_id, recommendation): googleads_client = GoogleAdsClient.load_from_storage(version="v16") parser = argparse.ArgumentParser( - description="Lists TEXT_AD recommendations for specified customer." + description=( + "Retrieves keyword recommendations for specified customer and " + "applies them." + ) ) # The following argument(s) should be provided to run the example. parser.add_argument(