עקרונות בסיסיים בנושא דיווח

מבוא

במדריך הזה מוסבר איך להפעיל ולהוריד דוח באמצעות ה-API. הוא מתייחס לשתיהן באמצעות שאילתת דוח שמורה קיימת, וליצור שאילתת דוח אד-הוק.

דרישות מוקדמות

Primer

אם אתם לא יודעים איך מדווחים ב-Ad Manager, תוכלו לקרוא את המאמר יצירת דוח חדש כדי לקבל סקירה כללית על הפעלת דוח בממשק המשתמש של Ad Manager. בממשק המשתמש יש תצוגה מקדימה של הפלט, וכן הסברים קצרים שמסבירים איזה שילובים של עמודות ומאפיינים נתמכים. כשיוצרים שאילתת דוח מורכבת, יכול להיות שיהיה קל יותר ליצור צריך להציג אותו קודם בממשק המשתמש, ואז לאחזר את השאילתה באמצעות ה-API.

אחזור ReportQuery שנשמר

פונקציית ReportQuery האובייקט מכיל את כל פרטי הדוח. אפשר ליצור שאילתות של דוחות ב- בממשק המשתמש של Ad Manager, ומאחזרים אותם באמצעות ReportService.getSavedQueriesByStatement . מזהה השאילתה השמור נכלל בכתובת ה-URL כשמציגים שאילתה ממשק משתמש. לדוגמה, בכתובת האתר https://www.google.com/admanager/1234#reports/report/detail/report_id=456789 מזהה השאילתה הוא 456789.

אם שאילתה לא תואמת לגרסה של ה-API, הערך של SavedQuery.reportQuery יהיה null והערך של SavedQuery.isCompatibleWithApiVersion יהיה false.

אפשר להריץ שאילתות שמורות תואמות או ללא שינוי.

Java

    StatementBuilder statementBuilder =
        new StatementBuilder()
            .where("id = :id")
            .orderBy("id ASC")
            .limit(1)
            .withBindVariableValue("id", savedQueryId);

    SavedQueryPage page = reportService.getSavedQueriesByStatement(statementBuilder.toStatement());
    SavedQuery savedQuery = Iterables.getOnlyElement(Arrays.asList(page.getResults()));

    if (!savedQuery.getIsCompatibleWithApiVersion()) {
      throw new IllegalStateException("The saved query is not compatible with this API version.");
    }

    ReportQuery reportQuery = savedQuery.getReportQuery();
    

Python

  statement = (ad_manager.StatementBuilder(version='v202408')
               .Where('id = :id')
               .WithBindVariable('id', int(saved_query_id))
               .Limit(1))

  response = report_service.getSavedQueriesByStatement(
      statement.ToStatement())

  if 'results' in response and len(response['results']):
    saved_query = response['results'][0]

    if saved_query['isCompatibleWithApiVersion']:
      report_job = {}

      # Set report query and optionally modify it.
      report_job['reportQuery'] = saved_query['reportQuery']
    

PHP

      $statementBuilder = (new StatementBuilder())->where('id = :id')
          ->orderBy('id ASC')
          ->limit(1)
          ->withBindVariableValue('id', $savedQueryId);

      $savedQueryPage = $reportService->getSavedQueriesByStatement(
          $statementBuilder->toStatement()
      );
      $savedQuery = $savedQueryPage->getResults()[0];

      if ($savedQuery->getIsCompatibleWithApiVersion() === false) {
          throw new UnexpectedValueException(
              'The saved query is not compatible with this API version.'
          );
      }

      $reportQuery = $savedQuery->getReportQuery();
    

C#‎

StatementBuilder statementBuilder = new StatementBuilder()
    .Where("id = :id")
    .OrderBy("id ASC")
    .Limit(1)
    .AddValue("id", savedQueryId);

SavedQueryPage page =
    reportService.getSavedQueriesByStatement(statementBuilder.ToStatement());
SavedQuery savedQuery = page.results[0];

if (!savedQuery.isCompatibleWithApiVersion)
{
    throw new InvalidOperationException("Saved query is not compatible with this " +
        "API version");
}

// Optionally modify the query.
ReportQuery reportQuery = savedQuery.reportQuery;
    

