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云端:

我使用这个脚本作为概念证明:

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)

当我执行脚本时出现此错误:

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 的评论解决了我的问题。谢谢!