Bing Ads API PHP Report Request without download
Asked Answered
N

1

6

Is there a way to get the data from a report request without downloading a file? The google adwords api allows the api to return the report in a tab delimited string which I can then dissect myself without ever making a file.

I am currently using the Bing API to get a Campaign Performance Report through the CampaignPerformancReportRequest class. I want to take the data and put it into our SQL database. The file is unnecessary. Now yes, I know that once the file is created, I could always use php to open the file and get the data from it, but honestly, this is tedious, and takes extra processing power for no reason. Can anyone help?

Here's a copy of the code in case someone needs a working Campaign Performance Report in PHP that downloads the report.

<?php

// Include the Bing Ads namespaced class file available
// for download at http://go.microsoft.com/fwlink/?LinkId=322147
include '..\..\includes\bing\ReportingClasses.php';
include '..\..\includes\bing\ClientProxy.php';

// Specify the BingAds\Reporting objects that will be used.
use BingAds\Reporting\SubmitGenerateReportRequest;
use BingAds\Reporting\CampaignPerformanceReportRequest;
use BingAds\Reporting\ReportFormat;
use BingAds\Reporting\ReportAggregation;
use BingAds\Reporting\AccountThroughAdGroupReportScope;
use BingAds\Reporting\CampaignReportScope;
use BingAds\Reporting\ReportTime;
use BingAds\Reporting\ReportTimePeriod;
use BingAds\Reporting\Date;
use BingAds\Reporting\CampaignPerformanceReportFilter;
use BingAds\Reporting\DeviceTypeReportFilter;
use BingAds\Reporting\CampaignPerformanceReportColumn;
use BingAds\Reporting\PollGenerateReportRequest;
use BingAds\Reporting\ReportRequestStatusType;
// use BingAds\Reporting\CampaignPerformanceReportSort;
use BingAds\Reporting\SortOrder;

// Specify the BingAds\Proxy object that will be used.
use BingAds\Proxy\ClientProxy;

// Disable WSDL caching.

ini_set("soap.wsdl_cache_enabled", "0");
ini_set("soap.wsdl_cache_ttl", "0");

// Specify your credentials.

$UserName = "XXX";          
$Password = "XXX";          
$DeveloperToken = "XXX"; 
$AccountId = XXX;
$CampaignId = XXX;


// Reporting WSDL.

$wsdl = "https://api.bingads.microsoft.com/Api/Advertiser/Reporting/V9/ReportingService.svc?singleWsdl";


// Specify the file to download the report to. Because the file is
// compressed use the .zip file extension.

$DownloadPath = "c:\\keywordperf.zip";

// Confirm that the download folder exist; otherwise, exit.

$length = strrpos($DownloadPath, '\\');
$folder = substr($DownloadPath, 0, $length);

if (!is_dir($folder))
{
    printf("The output folder, %s, does not exist.\nEnsure that the " .
        "folder exists and try again.", $folder);
    return;
}