Ruby

  statement = ad_manager.new_statement_builder do |sb|
    sb.where = 'id = :saved_query_id'
    sb.with_bind_variable('saved_query_id', saved_query_id)
  end

  saved_query_page = report_service.get_saved_queries_by_statement(
      statement.to_statement()
  )

  unless saved_query_page[:results].nil?
    saved_query = saved_query_page[:results].first

    if saved_query[:is_compatible_with_api_version]
      # Create report job.
      report_job = {:report_query => saved_query[:report_query]}
    else
      raise StandardError, 'Report query is not compatible with the API'
    end
    

כדי להריץ את השאילתה, ראו יצירת ReportJob.

פיתוח שאילתת דוח

בנוסף לשימוש בשאילתות שמורות, ניתן גם ליצור ReportQuery אד-הוק. לשם כך, צריך להגדיר את הפרמטרים הבאים מאפיינים, מאפיין המאפיינים, עמודות, סינון ו טווח תאריכים. הדוגמה הזו מתייחסת לדוח בסיסי על מסירה של הזמנה יחידה.

Java

    // Create report query.
    ReportQuery reportQuery = new ReportQuery();
    reportQuery.setDimensions(new Dimension[] {Dimension.DATE, Dimension.ORDER_ID});
    reportQuery.setColumns(
        new Column[] {
          Column.AD_SERVER_IMPRESSIONS,
          Column.AD_SERVER_CLICKS,
          Column.AD_SERVER_CTR,
          Column.AD_SERVER_CPM_AND_CPC_REVENUE
        });
    reportQuery.setDimensionAttributes(
        new DimensionAttribute[] {
          DimensionAttribute.ORDER_TRAFFICKER,
          DimensionAttribute.ORDER_START_DATE_TIME,
          DimensionAttribute.ORDER_END_DATE_TIME
        });

    // Create statement to filter for an order.
    StatementBuilder statementBuilder =
        new StatementBuilder()
            .where("ORDER_ID = :orderId")
            .withBindVariableValue("orderId", orderId);

    // Set the filter statement.
    reportQuery.setStatement(statementBuilder.toStatement());

    // Set the start and end dates or choose a dynamic date range type.
    reportQuery.setDateRangeType(DateRangeType.CUSTOM_DATE);
    reportQuery.setStartDate(
        DateTimes.toDateTime("2013-05-01T00:00:00", "America/New_York").getDate());
    reportQuery.setEndDate(
        DateTimes.toDateTime("2013-05-31T00:00:00", "America/New_York").getDate());
    

Python

  # Create statement object to filter for an order.
  statement = (ad_manager.StatementBuilder(version='v202408')
               .Where('ORDER_ID = :id')
               .WithBindVariable('id', int(order_id))
               .Limit(None)  # No limit or offset for reports
               .Offset(None))

  # Set the start and end dates of the report to run (past 8 days).
  end_date = datetime.now().date()
  start_date = end_date - timedelta(days=8)

  # Create report job.
  report_job = {
      'reportQuery': {
          'dimensions': ['ORDER_ID', 'ORDER_NAME'],
          'dimensionAttributes': ['ORDER_TRAFFICKER', 'ORDER_START_DATE_TIME',
                                  'ORDER_END_DATE_TIME'],
          'statement': statement.ToStatement(),
          'columns': ['AD_SERVER_IMPRESSIONS', 'AD_SERVER_CLICKS',
                      'AD_SERVER_CTR', 'AD_SERVER_CPM_AND_CPC_REVENUE',
                      'AD_SERVER_WITHOUT_CPD_AVERAGE_ECPM'],
          'dateRangeType': 'CUSTOM_DATE',
          'startDate': start_date,
          'endDate': end_date
      }
  }
    

PHP

      // Create report query.
      $reportQuery = new ReportQuery();
      $reportQuery->setDimensions(
          [
              Dimension::ORDER_ID,
              Dimension::ORDER_NAME
          ]
      );
      $reportQuery->setDimensionAttributes(
          [
              DimensionAttribute::ORDER_TRAFFICKER,
              DimensionAttribute::ORDER_START_DATE_TIME,
              DimensionAttribute::ORDER_END_DATE_TIME
          ]
      );
      $reportQuery->setColumns(
          [
              Column::AD_SERVER_IMPRESSIONS,
              Column::AD_SERVER_CLICKS,
              Column::AD_SERVER_CTR,
              Column::AD_SERVER_CPM_AND_CPC_REVENUE,
              Column::AD_SERVER_WITHOUT_CPD_AVERAGE_ECPM
          ]
      );

      // Create statement to filter for an order.
      $statementBuilder = (new StatementBuilder())
          ->where('ORDER_ID = :orderId')
          ->withBindVariableValue(
              'orderId',
              $orderId
          );

      // Set the filter statement.
      $reportQuery->setStatement($statementBuilder->toStatement());

      // Set the start and end dates or choose a dynamic date range type.
      $reportQuery->setDateRangeType(DateRangeType::CUSTOM_DATE);
      $reportQuery->setStartDate(
          AdManagerDateTimes::fromDateTime(
              new DateTime(
                  '-10 days',
                  new DateTimeZone('America/New_York')
              )
          )
              ->getDate()
      );
      $reportQuery->setEndDate(
          AdManagerDateTimes::fromDateTime(
              new DateTime(
                  'now',
                  new DateTimeZone('America/New_York')
              )
          )
              ->getDate()
      );
    

