Google 广告 API Python - 403 请求的身份验证范围不足
Google Ads API Python - 403 Request had insufficient authentication scopes
我正在尝试访问 Google 来自 Python 的广告活动报告 this tutorial。
我已经申请了具有基本访问权限的开发人员令牌。我认为它有 enough privileges 来执行脚本。当我转到 google 广告中的“API 中心”时,我可以看到我的令牌处于活动状态。
我在 google 云中创建了一个项目和一个 Oauth 令牌。
在google云端:
- 创建了一个新项目
- 激活了 Google 广告 API。
- 当我转到“管理”->“凭据”时,我看到 Oauth 令牌与 API 兼容。
- 我已成功创建刷新令牌。
我使用这个脚本作为概念证明:
import os
import json
import sys
from google.ads.google_ads.errors import GoogleAdsException
# Put an account id to download stats from. Note: not MCC, no dash lines
CUSTOMER_ID = "xxxxxxxxxx"
def get_account_id(account_id, check_only=False):
"""
Converts int to str, checks if str has dashes. Returns 10 chars str or raises error
:check_only - if True, returns None instead of Error
"""
if isinstance(account_id, int) and len(str(account_id)) == 10:
return str(account_id)
if isinstance(account_id, str) and len(account_id.replace("-", "")) == 10:
return account_id.replace("-", "")
if check_only:
return None
raise ValueError(f"Couldn't recognize account id from {account_id}")
def micros_to_currency(micros):
return micros / 1000000.0
def main(client, customer_id):
ga_service = client.get_service("GoogleAdsService")# , version="v5")
query = """
SELECT
campaign.id,
campaign.name,
ad_group.id,
ad_group.name,
ad_group_criterion.criterion_id,
ad_group_criterion.keyword.text,
ad_group_criterion.keyword.match_type,
metrics.impressions,
metrics.clicks,
metrics.cost_micros
FROM keyword_view
WHERE
segments.date DURING LAST_7_DAYS
AND campaign.advertising_channel_type = 'SEARCH'
AND ad_group.status = 'ENABLED'
AND ad_group_criterion.status IN ('ENABLED', 'PAUSED')
ORDER BY metrics.impressions DESC
LIMIT 50"""
# Issues a search request using streaming.
response = ga_service.search_stream(customer_id, query) #THIS LINE GENERATES THE ERROR
keyword_match_type_enum = client.get_type(
"KeywordMatchTypeEnum"
).KeywordMatchType
try:
for batch in response:
for row in batch.results:
campaign = row.campaign
ad_group = row.ad_group
criterion = row.ad_group_criterion
metrics = row.metrics
keyword_match_type = keyword_match_type_enum.Name(
criterion.keyword.match_type
)
print(
f'Keyword text "{criterion.keyword.text}" with '
f'match type "{keyword_match_type}" '
f"and ID {criterion.criterion_id} in "
f'ad group "{ad_group.name}" '
f'with ID "{ad_group.id}" '
f'in campaign "{campaign.name}" '
f"with ID {campaign.id} "
f"had {metrics.impressions} impression(s), "
f"{metrics.clicks} click(s), and "
f"{metrics.cost_micros} cost (in micros) during "
"the last 7 days."
)
except GoogleAdsException as ex:
print(
f'Request with ID "{ex.request_id}" failed with status '
f'"{ex.error.code().name}" and includes the following errors:'
)
for error in ex.failure.errors:
print(f'\tError with message "{error.message}".')
if error.location:
for field_path_element in error.location.field_path_elements:
print(f"\t\tOn field: {field_path_element.field_name}")
sys.exit(1)
if __name__ == "__main__":
# credentials dictonary
creds = {"google_ads": "googleads.yaml"}
if not os.path.isfile(creds["google_ads"]):
raise FileExistsError("File googleads.yaml doesn't exists. ")
resources = {"config": "config.json"}
# This logging allows to see additional information on debugging
import logging
logging.basicConfig(level=logging.INFO, format='[%(asctime)s - %(levelname)s] %(message).5000s')
logging.getLogger('google.ads.google_ads.client').setLevel(logging.DEBUG)
# Initialize the google_ads client
from google.ads.google_ads.client import GoogleAdsClient
gads_client = GoogleAdsClient.load_from_storage(creds["google_ads"])
id_to_load = get_account_id(CUSTOMER_ID)
main(gads_client, id_to_load)
- 我已经把CUSTOMER_ID改成了左上角出现的账号
- 我创建了 googleads.yaml 并加载了上述信息。
当我执行脚本时出现此错误:
Traceback (most recent call last):
File "download_keywords_from_account.py", line 138, in <module>
main(gads_client, id_to_load)
File "download_keywords_from_account.py", line 70, in main
response = ga_service.search_stream(customer_id, query)
File "google/ads/google_ads/v6/services/google_ads_service_client.py", line 366, in search_stream
return self._inner_api_calls['search_stream'](request, retry=retry, timeout=timeout, metadata=metadata)
File google/api_core/gapic_v1/method.py", line 145, in __call__
return wrapped_func(*args, **kwargs)
File "google/api_core/retry.py", line 281, in retry_wrapped_func
return retry_target(
File "google/api_core/retry.py", line 184, in retry_target
return target()
File "google/api_core/timeout.py", line 214, in func_with_timeout
return func(*args, **kwargs)
File "google/api_core/grpc_helpers.py", line 152, in error_remapped_callable
six.raise_from(exceptions.from_grpc_error(exc), exc)
File "<string>", line 3, in raise_from
google.api_core.exceptions.PermissionDenied: 403 Request had insufficient authentication scopes
googleads.yaml 文件如下所示:
#############################################################################
# Required Fields #
#############################################################################
developer_token: {developer token as seen in google ads -> tools -> api center}
#############################################################################
# Optional Fields #
#############################################################################
login_customer_id: {Id from the top left corner in google ads, only numbers}
# user_agent: INSERT_USER_AGENT_HERE
# partial_failure: True
validate_only: False
#############################################################################
# OAuth2 Configuration #
# Below you may provide credentials for either the installed application or #
# service account flows. Remove or comment the lines for the flow you're #
# not using. #
#############################################################################
# The following values configure the client for the installed application
# flow.
client_id: {Oauth client id taken from gcloud -> api -> credentials} ends with apps.googleusercontent.com
client_secret: {got it while generating the token}
refresh_token: 1//0hr.... made with generate_refresh_token.py
# The following values configure the client for the service account flow.
path_to_private_key_file: ads.json
# delegated_account: INSERT_DOMAIN_WIDE_DELEGATION_ACCOUNT
#############################################################################
# ReportDownloader Headers #
# Below you may specify boolean values for optional headers that will be #
# applied to all requests made by the ReportDownloader utility by default. #
#############################################################################
# report_downloader_headers:
# skip_report_header: False
# skip_column_header: False
# skip_report_summary: False
# use_raw_enum_values: False
备注:
文件 ads.json 包含从 gcloud 中的凭据页面下载的私钥。
我看过一些关于这个问题的帖子,但其中 none 是 Python + GoogleAD,我在那里也找不到解决方案。
我也试过其他 Python + GoogleAds 示例得到同样的错误。这让我
认为我必须在 gcloud / google 广告中配置错误。但是我不明白什么。
请帮我查询我真的卡住了。
非常感谢!
@DazWilkin 的评论解决了我的问题。谢谢!
我正在尝试访问 Google 来自 Python 的广告活动报告 this tutorial。
我已经申请了具有基本访问权限的开发人员令牌。我认为它有 enough privileges 来执行脚本。当我转到 google 广告中的“API 中心”时,我可以看到我的令牌处于活动状态。
我在 google 云中创建了一个项目和一个 Oauth 令牌。
在google云端:
- 创建了一个新项目
- 激活了 Google 广告 API。
- 当我转到“管理”->“凭据”时,我看到 Oauth 令牌与 API 兼容。
- 我已成功创建刷新令牌。
我使用这个脚本作为概念证明:
import os
import json
import sys
from google.ads.google_ads.errors import GoogleAdsException
# Put an account id to download stats from. Note: not MCC, no dash lines
CUSTOMER_ID = "xxxxxxxxxx"
def get_account_id(account_id, check_only=False):
"""
Converts int to str, checks if str has dashes. Returns 10 chars str or raises error
:check_only - if True, returns None instead of Error
"""
if isinstance(account_id, int) and len(str(account_id)) == 10:
return str(account_id)
if isinstance(account_id, str) and len(account_id.replace("-", "")) == 10:
return account_id.replace("-", "")
if check_only:
return None
raise ValueError(f"Couldn't recognize account id from {account_id}")
def micros_to_currency(micros):
return micros / 1000000.0
def main(client, customer_id):
ga_service = client.get_service("GoogleAdsService")# , version="v5")
query = """
SELECT
campaign.id,
campaign.name,
ad_group.id,
ad_group.name,
ad_group_criterion.criterion_id,
ad_group_criterion.keyword.text,
ad_group_criterion.keyword.match_type,
metrics.impressions,
metrics.clicks,
metrics.cost_micros
FROM keyword_view
WHERE
segments.date DURING LAST_7_DAYS
AND campaign.advertising_channel_type = 'SEARCH'
AND ad_group.status = 'ENABLED'
AND ad_group_criterion.status IN ('ENABLED', 'PAUSED')
ORDER BY metrics.impressions DESC
LIMIT 50"""
# Issues a search request using streaming.
response = ga_service.search_stream(customer_id, query) #THIS LINE GENERATES THE ERROR
keyword_match_type_enum = client.get_type(
"KeywordMatchTypeEnum"
).KeywordMatchType
try:
for batch in response:
for row in batch.results:
campaign = row.campaign
ad_group = row.ad_group
criterion = row.ad_group_criterion
metrics = row.metrics
keyword_match_type = keyword_match_type_enum.Name(
criterion.keyword.match_type
)
print(
f'Keyword text "{criterion.keyword.text}" with '
f'match type "{keyword_match_type}" '
f"and ID {criterion.criterion_id} in "
f'ad group "{ad_group.name}" '
f'with ID "{ad_group.id}" '
f'in campaign "{campaign.name}" '
f"with ID {campaign.id} "
f"had {metrics.impressions} impression(s), "
f"{metrics.clicks} click(s), and "
f"{metrics.cost_micros} cost (in micros) during "
"the last 7 days."
)
except GoogleAdsException as ex:
print(
f'Request with ID "{ex.request_id}" failed with status '
f'"{ex.error.code().name}" and includes the following errors:'
)
for error in ex.failure.errors:
print(f'\tError with message "{error.message}".')
if error.location:
for field_path_element in error.location.field_path_elements:
print(f"\t\tOn field: {field_path_element.field_name}")
sys.exit(1)
if __name__ == "__main__":
# credentials dictonary
creds = {"google_ads": "googleads.yaml"}
if not os.path.isfile(creds["google_ads"]):
raise FileExistsError("File googleads.yaml doesn't exists. ")
resources = {"config": "config.json"}
# This logging allows to see additional information on debugging
import logging
logging.basicConfig(level=logging.INFO, format='[%(asctime)s - %(levelname)s] %(message).5000s')
logging.getLogger('google.ads.google_ads.client').setLevel(logging.DEBUG)
# Initialize the google_ads client
from google.ads.google_ads.client import GoogleAdsClient
gads_client = GoogleAdsClient.load_from_storage(creds["google_ads"])
id_to_load = get_account_id(CUSTOMER_ID)
main(gads_client, id_to_load)
- 我已经把CUSTOMER_ID改成了左上角出现的账号
- 我创建了 googleads.yaml 并加载了上述信息。
当我执行脚本时出现此错误:
Traceback (most recent call last):
File "download_keywords_from_account.py", line 138, in <module>
main(gads_client, id_to_load)
File "download_keywords_from_account.py", line 70, in main
response = ga_service.search_stream(customer_id, query)
File "google/ads/google_ads/v6/services/google_ads_service_client.py", line 366, in search_stream
return self._inner_api_calls['search_stream'](request, retry=retry, timeout=timeout, metadata=metadata)
File google/api_core/gapic_v1/method.py", line 145, in __call__
return wrapped_func(*args, **kwargs)
File "google/api_core/retry.py", line 281, in retry_wrapped_func
return retry_target(
File "google/api_core/retry.py", line 184, in retry_target
return target()
File "google/api_core/timeout.py", line 214, in func_with_timeout
return func(*args, **kwargs)
File "google/api_core/grpc_helpers.py", line 152, in error_remapped_callable
six.raise_from(exceptions.from_grpc_error(exc), exc)
File "<string>", line 3, in raise_from
google.api_core.exceptions.PermissionDenied: 403 Request had insufficient authentication scopes
googleads.yaml 文件如下所示:
#############################################################################
# Required Fields #
#############################################################################
developer_token: {developer token as seen in google ads -> tools -> api center}
#############################################################################
# Optional Fields #
#############################################################################
login_customer_id: {Id from the top left corner in google ads, only numbers}
# user_agent: INSERT_USER_AGENT_HERE
# partial_failure: True
validate_only: False
#############################################################################
# OAuth2 Configuration #
# Below you may provide credentials for either the installed application or #
# service account flows. Remove or comment the lines for the flow you're #
# not using. #
#############################################################################
# The following values configure the client for the installed application
# flow.
client_id: {Oauth client id taken from gcloud -> api -> credentials} ends with apps.googleusercontent.com
client_secret: {got it while generating the token}
refresh_token: 1//0hr.... made with generate_refresh_token.py
# The following values configure the client for the service account flow.
path_to_private_key_file: ads.json
# delegated_account: INSERT_DOMAIN_WIDE_DELEGATION_ACCOUNT
#############################################################################
# ReportDownloader Headers #
# Below you may specify boolean values for optional headers that will be #
# applied to all requests made by the ReportDownloader utility by default. #
#############################################################################
# report_downloader_headers:
# skip_report_header: False
# skip_column_header: False
# skip_report_summary: False
# use_raw_enum_values: False
备注:
文件 ads.json 包含从 gcloud 中的凭据页面下载的私钥。
我看过一些关于这个问题的帖子,但其中 none 是 Python + GoogleAD,我在那里也找不到解决方案。
我也试过其他 Python + GoogleAds 示例得到同样的错误。这让我 认为我必须在 gcloud / google 广告中配置错误。但是我不明白什么。
请帮我查询我真的卡住了。
非常感谢!
@DazWilkin 的评论解决了我的问题。谢谢!