API 常用操作

账户查询

获取账户层级结构

from google.ads.googleads.client import GoogleAdsClient

client = GoogleAdsClient.load_from_storage("google-ads.yaml")
ga_service = client.get_service("GoogleAdsService")

# 查询客户账户列表
query = """
    SELECT
        customer.id,
        customer.descriptive_name,
        customer.currency_code,
        customer.time_zone
    FROM customer
"""

response = ga_service.search(customer_id="1234567890", query=query)
for row in response:
    print(f"账户 ID: {row.customer.id}")
    print(f"账户名称: {row.customer.descriptive_name}")
    print(f"货币: {row.customer.currency_code}")
    print(f"时区: {row.customer.time_zone}")

获取广告系列列表

query = """
    SELECT
        campaign.id,
        campaign.name,
        campaign.status,
        campaign.advertising_channel_type,
        campaign_budget.amount_micros
    FROM campaign
    WHERE campaign.status != 'REMOVED'
    ORDER BY campaign.id
"""

response = ga_service.search(customer_id="1234567890", query=query)
for row in response:
    campaign = row.campaign
    budget = row.campaign_budget
    print(f"{campaign.id}: {campaign.name}")
    print(f"  状态: {campaign.status.name}")
    print(f"  类型: {campaign.advertising_channel_type.name}")
    print(f"  预算: {budget.amount_micros / 1_000_000:.2f}")

广告系列创建

创建搜索广告系列

from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.v16.services.types import CampaignOperation

client = GoogleAdsClient.load_from_storage("google-ads.yaml")
campaign_service = client.get_service("CampaignService")
campaign_budget_service = client.get_service("CampaignBudgetService")

# 1. 创建预算
budget_operation = client.get_type("CampaignBudgetOperation")
budget = budget_operation.create
budget.name = "每日预算 500元"
budget.delivery_method = client.enums.BudgetDeliveryMethodEnum.STANDARD
budget.amount_micros = 500_000_000  # 500元 = 500 * 1,000,000 micros

budget_response = campaign_budget_service.mutate_campaign_budgets(
    customer_id="1234567890",
    operations=[budget_operation]
)
budget_resource_name = budget_response.results[0].resource_name

# 2. 创建广告系列
campaign_operation = client.get_type("CampaignOperation")
campaign = campaign_operation.create
campaign.name = "搜索广告系列 - API 测试"
campaign.advertising_channel_type = client.enums.AdvertisingChannelTypeEnum.SEARCH
campaign.status = client.enums.CampaignStatusEnum.PAUSED  # 先暂停,审核后再启用
campaign.campaign_budget = budget_resource_name

# 出价策略
campaign.manual_cpc = client.get_type("ManualCpc")

# 网络设置
campaign.network_settings.target_google_search = True
campaign.network_settings.target_search_network = True
campaign.network_settings.target_content_network = False
campaign.network_settings.target_partner_search_network = False

# 投放时间
campaign.start_date = "20260620"
campaign.end_date = "20261231"

campaign_response = campaign_service.mutate_campaigns(
    customer_id="1234567890",
    operations=[campaign_operation]
)
print(f"创建成功: {campaign_response.results[0].resource_name}")

关键词批量操作

批量添加关键词

from google.ads.googleads.client import GoogleAdsClient

client = GoogleAdsClient.load_from_storage("google-ads.yaml")
ad_group_criterion_service = client.get_service("AdGroupCriterionService")

# 关键词列表(关键词 + 匹配类型)
keywords = [
    ("数字营销服务", "EXACT"),
    ("Google Ads 代理", "PHRASE"),
    ("搜索广告投放", "BROAD"),
    ("B2B 营销方案", "EXACT"),
    ("广告优化技巧", "PHRASE"),
]

operations = []
for keyword_text, match_type in keywords:
    operation = client.get_type("AdGroupCriterionOperation")
    criterion = operation.create
    criterion.ad_group = "customers/1234567890/adGroups/9876543210"
    criterion.keyword.text = keyword_text
    criterion.keyword.match_type = getattr(
        client.enums.KeywordMatchTypeEnum, match_type
    )
    criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED
    
    # 设置默认出价
    criterion.cpc_bid_micros = 2_000_000  # 2元
    
    operations.append(operation)

