Yêu cầu không đồng bộ

API Báo cáo Search Ads 360 mới hiện đã ra mắt. API mới mang đến khả năng linh hoạt hơn nữa trong việc xây dựng các báo cáo tuỳ chỉnh và tích hợp dữ liệu vào các ứng dụng và quy trình báo cáo. Hãy tìm hiểu thêm về việc di chuyển sang và sử dụng API Báo cáo Search Ads 360 mới.

Vì có thể mất một chút thời gian để tạo yêu cầu cho báo cáo lớn, nên API Search Ads 360 cung cấp một kỹ thuật không đồng bộ để yêu cầu và tải báo cáo xuống. Với kỹ thuật này, bạn sẽ gửi một yêu cầu ban đầu chỉ định dữ liệu mà bạn muốn có trong báo cáo, sau đó gửi các yêu cầu thăm dò bổ sung cho đến khi Search Ads 360 tạo xong báo cáo. Tuỳ thuộc vào kích thước của báo cáo, Search Ads 360 sẽ chia dữ liệu thành nhiều tệp. Sau khi báo cáo được tạo, bạn sẽ gửi yêu cầu tải từng tệp báo cáo xuống. Nếu yêu cầu lượng dữ liệu nhỏ, bạn có thể chỉ gửi một yêu cầu đồng bộ.

Để tạo yêu cầu không đồng bộ

  1. Gọi Reports.request() để chỉ định loại dữ liệu bạn muốn đưa vào báo cáo. Hãy xem phần Các loại báo cáo để biết các loại dữ liệu mà bạn có thể yêu cầu.

    Search Ads 360 xác thực yêu cầu và trả về một mã báo cáo. Đây là giá trị nhận dạng duy nhất của yêu cầu này.

  2. Gọi Reports.get() bằng mã báo cáo.

    Nội dung phản hồi của Search Ads 360 cho biết:

    • Liệu báo cáo đã sẵn sàng để tải xuống hay chưa.
    • Nếu báo cáo đã sẵn sàng, một hoặc nhiều URL để tải báo cáo xuống.
  3. Gọi Reports.getFile() để tải các tệp báo cáo được mã hoá xuống hoặc chỉ cần tải trực tiếp từ các URL xuống.

    Search Ads 360 trả về báo cáo dưới dạng tệp được mã hoá UTF-8.

Tôi nên thăm dò các báo cáo với tần suất như thế nào để xem các báo cáo đã sẵn sàng hay chưa?

Thời gian mà Search Ads 360 cần để tạo báo cáo chủ yếu phụ thuộc vào lượng dữ liệu trong báo cáo đó. Hãy thử thăm dò trạng thái báo cáo một lần mỗi phút, sau đó điều chỉnh tần suất nếu yêu cầu báo cáo trung bình của bạn mất nhiều hơn hoặc ít thời gian hơn đáng kể để hoàn thành.

Tách các báo cáo không đồng bộ thành nhiều tệp

Để phản hồi một yêu cầu không đồng bộ, Search Ads 360 sẽ tự động chia các báo cáo lớn thành nhiều tệp. Sử dụng thuộc tính Reports.request.maxRowsPerFile để chỉ định kích thước tệp tối đa. Mỗi tệp báo cáo thu được đảm bảo sẽ có tối đa maxRowsPerFile hàng báo cáo (không tính tiêu đề). Một URL khác được tạo cho mỗi tệp và trả về để phản hồi Reports.get(). Để biết thông tin về cách tải tệp báo cáo xuống, hãy xem phần Tải báo cáo xuống.

Đối với báo cáo CSV, tiêu đề được lặp lại trong mỗi tệp.

Lưu ý: API không hỗ trợ tính năng phân trang cho các yêu cầu không đồng bộ. Tức là bạn không thể chỉ định hàng nào trong tệp báo cáo mà bạn muốn. Cách các hàng được phân chia giữa các tệp là tuỳ ý và có thể thay đổi nếu cùng một báo cáo được chạy lại.

Ví dụ không đồng bộ

Dưới đây là các yêu cầu và phản hồi mẫu sử dụng kỹ thuật không đồng bộ.

JSON

POST  https://www.googleapis.com/doubleclicksearch/v2/reports
Authorization: Bearer your OAuth 2.0 access token
Content-type: application/json