C#‎

// Create report job.
ReportJob reportJob = new ReportJob();
reportJob.reportQuery = new ReportQuery();
reportJob.reportQuery.dimensions = new Dimension[]
{
    Dimension.ORDER_ID,
    Dimension.ORDER_NAME
};
reportJob.reportQuery.dimensionAttributes = new DimensionAttribute[]
{
    DimensionAttribute.ORDER_TRAFFICKER,
    DimensionAttribute.ORDER_START_DATE_TIME,
    DimensionAttribute.ORDER_END_DATE_TIME
};
reportJob.reportQuery.columns = new Column[]
{
    Column.AD_SERVER_IMPRESSIONS,
    Column.AD_SERVER_CLICKS,
    Column.AD_SERVER_CTR,
    Column.AD_SERVER_CPM_AND_CPC_REVENUE,
    Column.AD_SERVER_WITHOUT_CPD_AVERAGE_ECPM
};

// Set a custom date range for the last 8 days
reportJob.reportQuery.dateRangeType = DateRangeType.CUSTOM_DATE;
System.DateTime endDateTime = System.DateTime.Now;
reportJob.reportQuery.startDate = DateTimeUtilities
    .FromDateTime(endDateTime.AddDays(-8), "America/New_York").date;
reportJob.reportQuery.endDate = DateTimeUtilities
    .FromDateTime(endDateTime, "America/New_York").date;

// Create statement object to filter for an order.
StatementBuilder statementBuilder = new StatementBuilder().Where("ORDER_ID = :id")
    .AddValue("id", orderId);
reportJob.reportQuery.statement = statementBuilder.ToStatement();
    

Ruby

  # Specify a report to run for the last 7 days.
  report_end_date = ad_manager.today()
  report_start_date = report_end_date - 7

  # Create statement object to filter for an order.
  statement = ad_manager.new_report_statement_builder do |sb|
    sb.where = 'ORDER_ID = :order_id'
    sb.with_bind_variable('order_id', order_id)
  end

  # Create report query.
  report_query = {
    :date_range_type => 'CUSTOM_DATE',
    :start_date => report_start_date.to_h,
    :end_date => report_end_date.to_h,
    :dimensions => ['ORDER_ID', 'ORDER_NAME'],
    :dimension_attributes => ['ORDER_TRAFFICKER', 'ORDER_START_DATE_TIME',
        'ORDER_END_DATE_TIME'],
    :columns => ['AD_SERVER_IMPRESSIONS', 'AD_SERVER_CLICKS', 'AD_SERVER_CTR',
        'AD_SERVER_CPM_AND_CPC_REVENUE', 'AD_SERVER_WITHOUT_CPD_AVERAGE_ECPM'],
    :statement => statement.to_statement()
  }
    

יצירת הדוח 'משימה'

לאחר יצירת ReportQuery, זה הזמן להריץ את הדוח. אובייקט ReportJob מכיל את הסטטוס של דוח ומודיע לך כשהוא מוכן להורדה. שפת תרגום להתחיל להריץ את הדוח, ReportService.runReportJob .

Java

    // Create report job.
    ReportJob reportJob = new ReportJob();
    reportJob.setReportQuery(reportQuery);

    // Run report job.
    reportJob = reportService.runReportJob(reportJob);
    

Python

  # Initialize a DataDownloader.
  report_downloader = client.GetDataDownloader(version='v202408')

  try:
    # Run the report and wait for it to finish.
    report_job_id = report_downloader.WaitForReport(report_job)
  except errors.AdManagerReportError as e:
    print('Failed to generate report. Error was: %s' % e)
    

PHP

      // Create report job and start it.
      $reportJob = new ReportJob();
      $reportJob->setReportQuery($reportQuery);
      $reportJob = $reportService->runReportJob($reportJob);
    

