You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用GA4 Admin API导出包含用户组、服务账号等所有类型权限主体的账号及媒体资源权限用户信息(Python脚本实现)

如何利用GA4 Admin API导出包含用户组、服务账号等所有类型权限主体的账号及媒体资源权限用户信息(Python脚本实现)

嗨,我来帮你搞定这个问题!你之前用accounts.accessBindings和properties.accessBindings接口只拿到了user类型的主体,其实是因为你可能没正确解析返回结果——这两个接口本身是会返回所有类型权限主体的,包括用户组、服务账号,只是它们的格式和USER类型不一样而已。下面我一步步给你讲怎么用Python脚本导出所有类型的权限信息,还能实现类似网页里的展开用户组成员的功能。

一、先搞清楚核心问题:为什么你之前只看到USER类型?

你提到在网页端能看到用户组、服务账号,但API返回里没注意到——其实GA4 Admin API返回的accessBinding对象里,member字段是类型:标识符的格式(比如group:marketing-team@yourdomain.com、serviceAccount:my-sa@project.iam.gserviceaccount.com),同时还有memberType字段直接标记主体类型(GROUP、SERVICE_ACCOUNT、USER)。你之前可能只过滤了USER类型,或者没拆分member字段,所以才只看到USER类型的结果。

二、准备工作

  • 确保你的服务账号(或认证账号)有GA4 Admin API的访问权限(比如Analytics Readonly或Analytics Admin Editor权限)
  • 安装必要的Python依赖:
    pip install google-analytics-admin google-auth google-api-python-client
    

三、基础版脚本:导出所有权限主体(含用户组、服务账号)

这个脚本会遍历你的所有GA4账号和媒体资源,导出所有类型的权限主体信息,包括用户、用户组、服务账号,最后保存为CSV文件。

from google.analytics.admin import AnalyticsAdminServiceClient
from google.oauth2 import service_account
import csv

# 替换成你的服务账号密钥文件路径
SERVICE_ACCOUNT_KEY_PATH = "path/to/your-service-account-key.json"

def authenticate():
    # 用服务账号认证
    credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_KEY_PATH,
        scopes=["https://www.googleapis.com/auth/analytics.readonly"]
    )
    return AnalyticsAdminServiceClient(credentials=credentials)

def fetch_account_permissions(client, account_id):
    """获取指定账号的所有权限绑定"""
    permissions = []
    # 调用账号权限接口
    request = client.list_account_access_bindings(parent=f"accounts/{account_id}")
    for binding in request:
        # 拆分member字段提取类型和主体标识
        member_parts = binding.member.split(":", 1)
        member_type = member_parts[0].upper()
        member_id = member_parts[1]
        permissions.append({
            "层级": "账号",
            "层级ID": account_id,
            "主体类型": member_type,
            "主体标识": member_id,
            "权限角色": binding.role,
            "显示名称": getattr(binding, "display_name", "无")
        })
    return permissions

def fetch_property_permissions(client, property_id):
    """获取指定媒体资源的所有权限绑定"""
    permissions = []
    # 调用媒体资源权限接口
    request = client.list_property_access_bindings(parent=f"properties/{property_id}")
    for binding in request:
        member_parts = binding.member.split(":", 1)
        member_type = member_parts[0].upper()
        member_id = member_parts[1]
        permissions.append({
            "层级": "媒体资源",
            "层级ID": property_id,
            "主体类型": member_type,
            "主体标识": member_id,
            "权限角色": binding.role,
            "显示名称": getattr(binding, "display_name", "无")
        })
    return permissions

def main():
    client = authenticate()
    all_permissions = []

    # 遍历所有GA4账号
    print("开始遍历所有账号...")
    for account in client.list_accounts():
        account_id = account.name.split("/")[-1]
        print(f"正在处理账号: {account.display_name} (ID: {account_id})")
        # 获取账号层级权限
        account_perms = fetch_account_permissions(client, account_id)
        all_permissions.extend(account_perms)
        # 遍历该账号下的所有媒体资源
        for prop in client.list_account_properties(parent=f"accounts/{account_id}"):
            prop_id = prop.name.split("/")[-1]
            print(f"  正在处理媒体资源: {prop.display_name} (ID: {prop_id})")
            # 获取媒体资源层级权限
            prop_perms = fetch_property_permissions(client, prop_id)
            all_permissions.extend(prop_perms)

    # 导出到CSV文件
    output_file = "ga4_all_permissions.csv"
    with open(output_file, "w", newline="", encoding="utf-8-sig") as f:
        fieldnames = ["层级", "层级ID", "主体类型", "主体标识", "权限角色", "显示名称"]
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(all_permissions)
    
    print(f"所有权限信息已成功导出到 {output_file}!")

if __name__ == "__main__":
    main()

脚本说明

  • 认证部分用服务账号密钥,确保密钥文件路径正确
  • 脚本会自动遍历所有账号和对应的媒体资源,不需要手动输入ID
  • 导出的CSV里会清晰显示每个权限主体的类型(USER/GROUP/SERVICE_ACCOUNT)、标识(邮箱)、所属层级、权限角色等信息

四、进阶版:展开用户组成员到具体用户

如果你需要像网页端那样“展开所有用户组到成员”,也就是把用户组替换成组内的具体用户,需要额外调用Google Workspace Admin SDK Directory API(需要Workspace管理员授权服务账号的相关权限)。

新增功能代码