{
  "reportScope": {
    "agencyId": "12300000000000456", // Replace with your ID
    "advertiserId": "21700000000011523", // Replace with your ID
  },
  "reportType": "keyword",              // This report covers all keywords in the
                                        // advertiser specified in reportScope.
  "columns": [
    { "columnName": "campaignId" },     // Here are some attribute columns available for keyword
    { "columnName": "keywordText" },    // reports.
    { "columnName": "keywordLandingPage" },

    { "columnName": "date" },           // The date column segments the report by individual days.

    { "columnName": "dfaRevenue" },     // Here are some metric columns available for keyword
    {                                   // reports
      "columnName": "visits",
      "startDate": "2013-01-01",        // Each metric column can optionally specify its own start
      "endDate": "2013-01-31",          // and end date; by default the report timeRange is used.
      "headerText": "visits last month" // Every column can optionally specify a headerText, which
                                        // changes the name of the column in the report.
    }
  ],
  "timeRange" : {
    "startDate" : "2012-05-01",         // Dates are inclusive and specified in YYYY-MM-DD format.
    "endDate" : "2012-05-02"

    // Alternatively, try the "changedMetricsSinceTimestamp" or "changedAttributesSinceTimestamp"
    // options. See Incremental reports.

  },
  "filters": [
    {
      "column" : { "columnName": "keywordLandingPage" },
      "operator" : "startsWith",
      "values" : [                      // With this filter, only keywords with landing pages
        "http://www.foo.com",           // rooted at www.foo.com or www.bar.com are returned.
        "http://www.bar.com"            // See Filtered reports.
      ]
    }
  ],
  "downloadFormat": "csv",
  "maxRowsPerFile": 6000000,            // Required. See Splitting reports into multiple files.
  "statisticsCurrency": "agency",       // Required. See Currency for statistics.
  "verifySingleTimeZone": false,        // Optional. Defaults to false. See Time zone.
  "includeRemovedEntities": false           // Optional. Defaults to false.
}
          

Java

/**
 * Creates a campaign report request, submits the report, and returns the report ID.
 */
private static String createReport(Doubleclicksearch service) throws IOException {
  try {
     return service.reports().request(createSampleRequest()).execute().getId();
  } catch (GoogleJsonResponseException e) {
    System.err.println("Report request was rejected.");
    for (ErrorInfo error : e.getDetails().getErrors()) {
      System.err.println(error.getMessage());
    }
    System.exit(e.getStatusCode());
    return null; // Unreachable code.
  }
}

/**
 * Creates a simple static request that lists the ID and name of all
 * campaigns under agency 12300000000000456 and advertiser 21700000000011523.
 * Substitute your own agency ID and advertiser IDs for the IDs in this sample.
 */
private static ReportRequest createSampleRequest() {
  return new ReportRequest()
      .setReportScope(new ReportScope()
          .setAgencyId(12300000000000456L) // Replace with your ID
          .setAdvertiserId(21700000000011523L)) // Replace with your ID
      .setReportType("campaign")
      .setColumns(Arrays.asList(
          new ReportApiColumnSpec[] {
            new ReportApiColumnSpec().setColumnName("campaignId"),
            new ReportApiColumnSpec().setColumnName("campaign")
          }))
      .setTimeRange(new TimeRange()
          .setStartDate("2012-05-01")
          .setEndDate("2012-05-01"))
      .setDownloadFormat("csv")
      .setStatisticsCurrency("usd")
      .setMaxRowsPerFile(5000000);
}

.NET

Hàm này tạo một báo cáo liệt kê các chiến dịch của một nhà quảng cáo và chỉ định mã thông báo được trả về cho reportId.
using api = Google.Apis.Doubleclicksearch.v2;

/// <summary>
/// Creates a report with a sample request and returns the report ID.
/// </summary>
/// <param name="service">Search Ads 360 API service.</param>
private static string CreateReport(api.DoubleclicksearchService service)
{

    var req = service.Reports.Request(CreateSampleRequest());
    var report = req.Execute();
    Console.WriteLine("Created report: ID={0}", report.Id);
    return report.Id;
}