try
{
    $proxy = ClientProxy::ConstructWithAccountId($wsdl, $UserName, $Password, $DeveloperToken, $AccountId, null);

    // Build a keyword performance report request, including Format, ReportName, Aggregation,
    // Scope, Time, Filter, and Columns.

    $report = new CampaignPerformanceReportRequest();

    $report->Format = ReportFormat::Tsv;
    $report->ReportName = 'My Keyword Performance Report';
    $report->ReturnOnlyCompleteData = false;
    $report->Aggregation = ReportAggregation::Daily;

    $report->Scope = new AccountThroughAdGroupReportScope();
    $report->Scope->AccountIds = null;
    $report->Scope->AdGroups = null;
    $report->Scope->Campaigns = null;
    // $campaignReportScope = new CampaignReportScope();
    // $campaignReportScope->CampaignId = $CampaignId;
    // $campaignReportScope->AccountId = $AccountId;
    // $report->Scope->Campaigns[] = $campaignReportScope;

    $report->Time = new ReportTime();
    $report->Time->PredefinedTime = ReportTimePeriod::Yesterday;

    //  You may either use a custom date range or predefined time.
    //    $report->Time->CustomDateRangeStart = new Date();
    //    $report->Time->CustomDateRangeStart->Month = 2;
    //    $report->Time->CustomDateRangeStart->Day = 1;
    //    $report->Time->CustomDateRangeStart->Year = 2012;
    //    $report->Time->CustomDateRangeEnd = new Date();
    //    $report->Time->CustomDateRangeEnd->Month = 2;
    //    $report->Time->CustomDateRangeEnd->Day = 15;
    //    $report->Time->CustomDateRangeEnd->Year = 2012;

    // $report->Filter = new CampaignPerformanceReportFilter();
    // $report->Filter->DeviceType = array (
            // DeviceTypeReportFilter::Computer,
            // DeviceTypeReportFilter::SmartPhone
    // );

    $report->Columns = array (
            CampaignPerformanceReportColumn::TimePeriod,
            CampaignPerformanceReportColumn::AccountId,
            CampaignPerformanceReportColumn::CampaignName,
            CampaignPerformanceReportColumn::CampaignId,

            CampaignPerformanceReportColumn::Clicks,
            CampaignPerformanceReportColumn::Impressions,
            CampaignPerformanceReportColumn::Ctr,
            CampaignPerformanceReportColumn::AverageCpc,
            CampaignPerformanceReportColumn::Spend,
            CampaignPerformanceReportColumn::QualityScore
    );

    // You may optionally sort by any CampaignPerformanceReportColumn, and optionally
    // specify the maximum number of rows to return in the sorted report.

    // $report->Sort = array ();
    // $CampaignPerformanceReportSort = new CampaignPerformanceReportSort();
    // $CampaignPerformanceReportSort->SortColumn = CampaignPerformanceReportColumn::Clicks;
    // $CampaignPerformanceReportSort->SortOrder = SortOrder::Ascending;
    // $report->Sort[] = $CampaignPerformanceReportSort;

    $report->MaxRows = 10;

    $encodedReport = new SoapVar($report, SOAP_ENC_OBJECT, 'CampaignPerformanceReportRequest', $proxy->GetNamespace());

    // SubmitGenerateReport helper method calls the corresponding Bing Ads service operation
    // to request the report identifier. The identifier is used to check report generation status
    // before downloading the report.

    $reportRequestId = SubmitGenerateReport(
            $proxy, 
            $encodedReport
            );

    printf("Report Request ID: %s\n\n", $reportRequestId);

    $waitTime = 30 * 1; 
    $reportRequestStatus = null;

    // This sample polls every 30 seconds up to 5 minutes.
    // In production you may poll the status every 1 to 2 minutes for up to one hour.
    // If the call succeeds, stop polling. If the call or 
    // download fails, the call throws a fault.

    for ($i = 0; $i < 10; $i++)
    {
        sleep($waitTime);

        // PollGenerateReport helper method calls the corresponding Bing Ads service operation
        // to get the report request status.

        $reportRequestStatus = PollGenerateReport(
                $proxy, 
                $reportRequestId
                );

        if ($reportRequestStatus->Status == ReportRequestStatusType::Success ||
            $reportRequestStatus->Status == ReportRequestStatusType::Error)
        {
            break;
        }
    }

    if ($reportRequestStatus != null)
    {
        if ($reportRequestStatus->Status == ReportRequestStatusType::Success)
        {
            $reportDownloadUrl = $reportRequestStatus->ReportDownloadUrl;
            printf("Downloading from %s.\n\n", $reportDownloadUrl);
            DownloadFile($reportDownloadUrl, $DownloadPath);
            printf("The report was written to %s.\n", $DownloadPath);
        }
        else if ($reportRequestStatus->Status == ReportRequestStatusType::Error)
        {
            printf("The request failed. Try requesting the report " .
                    "later.\nIf the request continues to fail, contact support.\n");
        }
        else  // Pending
        {
            printf("The request is taking longer than expected.\n " .
                    "Save the report ID (%s) and try again later.\n",
                    $reportRequestId);
        }
    }

}
catch (SoapFault $e)
{
    // Output the last request/response.

    print "\nLast SOAP request/response:\n";
    print $proxy->GetWsdl() . "\n";
    print $proxy->GetService()->__getLastRequest()."\n";
    print $proxy->GetService()->__getLastResponse()."\n";

    // Reporting service operations can throw AdApiFaultDetail.
    if (isset($e->detail->AdApiFaultDetail))
    {
        // Log this fault.

        print "The operation failed with the following faults:\n";

        $errors = is_array($e->detail->AdApiFaultDetail->Errors->AdApiError)
        ? $e->detail->AdApiFaultDetail->Errors->AdApiError
        : array('AdApiError' => $e->detail->AdApiFaultDetail->Errors->AdApiError);

        // If the AdApiError array is not null, the following are examples of error codes that may be found.
        foreach ($errors as $error)
        {
            print "AdApiError\n";
            printf("Code: %d\nError Code: %s\nMessage: %s\n", $error->Code, $error->ErrorCode, $error->Message);

            switch ($error->Code)
            {
                case 0:    // InternalError
                    break;
                case 105:  // InvalidCredentials
                    break;
                default:
                    print "Please see MSDN documentation for more details about the error code output above.\n";
                    break;
            }
        }
    }

    // Reporting service operations can throw ApiFaultDetail.
    elseif (isset($e->detail->ApiFaultDetail))
    {
        // Log this fault.

        print "The operation failed with the following faults:\n";

        // If the BatchError array is not null, the following are examples of error codes that may be found.
        if (!empty($e->detail->ApiFaultDetail->BatchErrors))
        {
            $errors = is_array($e->detail->ApiFaultDetail->BatchErrors->BatchError)
            ? $e->detail->ApiFaultDetail->BatchErrors->BatchError
            : array('BatchError' => $e->detail->ApiFaultDetail->BatchErrors->BatchError);

            foreach ($errors as $error)
            {
                printf("BatchError at Index: %d\n", $error->Index);
                printf("Code: %d\nError Code: %s\nMessage: %s\n", $error->Code, $error->ErrorCode, $error->Message);

                switch ($error->Code)
                {
                    case 0:     // InternalError
                        break;
                    default:
                        print "Please see MSDN documentation for more details about the error code output above.\n";
                        break;
                }
            }
        }

        // If the OperationError array is not null, the following are examples of error codes that may be found.
        if (!empty($e->detail->ApiFaultDetail->OperationErrors))
        {
            $errors = is_array($e->detail->ApiFaultDetail->OperationErrors->OperationError)
            ? $e->detail->ApiFaultDetail->OperationErrors->OperationError
            : array('OperationError' => $e->detail->ApiFaultDetail->OperationErrors->OperationError);

            foreach ($errors as $error)
            {
                print "OperationError\n";
                printf("Code: %d\nError Code: %s\nMessage: %s\n", $error->Code, $error->ErrorCode, $error->Message);

                switch ($error->Code)
                {
                    case 0:     // InternalError
                        break;
                    case 106:   // UserIsNotAuthorized
                        break;
                    case 2100:  // ReportingServiceInvalidReportId
                        break;
                    default:
                        print "Please see MSDN documentation for more details about the error code output above.\n";
                        break;
                }
            }
        }
    }
}
catch (Exception $e)
{
    if ($e->getPrevious())
    {
        ; // Ignore fault exceptions that we already caught.
    }
    else
    {
        print $e->getCode()." ".$e->getMessage()."\n\n";
        print $e->getTraceAsString()."\n\n";
    }
}