C#‎

// Run report job.
reportJob = reportService.runReportJob(reportJob);
    

Ruby

  # Create report job.
  report_job = {:report_query => report_query}

  # Run report job.
  report_job = report_service.run_report_job(report_job);
    

הורדת הדוח

אחרי שתפעילו את משימת הדוח, השרת יגדיר לה מזהה. שימוש בטיוטה הזו מזהה עם ReportService.getReportJobStatus כדי לבדוק את הסטטוס של הדוח. כשהסטטוס הוא ReportJobStatus.COMPLETED הדוח מוכן להורדה.

חלק מספריות הלקוח שלנו כוללות כלי עזר שמטרתם לבדוק את ה-API להמתין להשלמת הדוח. כשהדוח יסתיים, ניתן לקבל את להורדת כתובת ה-URL עם ReportService.getReportDownloadURL . אפשר להוריד דוח בפורמטים שונים. אם רוצים לעשות עיבוד מכונה נוסף בדוח, צריך להשתמש CSV_DUMP הפורמט.

Java

    // Create report downloader.
    ReportDownloader reportDownloader = new ReportDownloader(reportService, reportJob.getId());

    // Wait for the report to be ready.
    if (reportDownloader.waitForReportReady()) {
      // Change to your file location.
      File file = File.createTempFile("delivery-report-", ".csv.gz");

      System.out.printf("Downloading report to %s ...", file.toString());

      // Download the report.
      ReportDownloadOptions options = new ReportDownloadOptions();
      options.setExportFormat(ExportFormat.CSV_DUMP);
      options.setUseGzipCompression(true);
      URL url = reportDownloader.getDownloadUrl(options);
      Resources.asByteSource(url).copyTo(Files.asByteSink(file));

      System.out.println("done.");
    } else {
      System.out.printf("Report job %d failed.%n", reportJob.getId());
    }
    

Python

  # Change to your preferred export format.
  export_format = 'CSV_DUMP'

  report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)

  # Download report data.
  report_downloader.DownloadReportToFile(
      report_job_id, export_format, report_file)

  report_file.close()

  # Display results.
  print('Report job with id "%s" downloaded to:\n%s' % (
      report_job_id, report_file.name))
    

PHP

      // Create report downloader to poll report's status and download when
      // ready.
      $reportDownloader = new ReportDownloader(
          $reportService,
          $reportJob->getId()
      );
      if ($reportDownloader->waitForReportToFinish()) {
          // Write to system temp directory by default.
          $filePath = sprintf(
              '%s.csv.gz',
              tempnam(sys_get_temp_dir(), 'delivery-report-')
          );
          printf("Downloading report to %s ...%s", $filePath, PHP_EOL);
          // Download the report.
          $reportDownloader->downloadReport(
              ExportFormat::CSV_DUMP,
              $filePath
          );
          print "done.\n";
      } else {
          print "Report failed.\n";
      }
    

C#‎

ReportUtilities reportUtilities =
    new ReportUtilities(reportService, reportJob.id);

// Set download options.
ReportDownloadOptions options = new ReportDownloadOptions();
options.exportFormat = ExportFormat.CSV_DUMP;
options.useGzipCompression = true;
reportUtilities.reportDownloadOptions = options;

// Download the report.
using (ReportResponse reportResponse = reportUtilities.GetResponse())
{
    reportResponse.Save(filePath);
}

Console.WriteLine("Report saved to \"{0}\".", filePath);
    

Ruby

  MAX_RETRIES.times do |retry_count|
    # Get the report job status.
    report_job_status = report_service.get_report_job_status(report_job[:id])

    break unless report_job_status == 'IN_PROGRESS'
    puts 'Report with ID %d is still running.' % report_job[:id]
    sleep(RETRY_INTERVAL)
  end

  puts 'Report job with ID %d finished with status "%s".' % [report_job[:id],
      report_service.get_report_job_status(report_job[:id])]

  # Get the report URL.
  download_url = report_service.get_report_download_url(
      report_job_id, export_format
  )

  puts 'Downloading "%s" to "%s"...' % [download_url, file_name]
  open(file_name, 'wb') do |local_file|
    local_file << open(download_url).read()
  end
    

קריאת נתוני הדוח

רבות מספריות הלקוח שלנו כוללות כלים לקריאת נתוני דוחות. הדבר שימושיות לעיבוד נוסף של נתוני הדוח, או לשילוב דוחות מטווחי תאריכים שונים. שימו לב שבקוד לדוגמה ההנחה היא שהקובץ לא דחוס.