/// <summary>
/// Returns a simple static request that lists the ID and name of all
/// campaigns under an advertiser.
/// Substitute your own agency ID and advertiser IDs for the IDs in this sample.
/// </summary>
private static api.Data.ReportRequest CreateSampleRequest()
{
    return new api.Data.ReportRequest
    {
        ReportScope = new api.Data.ReportRequest.ReportScopeData
        {
            AgencyId = 12300000000000456, // Replace with your ID
            AdvertiserId = 21700000000011523 // Replace with your ID
        },
        ReportType = ReportType.CAMPAIGN,
        Columns = new List<api.Data.ReportApiColumnSpec>
        {
            new api.Data.ReportApiColumnSpec
            {
                ColumnName = "campaignId",
            },
            new api.Data.ReportApiColumnSpec
            {
                ColumnName = "campaign",
            },
        },
        TimeRange = new api.Data.ReportRequest.TimeRangeData
        {
            StartDate = "2015-01-01",
            EndDate = "2015-01-07",
        },
        DownloadFormat = "csv",
        StatisticsCurrency = "usd",
        MaxRowsPerFile = 5000000,
    };
}

Python

def request_report(service):
  """Request sample report and print the report ID that DS returns. See Set Up Your Application.

  Args:
    service: An authorized Doubleclicksearch service.
  Returns:
    The report id.
  """
  request = service.reports().request(
      body=
      {
        "reportScope": {
            "agencyId": "12300000000000456", // Replace with your ID
            "advertiserId": "21700000000011523", // Replace with your ID
            "engineAccountId": "700000000073991" // Replace with your ID
            },
        "reportType": "keyword",
        "columns": [
            { "columnName": "campaignId" },
            { "columnName": "keywordText" },
            { "columnName": "keywordLandingPage" },

            { "columnName": "date" },

            { "columnName": "dfaRevenue" },
            {
              "columnName": "visits",
              "startDate": "2013-01-01",
              "endDate": "2013-01-31",
              "headerText": "visits last month"
            }
          ],
          "timeRange" : {
            "startDate" : "2012-05-01",
            "endDate" : "2012-05-02"
          },
          "filters": [
            {
              "column" : { "columnName": "keywordLandingPage" },
              "operator" : "startsWith",
              "values" : [
                "http://www.foo.com",
                "http://www.bar.com"
              ]
            }
          ],
          "downloadFormat": "csv",
          "maxRowsPerFile": 6000000,
          "statisticsCurrency": "agency",
          "verifySingleTimeZone": "false",
          "includeRemovedEntities": "false"
        }
  )

  json_data = request.execute()
  return json_data['id']

Nếu xác thực thành công

Nếu báo cáo vượt qua quy trình xác thực, Search Ads 360 sẽ trả về một mã báo cáo. Search Ads 360 cũng trả về mã đơn vị tiền tệ và múi giờ liên quan đến siêu dữ liệu.

{
  "kind": "adsdartsearch#report",
  "id": "MTMyNDM1NDYK",                      // This is the report id.
  "isReportReady": false,                    // The report is not finished generating.

  "request": {                               // The request that created this report.
    ...
  },

  "statisticsCurrencyCode": "CAD",           // The currency used for statistics. E.g., if
                                             // advertiser currency was requested, this would be
                                             // currency code of the advertiser in scope.

  "statisticsTimeZone": "America/New_York"   // If all statistics in the report were sourced from
                                             // a single time zone, this would be it. If
                                             // verifySingleTimeZone was set to true in the request,
                                             // then this field will always be populated (or the
                                             // request will fail).
}
      

Nếu xác thực thất bại

Nếu báo cáo không vượt qua được quy trình xác thực, Search Ads 360 sẽ trả về phản hồi HTTP 400 kèm theo đối tượng lỗi. Ví dụ: yêu cầu trong ví dụ ở trên không chỉ định một đại lý thực tế:

{
 "error": {
   "code": 400,
   "message": "statisticsCurrency: the agency in scope does not have a valid currency. Please make sure the agency is properly initialized in Search Ads 360."
 }
}

Thăm dò ý kiến về trạng thái báo cáo

Gọi Report.Get() bằng mã báo cáo.

JSON

GET https://www.googleapis.com/doubleclicksearch/v2/reports/MTMyNDM1NDYK
          

Java

/**
 * Polls the reporting API with the reportId until the report is ready.
 * Returns the report.
 */