# 批量提交(每次最多 10,000 个)
response = ad_group_criterion_service.mutate_ad_group_criteria(
    customer_id="1234567890",
    operations=operations
)

print(f"成功添加 {len(response.results)} 个关键词")
for result in response.results:
    print(f"  - {result.resource_name}")

批量暂停低效关键词

query = """
    SELECT
        ad_group_criterion.criterion_id,
        ad_group_criterion.keyword.text,
        metrics.clicks,
        metrics.impressions,
        metrics.cost_micros,
        metrics.conversions
    FROM ad_group_criterion
    WHERE 
        ad_group_criterion.type = 'KEYWORD'
        AND segments.date DURING LAST_30_DAYS
    ORDER BY metrics.clicks ASC
"""

response = ga_service.search(customer_id="1234567890", query=query)

operations = []
for row in response:
    clicks = row.metrics.clicks
    impressions = row.metrics.impressions
    cost = row.metrics.cost_micros / 1_000_000
    conversions = row.metrics.conversions
    
    # 暂停条件:30天0点击 或 花费>100元但0转化
    if clicks == 0 and impressions > 1000:
        operation = client.get_type("AdGroupCriterionOperation")
        operation.remove = row.ad_group_criterion.resource_name
        operations.append(operation)
        print(f"暂停(无点击): {row.ad_group_criterion.keyword.text}")
    elif cost > 100 and conversions == 0:
        operation = client.get_type("AdGroupCriterionOperation")
        operation.remove = row.ad_group_criterion.resource_name
        operations.append(operation)
        print(f"暂停(高花费无转化): {row.ad_group_criterion.keyword.text}")

if operations:
    response = ad_group_criterion_service.mutate_ad_group_criteria(
        customer_id="1234567890",
        operations=operations
    )
    print(f"共暂停 {len(response.results)} 个关键词")

出价调整

基于转化成本自动调整出价

def adjust_bids_based_on_cpa(customer_id, target_cpa=50):
    """
    如果关键词的转化成本低于目标CPA,提高出价20%
    如果高于目标CPA,降低出价20%
    """
    query = """
        SELECT
            ad_group_criterion.criterion_id,
            ad_group_criterion.keyword.text,
            metrics.cost_micros,
            metrics.conversions,
            ad_group_criterion.cpc_bid_micros
        FROM ad_group_criterion
        WHERE 
            ad_group_criterion.type = 'KEYWORD'
            AND segments.date DURING LAST_30_DAYS
            AND metrics.conversions > 0
    """
    
    response = ga_service.search(customer_id=customer_id, query=query)
    
    operations = []
    for row in response:
        cost = row.metrics.cost_micros / 1_000_000
        conversions = row.metrics.conversions
        current_bid = row.ad_group_criterion.cpc_bid_micros
        
        actual_cpa = cost / conversions
        
        operation = client.get_type("AdGroupCriterionOperation")
        criterion = operation.update
        criterion.resource_name = row.ad_group_criterion.resource_name
        
        if actual_cpa < target_cpa * 0.8:
            # CPA 低于目标,提高出价
            new_bid = int(current_bid * 1.2)
            criterion.cpc_bid_micros = new_bid
            print(f"提高出价: {row.ad_group_criterion.keyword.text} "
                  f"({actual_cpa:.2f} < {target_cpa}) "
                  f"{current_bid/1e6:.2f} -> {new_bid/1e6:.2f}")
        elif actual_cpa > target_cpa * 1.2:
            # CPA 高于目标,降低出价
            new_bid = int(current_bid * 0.8)
            criterion.cpc_bid_micros = new_bid
            print(f"降低出价: {row.ad_group_criterion.keyword.text} "
                  f"({actual_cpa:.2f} > {target_cpa}) "
                  f"{current_bid/1e6:.2f} -> {new_bid/1e6:.2f}")
        else:
            continue
        
        # 指定更新字段
        client.copy_from(
            operation.update_mask,
            protobuf_helpers.field_mask(None, criterion._pb)
        )
        operations.append(operation)
    
    if operations:
        response = ad_group_criterion_service.mutate_ad_group_criteria(
            customer_id=customer_id,
            operations=operations
        )
        print(f"共调整 {len(response.results)} 个关键词出价")