在基础版脚本中添加以下函数:

from googleapiclient.discovery import build

def get_group_members(group_email, credentials):
    """获取指定Google Workspace用户组的所有成员"""
    members = []
    # 初始化Directory API服务
    service = build('admin', 'directory_v1', credentials=credentials)
    # 批量获取组成员
    results = service.members().list(groupKey=group_email).execute()
    members.extend(results.get('members', []))
    # 处理分页
    while 'nextPageToken' in results:
        page_token = results['nextPageToken']
        results = service.members().list(groupKey=group_email, pageToken=page_token).execute()
        members.extend(results.get('members', []))
    # 提取成员邮箱(过滤无效条目)
    return [member['email'] for member in members if member.get('email')]

然后修改fetch_account_permissions和fetch_property_permissions函数,在处理GROUP类型时展开成员:

def fetch_account_permissions(client, account_id, credentials=None):
    """获取指定账号的所有权限绑定,支持展开用户组成员"""
    permissions = []
    request = client.list_account_access_bindings(parent=f"accounts/{account_id}")
    for binding in request:
        member_parts = binding.member.split(":", 1)
        member_type = member_parts[0].upper()
        member_id = member_parts[1]
        
        # 如果是用户组且需要展开成员
        if member_type == "GROUP" and credentials:
            group_members = get_group_members(member_id, credentials)
            for member_email in group_members:
                permissions.append({
                    "层级": "账号",
                    "层级ID": account_id,
                    "主体类型": "USER",
                    "主体标识": member_email,
                    "权限角色": binding.role,
                    "显示名称": f"来自用户组: {binding.display_name}"
                })
            # 可选:保留用户组本身的权限条目
            permissions.append({
                "层级": "账号",
                "层级ID": account_id,
                "主体类型": "GROUP",
                "主体标识": member_id,
                "权限角色": binding.role,
                "显示名称": binding.display_name
            })
        else:
            permissions.append({
                "层级": "账号",
                "层级ID": account_id,
                "主体类型": member_type,
                "主体标识": member_id,
                "权限角色": binding.role,
                "显示名称": getattr(binding, "display_name", "无")
            })
    return permissions

# 同理修改fetch_property_permissions函数,逻辑和上面一致
def fetch_property_permissions(client, property_id, credentials=None):
    """获取指定媒体资源的所有权限绑定,支持展开用户组成员"""
    permissions = []
    request = client.list_property_access_bindings(parent=f"properties/{property_id}")
    for binding in request:
        member_parts = binding.member.split(":", 1)
        member_type = member_parts[0].upper()
        member_id = member_parts[1]
        
        # 如果是用户组且需要展开成员
        if member_type == "GROUP" and credentials:
            group_members = get_group_members(member_id, credentials)
            for member_email in group_members:
                permissions.append({
                    "层级": "媒体资源",
                    "层级ID": property_id,
                    "主体类型": "USER",
                    "主体标识": member_email,
                    "权限角色": binding.role,
                    "显示名称": f"来自用户组: {binding.display_name}"
                })
            # 可选:保留用户组本身的权限条目
            permissions.append({
                "层级": "媒体资源",
                "层级ID": property_id,
                "主体类型": "GROUP",
                "主体标识": member_id,
                "权限角色": binding.role,
                "显示名称": binding.display_name
            })
        else:
            permissions.append({
                "层级": "媒体资源",
                "层级ID": property_id,
                "主体类型": member_type,
                "主体标识": member_id,
                "权限角色": binding.role,
                "显示名称": getattr(binding, "display_name", "无")
            })
    return permissions

最后在main函数中,把认证后的credentials传递给权限获取函数:

def main():
    credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_KEY_PATH,
        scopes=["https://www.googleapis.com/auth/analytics.readonly", "https://www.googleapis.com/auth/admin.directory.group.member.readonly"]
    )
    client = AnalyticsAdminServiceClient(credentials=credentials)
    all_permissions = []

    print("开始遍历所有账号...")
    for account in client.list_accounts():
        account_id = account.name.split("/")[-1]
        print(f"正在处理账号: {account.display_name} (ID: {account_id})")
        # 传递credentials用于展开用户组
        account_perms = fetch_account_permissions(client, account_id, credentials)
        all_permissions.extend(account_perms)
        for prop in client.list_account_properties(parent=f"accounts/{account_id}"):
            prop_id = prop.name.split("/")[-1]
            print(f"  正在处理媒体资源: {prop.display_name} (ID: {prop_id})")
            prop_perms = fetch_property_permissions(client, prop_id, credentials)
            all_permissions.extend(prop_perms)
    # 导出逻辑不变
    output_file = "ga4_all_permissions.csv"
    with open(output_file, "w", newline="", encoding="utf-8-sig") as f:
        fieldnames = ["层级", "层级ID", "主体类型", "主体标识", "权限角色", "显示名称"]
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(all_permissions)
    
    print(f"所有权限信息已成功导出到 {output_file}!")

if __name__ == "__main__":
    main()

进阶版说明

  • 需要Workspace管理员给服务账号授权Admin Directory Group Member Readonly权限
  • 新增的get_group_members函数会批量获取用户组的所有成员,处理分页避免遗漏
  • 你可以选择是否保留用户组本身的权限条目,或者只保留展开后的用户条目

备注:内容来源于stack exchange,提问作者THITINUN CHOTIGAVANICH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 14:19:33