private static Report pollUntilReportIsFinished(Doubleclicksearch service, String reportId)
    throws IOException, InterruptedException {
  long delay = 1;
  while (true) {
    Report report = null;
    try {
      report = service.reports().get(reportId).execute();
    } catch (GoogleJsonResponseException e) {
      System.err.println("Report generation has failed.");
      System.exit(e.getStatusCode());
    }
    if (report.getIsReportReady()) {
      return report;
    }
    System.out.format("Report %s is not ready - waiting %s seconds.%n", reportId, delay);
    Thread.sleep(TimeUnit.SECONDS.toMillis(delay));
    delay = delay + delay; // Double the delay for the next iteration.
  }
}

.NET

using api = Google.Apis.Doubleclicksearch.v2;

/// <summary>
/// Polls until the report with the given ID completes.
/// </summary>
/// <param name="service">Search Ads 360 API service.</param>
/// <param name="reportId">Report ID to poll.</param>
/// <exception cref="ApplicationException">
/// Thrown when the report completes, but has failed.
/// </exception>
private static api.Data.Report PollUntilReportIsFinished(
    api.DoubleclicksearchService service,
    string reportId)
{
    TimeSpan delay = TimeSpan.FromSeconds(1);
    while (true)
    {
        api.Data.Report report;
        try
        {
            report = service.Reports.Get(reportId).Execute();
        }
        catch (Google.GoogleApiException ex)
        {
            throw new ApplicationException("Report generation failed", ex);
        }
        if (report.IsReportReady.GetValueOrDefault(false))
        {
            return report;
        }
        Console.WriteLine("Report is not ready - waiting {0}", delay);
        Thread.Sleep(delay);
        delay = delay.Add(delay); // Double the delay for the next iteration.
    }
}

Python

import pprint
import simplejson
from googleapiclient.errors import HttpError

def poll_report(service, report_id):
  """Poll the API with the reportId until the report is ready, up to ten times.

  Args:
    service: An authorized Doubleclicksearch service.
    report_id: The ID DS has assigned to a report.
  """
  for _ in xrange(10):
    try:
      request = service.reports().get(reportId=report_id)
      json_data = request.execute()
      if json_data['isReportReady']:
        pprint.pprint('The report is ready.')

        # For large reports, DS automatically fragments the report into multiple
        # files. The 'files' property in the JSON object that DS returns contains
        # the list of URLs for file fragment. To download a report, DS needs to
        # know the report ID and the index of a file fragment.
        for i in range(len(json_data['files'])):
          pprint.pprint('Downloading fragment ' + str(i) + ' for report ' + report_id)
          download_files(service, report_id, str(i)) # See Download the report.
        return

      else:
        pprint.pprint('Report is not ready. I will try again.')
        time.sleep(10)
    except HttpError as e:
      error = simplejson.loads(e.content)['error']['errors'][0]

      # See Response Codes
      pprint.pprint('HTTP code %d, reason %s' % (e.resp.status, error['reason']))
      break

Nếu báo cáo chưa sẵn sàng

Nếu báo cáo chưa sẵn sàng, Search Ads 360 sẽ trả về phản hồi HTTP 202 và trường isReportReady là false.

{
  "kind": "doubleclicksearch#report",
  "id": "MTMyNDM1NDYK",
  "isReportReady": false,
  "request": {
    ...
  },
  ...
}
      

Thời điểm báo cáo sẵn sàng

Khi báo cáo đã hoàn tất và sẵn sàng để tải xuống, trường isReportReady là đúng. Phản hồi này cũng có một trường bổ sung là files, chứa các URL để tải các tệp báo cáo xuống.

{
  "kind": "doubleclicksearch#report",
  "id": "MTMyNDM1NDYK",
  "isReportReady": true,
  "request": {
    ...
  },
  ...
  "rowCount": 1329,        // Total rows in the report, not counting headers.
  "files": [
    {
      "url": "https://www.googleapis.com/doubleclicksearch/v2/reports/MTMyNDM1NDYK/files/0"
      "byteCount": "10242323"
    },
    {
      "url": "https://www.googleapis.com/doubleclicksearch/v2/reports/MTMyNDM1NDYK/files/1"
      "byteCount": "10242323"
    }
  ],
}
      

Nếu tạo báo cáo không thành công

Nếu không thể tạo báo cáo, Search Ads 360 sẽ trả về một trong các mã lỗi HTTP cùng với nội dung mô tả.

{
  "error" : {
   "code" : 410,            // Or other error codes.
   "message" : "Processing was halted on the backend and will not continue."
  }
}
      

Hãy xem phần Mã phản hồi để biết danh sách các lỗi mà Search Ads 360 có thể trả về.

