API отчетов Bing Ads возвращает download_url: нетPython

Программы на Python
Ответить
Anonymous
 API отчетов Bing Ads возвращает download_url: нет

Сообщение Anonymous »

У меня есть сценарий Python, использующий библиотеку запросов Python, где я пытаюсь получить данные из API отчетов Bings, но по какой-то причине я получаю следующий ответ всякий раз, когда я использую API службы PollGenerateReport.
https://learn.microsoft.com/en-us/adver ... ivots=rest
{'ReportRequestStatus': {'Status': 'Success', 'ReportDownloadUrl': None}}
Я не получил ссылок для загрузки API эффективности кампании.
Каждый раз, когда я пытаюсь получить объект данных обычных кампаний из API системы управления кампаниями, он показывает всю кампанию.

https://learn.microsoft.com/en-us/adver ... ivots=rest
Вот сценарий:
import requests
import csv
import io
import time
import logging

ACCESS_TOKEN = "XXXXXXXX"
DEVELOPER_TOKEN = "XXXXXXXXXXXX"
CUSTOMER_ID = "XXXXXXXX"
ACCOUNT_ID = "XXXXXXX"

START_DATE = {"Year": 2024, "Month": 1, "Day": 1}
END_DATE = {"Year": 2026, "Month": 3, "Day": 5}

REPORT_TYPE = "CampaignPerformanceReport"

COLUMNS = [
"TimePeriod",
"AccountId",
"AccountName",
"CampaignId",
"CampaignName",
"Impressions",
"Clicks",
"Spend",
"Ctr",
"AverageCpc",
"Conversions",
]

payload = {
"ReportRequest": {
"Type": "CampaignPerformanceReportRequest",
"ReportName": "CampaignReport",
"Format": "Csv",
"FormatVersion": "2.0",
"Aggregation": "Daily",
"ReturnOnlyCompleteData": False,
"ExcludeColumnHeaders": False,
"ExcludeReportHeader": True,
"ExcludeReportFooter": True,
"Scope": {
"AccountIds": [int(ACCOUNT_ID)]
},
"Time": {
"CustomDateRangeStart": START_DATE,
"CustomDateRangeEnd": END_DATE,
"ReportTimeZone": "PacificTimeUSCanadaTijuana"
},
"Columns": COLUMNS,
}
}

def get_headers():
return {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"DeveloperToken": DEVELOPER_TOKEN,
"CustomerAccountId": str(ACCOUNT_ID),
"CustomerId": str(CUSTOMER_ID),
}

def submit_report(report_type=REPORT_TYPE, columns=COLUMNS):
url = "https://reporting.api.bingads.microsoft ... ort/Submit"

api_type = report_type + "Request" if not report_type.endswith("Request") else report_type

payload = {
"ReportRequest": {
"Type": api_type,
"ReportName": f"{report_type}_{int(time.time())}",
"Format": "Csv",
"FormatVersion": "2.0",
"Aggregation": "Daily",
"ReturnOnlyCompleteData": False,
"ExcludeColumnHeaders": False,
"ExcludeReportHeader": True,
"ExcludeReportFooter": True,
"Scope": {
"AccountIds": [int(ACCOUNT_ID)]
},
"Time": {
"CustomDateRangeStart": START_DATE,
"CustomDateRangeEnd": END_DATE,
"ReportTimeZone": "PacificTimeUSCanadaTijuana"
},
"Columns": columns,
}
}

logging.info(f"Submitting report: {report_type}")
response = requests.post(url, json=payload, headers=get_headers())
print("Submit Response: ", response.json())
if not response.ok:
logging.error(f"Submit failed [{response.status_code}]: {response.text}")
response.raise_for_status()

report_request_id = response.json().get("ReportRequestId")
logging.info(f"Report submitted. ID: {report_request_id}")
return report_request_id

def poll_report(report_request_id, max_attempts=60, sleep_seconds=5):
url = "https://reporting.api.bingads.microsoft ... eport/Poll"

payload = {"ReportRequestId": report_request_id}

for attempt in range(1, max_attempts + 1):
response = requests.post(url, json=payload, headers=get_headers())
print("Poll Response: ", response.json())
if not response.ok:
logging.error(f"Poll failed [{response.status_code}]: {response.text}")
response.raise_for_status()

result = response.json()
status_obj = result.get("ReportRequestStatus", result)
status = status_obj.get("Status")
download_url = status_obj.get("ReportDownloadUrl")

logging.info(f"Poll {attempt}/{max_attempts}: status = {status}")

if status == "Success":
if not download_url:
logging.warning("Report succeeded but no download URL — no data for this date range / account.")
return None
return download_url

elif status == "Error":
raise Exception(f"Report failed on the server side. Full response: {result}")

elif status in ("Pending", "InProgress"):
if attempt < max_attempts:
time.sleep(sleep_seconds)
else:
raise Exception("Timed out waiting for report.")

else:
raise Exception(f"Unexpected status '{status}'. Response: {result}")

raise Exception("Exceeded max poll attempts.")

def download_report(download_url):
logging.info("Downloading report...")
response = requests.get(download_url, timeout=120)

if not response.ok:
logging.error(f"Download failed [{response.status_code}]")
response.raise_for_status()

lines = response.text.splitlines()
start_idx = 0
for i, line in enumerate(lines):
if any(col in line for col in COLUMNS):
start_idx = i
break

clean_csv = "\n".join(lines[start_idx:])
reader = csv.DictReader(io.StringIO(clean_csv))
rows = [dict(row) for row in reader if any(v.strip() for v in row.values())]

logging.info(f"Parsed {len(rows)} rows.")
return rows

def run():
report_id = submit_report()

download_url = poll_report(report_id)
if download_url is None:
print("No data returned. Check your date range, account ID, and customer ID.")
return []

rows = download_report(download_url)

if rows:
print(f"Report: {REPORT_TYPE} | Rows: {len(rows)}")
for row in rows[:5]:
for k, v in row.items():
print(f" {k}: {v}")
print()
else:
print("Report downloaded but no rows found after parsing.")

return rows

if __name__ == "__main__":
rows = run()



Подробнее здесь: https://stackoverflow.com/questions/799 ... d-url-none
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»