# 执行
adjust_bids_based_on_cpa("1234567890", target_cpa=50)

报告生成

生成自定义性能报告

import pandas as pd
from datetime import datetime, timedelta

def generate_performance_report(customer_id, days=30):
    """生成过去 N 天的广告系列性能报告"""
    
    query = f"""
        SELECT
            segments.date,
            campaign.id,
            campaign.name,
            metrics.impressions,
            metrics.clicks,
            metrics.cost_micros,
            metrics.conversions,
            metrics.conversions_value
        FROM campaign
        WHERE 
            segments.date DURING LAST_{days}_DAYS
            AND campaign.status != 'REMOVED'
        ORDER BY segments.date DESC, metrics.cost_micros DESC
    """
    
    response = ga_service.search(customer_id=customer_id, query=query)
    
    data = []
    for row in response:
        data.append({
            '日期': row.segments.date,
            '广告系列ID': row.campaign.id,
            '广告系列名称': row.campaign.name,
            '展示次数': row.metrics.impressions,
            '点击次数': row.metrics.clicks,
            '花费(元)': row.metrics.cost_micros / 1_000_000,
            '转化次数': row.metrics.conversions,
            '转化价值': row.metrics.conversions_value,
            'CTR': f"{(row.metrics.clicks / row.metrics.impressions * 100):.2f}%" if row.metrics.impressions > 0 else "0%",
            'CPC': f"{(row.metrics.cost_micros / row.metrics.clicks / 1_000_000):.2f}" if row.metrics.clicks > 0 else "0"
        })
    
    df = pd.DataFrame(data)
    
    # 保存为 CSV
    filename = f"google_ads_report_{datetime.now().strftime('%Y%m%d')}.csv"
    df.to_csv(filename, index=False, encoding='utf-8-sig')
    print(f"报告已保存: {filename}")
    
    # 打印汇总
    print("\n=== 汇总数据 ===")
    print(f"总花费: {df['花费(元)'].sum():.2f} 元")
    print(f"总点击: {df['点击次数'].sum()}")
    print(f"总转化: {df['转化次数'].sum():.0f}")
    print(f"平均CPA: {df['花费(元)'].sum() / df['转化次数'].sum():.2f} 元" if df['转化次数'].sum() > 0 else "无转化")
    
    return df

# 生成报告
df = generate_performance_report("1234567890", days=30)

定时报告(使用 Airflow / Cron)

# daily_report.py - 每日定时执行
import schedule
import time

def job():
    print(f"[{datetime.now()}] 开始生成日报...")
    generate_performance_report("1234567890", days=1)
    print("日报生成完成")

# 每天上午 9 点执行
schedule.every().day.at("09:00").do(job)

while True:
    schedule.run_pending()
    time.sleep(60)

批量操作最佳实践

分批处理

def batch_mutate(service, customer_id, operations, batch_size=1000):
    """分批执行变异操作,避免超出限制"""
    results = []
    for i in range(0, len(operations), batch_size):
        batch = operations[i:i + batch_size]
        response = service.mutate(
            customer_id=customer_id,
            operations=batch
        )
        results.extend(response.results)
        print(f"处理批次 {i//batch_size + 1}/{(len(operations)-1)//batch_size + 1}")
    return results

错误处理

from google.ads.googleads.errors import GoogleAdsException

try:
    response = campaign_service.mutate_campaigns(
        customer_id="1234567890",
        operations=[campaign_operation]
    )
except GoogleAdsException as ex:
    print(f"请求失败: {ex.request_id}")
    for error in ex.failure.errors:
        print(f"错误码: {error.error_code}")
        print(f"消息: {error.message}")
        if error.location:
            for field_path_element in error.location.field_path_elements:
                print(f"字段: {field_path_element.field_name}")

下一步