Get statistics for a selection of rows

Python 2 or 3 using XML with the Requests and PyXB libraries

After installing the PyXB library, the utility for generating pyxbgen classes will be available. Run

pyxbgen -u https://api.direct.yandex.com/v5/reports.xsd -m directapi5reports

As a result, two files will be formed: _general.py and directapi5reports.py. Import the directapi5reports.py file to the script in order to generate valid XML request codes. For more information, see http://pyxb.sourceforge.net.

This example shows a request to the Reports service, along with the result processing and output. The mode for generating the report is selected automatically. If the report is added to the queue in offline mode, repeated requests are made.

The report contains statistics on impressions, clicks, and expenditures for all the advertiser's campaigns for the past month, with grouping by date, campaign name, and ad ID. The report filters out rows with the most expensive clicks, where the number of clicks is less than the set value and the CPC is higher than the set value.

To use the example, specify the OAuth access token in the input data. For a request on behalf of an agency, also specify the client login. In the request message body, specify the thresholds for the number of clicks and the CPC, as well as a report name that is unique among the advertiser's reports.

# -*- coding: utf-8 -*-
import requests
from requests.exceptions import ConnectionError
from directapi5reports
import pyxb

#  Method for correctly parsing UTF-8 encoded strings for both Python 3 and Python 2
import sys

if sys.version_info < (3,):
    def u(x):
        try:
            return x.encode("utf8")
        except UnicodeDecodeError:
            return x
else:
    def u(x):
        if type(x) == type(b''):
            return x.decode('utf8')
        else:
            return x

# --- Input data ---
#  Address of the Reports service for sending XML requests (case-sensitive)
ReportsURL = 'https://api.direct.yandex.com/json/v5/reports'

# OAuth token of the user that requests will be made on behalf of
token = 'TOKEN'

# Login of the advertising agency client
# Required parameter if requests are made on behalf of an advertising agency
clientLogin = 'CLIENT_LOGIN'

# --- Preparing the request ---
#  Creating HTTP request headers
headers = {
           # OAuth token. The word Bearer must be used
           "Authorization": "Bearer " + token,
           # Login of the advertising agency client
           "Client-Login": clientLogin,
           # Response language
           "Accept-Language": "ru",
           # Report mode
           "processingMode": "auto"
           # Currency format in the report
           # "returnMoneyInMicros": "false",
           # Don't output the row with the report name and date range in the report
           # "skipReportHeader": "true",
           # Don't output the row with field names in the report
           # "skipColumnHeader": "true",
           # Don't output the row with the number of statistics rows in the report
           # "skipReportSummary": "true"
           }

# Creating the request body
requestData = directapi5reports.ReportDefinition()
# Data selection criteria
requestData.SelectionCriteria = pyxb.BIND()
# Select rows that have fewer clicks than the specified amount,
# but the click cost is higher than specified
requestData.SelectionCriteria.Filter = [pyxb.BIND("Clicks","LESS_THAN","CLICK_THRESHOLD"),pyxb.BIND("Cost","GREATER_THAN","COST_THRESHOLD")]
# Fields to get in the report
requestData.FieldNames = ["Date","CampaignName","AdId","Impressions","Clicks","Cost"]
# Sort by ascending date
requestData.OrderBy = [pyxb.BIND("Date","ASCENDING")]
# Report name
requestData.ReportName = u("Test_Report_By_XSD")
# Report type: ad statistics
requestData.ReportType = "AD_PERFORMANCE_REPORT"
# Report period: last month
requestData.DateRangeType = "LAST_MONTH"
# Report format
requestData.Format = "TSV"
# Return the cost of clicks without VAT
requestData.IncludeVAT ="NO"
# Return the cost of clicks without client discounts
requestData.IncludeDiscount = "NO"

# Convert data to XML request code
requestData = requestData.toxml()

# --- Starting the request execution loop ---
# If the HTTP 200 response is received, output the the report contents
# If the HTTP 201 or 202 response is received, send repeated requests
while True:
    try:
        req = requests.post(ReportsURL,requestData,headers=headers)
        req.encoding = 'utf-8'  # Force processing the response in UTF-8 encoding
        if req.status_code == 400:
            print("Invalid request parameters")
            print("RequestId: {}".format(req.headers.get("RequestId",False)))
            print("XML code of the request: {}".format(u(requestData)))
            print("XML code of the server response: \n{}".format(u(req.text)))
            break
        elif req.status_code == 200:
            print("Report created")
            print("RequestId: {}".format(req.headers.get("RequestId", False)))
            print("Output report file: \n{}".format(u(req.text)))
            break
        elif req.status_code == 201:
            print("Report added to the offline queue")
            retryIn = int(req.headers.get("retryIn",60))
            print("Request will be resent in  {} seconds".format(retryIn))
            print("RequestId: {}".format(req.headers.get("RequestId", False)))
            sleep(retryIn)
        elif req.status_code == 202:
            print("Report generated in offline mode")
            retryIn = int(req.headers.get("retryIn", 60))
            print("Resending request in {} seconds".format(retryIn))
            print("RequestId: {}".format(req.headers.get("RequestId", False)))
            sleep(retryIn)
        elif req.status_code == 500:
            print("Error generating report. Please repeat the request again later.")
            print("RequestId: {}".format(req.headers.get("RequestId",False)))
            print("XML code for the server response: \n{}".format(u(req.text)))
            break
        elif req.status_code == 502:
            print("Exceeded the server limit on report creation time.")
            print("Please try changing the request parameters: reduce the time period and the amount of data requested.")
            print("XML code for the request: {}".format(requestData))
            print("RequestId: {}".format(req.headers.get("RequestId",False)))
            print("XML code for the server's response: \n{}".format(u(req.text)))
            break
        else:
            print("Unexpected error")
            print("RequestId: {}".format(req.headers.get("RequestId", False)))
            print("XML code for the request: {}".format(requestData))
            print("XML code for the server's response: \n{}".format(u(req.text)))
            break

    # Error handling if the connection with the Yandex.Direct API server wasn't established.
    except ConnectionError:
        # In this case, we recommend repeating the request again later
        print("Error connecting to the Yandex.Direct API server")
        # Forced exit from loop
        break

    # If any other error occurred
    except:
        # In this case, you should analyze the application's actions
        print("Unexpected error")
        # Forced exit from loop
        break