// Request the report. Use the ID that the request returns to
// check for the completion of the report.

function SubmitGenerateReport($proxy, $report)
{
    // Set the request information.

    $request = new SubmitGenerateReportRequest();
    $request->ReportRequest = $report;

    return $proxy->GetService()->SubmitGenerateReport($request)->ReportRequestId;
}

// Check the status of the report request. The guidance of how often to poll
// for status is from every five to 15 minutes depending on the amount
// of data being requested. For smaller reports, you can poll every couple
// of minutes. You should stop polling and try again later if the request
// is taking longer than an hour.

function PollGenerateReport($proxy, $reportRequestId)
{
    // Set the request information.

    $request = new PollGenerateReportRequest();
    $request->ReportRequestId = $reportRequestId;

    return $proxy->GetService()->PollGenerateReport($request)->ReportRequestStatus;
}

// Using the URL that the PollGenerateReport operation returned,
// send an HTTP request to get the report and write it to the specified
// ZIP file.

function DownloadFile($reportDownloadUrl, $downloadPath)
{
    if (!$reader = fopen($reportDownloadUrl, 'rb'))
    {
        throw new Exception("Failed to open URL " . $reportDownloadUrl . ".");
    }

    if (!$writer = fopen($downloadPath, 'wb'))
    {
        fclose($reader);
        throw new Exception("Failed to create ZIP file " . $downloadPath . ".");
    }

    $bufferSize = 100 * 1024;

    while (!feof($reader))
    {
        if (false === ($buffer = fread($reader, $bufferSize)))
        {
             fclose($reader);
             fclose($writer);
             throw new Exception("Read operation from URL failed.");
        }

        if (fwrite($writer, $buffer) === false)
        {
             fclose($reader);
             fclose($writer);
             $exception = new Exception("Write operation to ZIP file failed.");
        }
    }

    fclose($reader);
    fflush($writer);
    fclose($writer);
}


?>

Vote up if this code helps you ;)

Nickeliferous answered 5/8, 2015 at 21:41 Comment(4)
basically no, that's how bing wants to provide the data - you have to live with that.Trapezium
I suppose you're right. Although features like the return of an object instead of a file is something that is well within Microsoft's capacity to implement, since Google Adwords API can do it. But I guess that's why Google controls over 60% of the search market and Bing barely gets over 30%, even with all their usage being across other domains like Yahoo. O well, I guess I will have to work with what I've got for now......Nickeliferous
Did you ever find a solution to this?Educated
I've never understood why Ad APIs in general seem to go out of their way to be overly complicated! Using the .zip seems to unfortunately still be the case here.Layard
W
3

I came across this looking for something else Bing Ads API related.

You may try to read entire file into a string (using OS memory mapping), then extract file from string. Following hack worked for me in similar case:

$data = file_get_contents($reportDownloadUrl);
$head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data, 0, 30));
$report = gzinflate(substr($data, 30 + $head['namelen'] + $head['exlen'], $head['csize']));

Hope it helps someone...

Wigwag answered 31/1, 2017 at 21:37 Comment(1)
is there a way I can do this in python3?Lexington

© 2022 - 2024 — McMap. All rights reserved.