Java

  List<String[]> rows = CsvFiles.getCsvDataArray(filePath, true);
  for (String[] row : rows) {
    // Additional row processing
    processReportRow(row);
  }
    

Python

  with open(report_file.name, 'rb') as report:
    report_reader = csv.reader(report)
    for row in report_reader:
      # Additional row processing
      process_row(row)
    

PHP

  $report = fopen($filePath, 'r');
  while (!feof($report)) {
    // Additional row processing
    processRow(fgetcsv($report));
  }
  fclose($report);
    

C#‎

  CsvFile file = new CsvFile();
  file.Read(fileName, true);
  for (String[] row : file.Records) {
    // Additional row processing
    ProcessReportRow(row);
  }
    

Ruby

    CSV.foreach(file_name, converters: :numeric, headers: true) do |row|
      # Additional row processing
      process_row(row)
    end
    

לדוגמאות נוספות לדיווח, עבור אל חשבון לקוח ספריות ב-GitHub.

שאלות נפוצות

מדוע כל תוצאות הדוחות ברשת הבדיקה שלי ריקות?
רשתות בדיקה לא מציגות מודעות, ולכן דוחות המסירה לא יכללו נתונים.
מדוע כל תוצאות הדוח ברשת הייצור שלי ריקות?
יכול להיות שלמשתמש שבחרת לאמת אין גישה לנתונים מנסה לדווח עליו. אימות הרשאות תפקיד הצוותים מוגדרים בצורה נכונה.
למה מוצגת לי השגיאה ReportError.COLUMNS_NOT_SUPPORTED_FOR_REQUESTED_DIMENSIONS בדוח שלי?
לא כל השילובים של העמודות והמאפיינים נתמכים ב-Ad Manager. בדוחות מורכבים, קל יותר ליצור דוח תקין בממשק המשתמש ואז לאחזר אותו באמצעות השיטה ReportService.getSavedQueriesByStatement.
למה הדוח השמור שלי לא מוחזר ב-API?
עליך לוודא שהבעלים של הדוח שיתף את הדוח עם המשתמשך מתבצע אימות בתור.
למה הדוח השמור לא תואם ל-API?
תכונות דיווח מסוימות לא זמינות ב-API. המידע הזה כולל עמודות, מאפייני מאפיינים, מאפיינים וטווחי תאריכים. עבור סוגי טווח תאריכים שאינם תואמים, ניתן לשמור את הדוח עם סוג נתמך כדי תהיה אפשרות לאחזר אותו, ואז לשנות את ReportQuery כך שיתאים לטווח התאריכים הקבוע הרצוי.
למה הקליקים/החשיפות בכל משך החיים לא תואמים לדוח שלי בממשק המשתמש?
חשיפות בכל משך החיים חלות על כל משך החיים של הפריט, ללא קשר ל טווח התאריכים של הדוח. אם הפריט עדיין מוצג, הערך עשויות לעבור בין הרצת שני דוחות.
הדוחות שלי נוצרים באיחור ובחלק מהמקרים חל זמן קצוב לתפוגה. מה אפשר לעשות?
הקטנת טווח התאריכים או מספר המאפיינים תעזור לשפר את הביצועים או של ביצועים. במקום זאת, כדאי לנסות להריץ מספר דוחות בטווחי תאריכים קטנים יותר. שלך לאחר מכן למזג את נתוני הדוח כך שיכסו את טווח התאריכים הרצוי.
מה ההבדל בין עמודות INVENTORY_LEVEL לבין LINE_ITEM_LEVEL? באיזו אפשרות כדאי להשתמש?

אפשר להשתמש בעמודות עם LINE_ITEM_LEVEL רק אם יש לך רמת פריט מופעלת הקצאה דינמית ברשת שלך. העמודות האלה כוללות נתונים הקצאה דינמית ברמת הפריט ל-AdSense או ל-Ad Exchange. באופן דומה, העמודות INVENTORY_LEVEL כוללות נתונים מהקצאה דינמית ברמת המלאי. למידע נוסף על הקצאה דינמית, אפשר לעיין במאמר פריטים של Ad Exchange.

אם אתם עדיין לא בטוחים באילו עמודות API כדאי להשתמש, אפשר ליצור שאילתה שמורה ממשק המשתמש של Ad Manager ומאחזרים אותו באמצעות ReportService.getSavedQueriesByStatement .