أساسيات إعداد التقارير

مقدمة

يشرح لك هذا الدليل كيفية تشغيل تقرير وتنزيله باستخدام واجهة برمجة التطبيقات. فهو يشمل كلاً من استخدام طلب بحث محفوظ حالي وإنشاء طلب تقرير مخصّص.

المتطلبات الأساسية

  • الوصول إلى شبكة "مدير إعلانات Google" للإنتاج
  • مكتبة عملاء في "مدير الإعلانات"

Primer

إذا لم تكن معتادًا على إعداد التقارير في "مدير الإعلانات"، يمكنك الاطّلاع على إنشاء تقرير جديد للحصول على نظرة عامة حول كيفية تشغيل تقرير في واجهة مستخدم "مدير الإعلانات". وتحتوي واجهة المستخدم على معاينة للناتج، بالإضافة إلى تلميحات للتلميح توضّح مجموعات الأعمدة والمكونات المتاحة. عند إنشاء طلب بحث معقد في التقرير، قد يكون من الأسهل إنشاؤه في واجهة المستخدم أولاً، ثم استرداد طلب البحث باستخدام واجهة برمجة التطبيقات.

استرداد طلب بحث محفوظ

يحتوي الكائن ReportQuery على جميع تفاصيل التقرير. يمكنك إنشاء طلبات بحث للتقارير في واجهة مستخدم "مدير الإعلانات"، واستردادها باستخدام طريقة ReportService.getSavedQueriesByStatement. يتم تضمين معرّف طلب البحث المحفوظ في عنوان URL عند عرض طلب بحث في واجهة المستخدم. على سبيل المثال، في عنوان URL https://www.google.com/admanager/1234#reports/report/detail/report_id=456789 رقم تعريف طلب البحث هو 456789.

إذا كان طلب البحث غير متوافق مع إصدار واجهة برمجة التطبيقات، سيكون 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='v202402')
               .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.

إنشاء طلب بحث في التقارير

بالإضافة إلى استخدام طلبات البحث المحفوظة، يمكنك أيضًا إنشاء طلب بحث مخصّص في التقارير. لإجراء ذلك، يجب ضبط السمات وسمات السمات والأعمدة والفلتر والنطاق الزمني في التقرير. هذا المثال مخصص لتقرير تسليم أساسي لطلب واحد.

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='v202402')
               .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='v202402')

  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، يكون التقرير جاهزًا للتنزيل.

تحتوي بعض مكتبات العملاء على أدوات مساعدة مساعِدة من شأنها فحص واجهة برمجة التطبيقات وانتظار اكتمال التقرير. عند اكتمال التقرير، يمكنك الحصول على عنوان 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 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 في تقريري؟
ليست كل مجموعات الأعمدة والسمات متوافقة مع "مدير الإعلانات". بالنسبة إلى التقارير المعقدة، قد يكون من الأسهل إنشاء تقرير صالح في واجهة المستخدم ثم استرداده باستخدام الطريقة ReportService.getSavedQueriesByStatement.
لماذا لا يتم عرض تقريري المحفوظ في واجهة برمجة التطبيقات؟
تأكد من أن مالك التقرير قد شارك التقرير مع المستخدم الذي تجري المصادقة باسمه.
لماذا لا يتوافق التقرير المحفوظ مع واجهة برمجة التطبيقات؟
بعض ميزات إعداد التقارير غير متوفرة في واجهة برمجة التطبيقات. ويشمل ذلك الأعمدة وسمات السمات والسمات وأنواع النطاقات الزمنية. بالنسبة إلى أنواع النطاقات الزمنية غير المتوافقة، يمكنك حفظ التقرير باستخدام نوع متوافق لجعله قابلاً للاسترداد، ثم تغيير ReportQuery لكي يتوافق مع النطاق الزمني الثابت المطلوب
.
لماذا لا تتطابق النقرات/مرات الظهور الدائمة مع تقريري في واجهة المستخدم؟
تمثل مرات الظهور الدائمة مدى الحياة الكاملة للعنصر، بغض النظر عن النطاق الزمني للتقرير. إذا كان أحد العناصر لا يزال معروضًا، من المحتمل أن تتغير القيمة بين تشغيل أي تقريرين.
تستغرق تقاريري وقتًا طويلاً جدًا، وأحيانًا تنتهي مهلتها. What can I do?
سيساعد خفض النطاق الزمني أو عدد السمات على تحسين الأداء. جرِّب تشغيل عدة تقارير لنطاقات زمنية أصغر بدلاً من ذلك. يمكنك بعد ذلك دمج بيانات التقرير لتغطية النطاق الزمني المطلوب.
ما الفرق بين عمودَي "INVENTORY_LEVEL" و"LINE_ITEM_LEVEL"؟ أيُّ أداة يجب أن أستخدمها؟

لا يمكن استخدام الأعمدة التي تتضمّن LINE_ITEM_LEVEL إلا في حال تفعيل التخصيص الديناميكي على مستوى العنصر في شبكتك. تتضمّن هذه الأعمدة بيانات من التخصيص الديناميكي على مستوى العنصر إلى AdSense أو Ad Exchange. وبالمثل، تتضمّن أعمدة INVENTORY_LEVEL بيانات من التخصيص الديناميكي على مستوى المستودع الإعلاني. للحصول على مزيد من المعلومات عن التخصيص الديناميكي، يُرجى الاطّلاع على عناصر Ad Exchange.

إذا كنت لا تزال غير متأكد من أعمدة واجهة برمجة التطبيقات المطلوب استخدامها، أنشئ طلب بحث محفوظًا في واجهة مستخدم "مدير الإعلانات" واسترِده باستخدام الطريقة ReportService.getSavedQueriesByStatement.