Tải báo cáo xuống

Bạn có thể tải báo cáo xuống bằng cách nhấn trực tiếp từng URL tệp báo cáo hoặc bằng cách gọi Reports.getFile() kèm theo mã báo cáo và số tệp (0 đã được lập chỉ mục). Search Ads 360 trả về báo cáo dưới dạng tệp được mã hoá UTF-8.

Dưới đây là ví dụ về yêu cầu Reports.getFile():

JSON

GET https://www.googleapis.com/doubleclicksearch/v2/reports/MTMyNDM1NDYK/files/0?alt=media
GET https://www.googleapis.com/doubleclicksearch/v2/reports/MTMyNDM1NDYK/files/1?alt=media
          

Java

/**
 * Downloads the shards of a completed report to the given local directory.
 * Files are named CampaignReport0.csv, CampaignReport1.csv, and so on.
 */
private static void downloadFiles(
    Doubleclicksearch service,
    Report report,
    String localPath) throws IOException {
  for (int i = 0; i < report.getFiles().size(); i++) {
    FileOutputStream outputStream =
        new FileOutputStream(new File(localPath, "CampaignReport" + i));
    service.reports().getFile(report.getId(), i).executeAndDownloadTo(outputStream);
    outputStream.close();
  }
}

.NET

using api = Google.Apis.Doubleclicksearch.v2;

/// <summary>
/// Downloads the shards of a completed report to the given local directory.
/// Files are named CampaignReport0.csv, CampaignReport1.csv, and so on.
/// </summary>
/// <param name="service">Search Ads 360 API service.</param>
/// <param name="report">Report ID to download.</param>
/// <param name="localPath">Path of the directory to place downloaded files.</param>
private static void DownloadFiles(
    api.DoubleclicksearchService service,
    api.Data.Report report,
    string localPath)
{
    Directory.CreateDirectory(localPath);
    for (int i = 0; i < report.Files.Count; ++i)
    {
        string fileName = Path.Combine(
            localPath,
            string.Format("CampaignReport{0}.csv", i));
        Console.WriteLine("Downloading shard {0} to {1}", i, fileName);
        using (Stream dst = File.OpenWrite(fileName))
        {
            service.Reports.GetFile(report.Id, i).Download(dst);
        }
    }
}

Python

def download_files(service, report_id, report_fragment):
  """Generate and print sample report.

  Args:
    service: An authorized Doubleclicksearch service.
    report_id: The ID DS has assigned to a report.
    report_fragment: The 0-based index of the file fragment from the files array.
  """
  f = file('report-' + report_fragment + '.csv', 'w')
  request = service.reports().getFile_media(reportId=report_id, reportFragment=report_fragment)
  f.write(request.execute().decode('utf-8'))
  f.close()

Báo cáo mẫu

Dưới đây là ví dụ về báo cáo CSV. Mỗi mảnh tệp báo cáo có hàng tiêu đề riêng. Để biết thông tin chi tiết về định dạng này, vui lòng xem RFC 4180.

keywordText,campaignId,landingPageUrl,day,revenue,visits last month,my revenue
google,71700000002104742,http://www.google.com,2012-05-01,10.2,5,20
google,71700000002104742,http://www.google.com,2012-05-02,11,10,60.23
      

Tải tệp ánh xạ mã nhận dạng xuống

Bạn có thể tải tệp chứa các liên kết mã nhận dạng giữa Search Ads 360 cũ và Search Ads 360 mới xuống. Đối với nhà quảng cáo được yêu cầu, tệp này bao gồm tất cả các thực thể con (ví dụ: tài khoản công cụ, chiến dịch, nhóm quảng cáo, v.v.) tồn tại trong cả Search Ads 360 cũ và Search Ads 360 mới.

Python

def download_mapping_file(service, file_name, agency_id, advertiser_id):
    """Generate and save mapping file to a csv.

    Args:
      service: An authorized Doubleclicksearch service.
      file_name: Filename to write the ID mapping file.
      agency_id: DS ID of the agency.
      advertiser_id: DS ID of the advertiser.
    """
    request = service.reports().getIdMappingFile_media(agencyId=agency_id,
        advertiserId=advertiser_id)

    response = request.execute()
    response = response.decode('utf-8')

    f = open(file_name + '.csv', 'w')
    f.write(response)
    f.close()