본문 바로가기

한국어/약간의 개발

[Adobe DSP] API를 사용하여 Adobe DSP (Tubemogul) 데이터를 자동으로 이메일로 전송

Adobe DSP (TubeMogul; 튜브모글; 튜브모굴; 어도비 DSP;  어도비 애드버타이징 클라우드; Adobe Advertising Cloud) API를 활용하여, 매주 일주일 동안의 데이터를 추출하여 해당 정보를 메일로 자동 전송하도록 설정했습니다. 

 

Adobe DSP의 기본 보고서 기능으로는 충족되지 않는 특정 데이터 추출 및 형식 지정 요구 사항이 있어 시작했고, 

Google 검색을 통해 얻을 수 없었던 API 정보를 찾아 획득했지만, 유저 인증과 관련된 API를 찾지 못해 유저 로그인 정보를 수집해야 했습니다.

 

개발자가 아니지만 호기심이 생겼을 때만 코딩을 하는 컴퓨터 공학 전공자로서, 코드를 공유하는게 많이 망설여지지만, 이 프로젝트의 진행 상황을 개인 기록용으로 문서화하기로 결정했습니다.

 

import requests
from bs4 import BeautifulSoup as bs
import re
import pandas as pd
import datetime
import time
from pandas import json_normalize
import json
import numpy as np
from pathlib import Path
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
from email.mime.application import MIMEApplication


def login():
    login_data = {
        'user': Adobe DSP ID,
        'password': Adobe DSP PW
    }

    with requests.Session() as session:
        request = session.post('https://advertising.adobe.com/auth/login', data=login_data)
        return session


def extract_token(session):
    url = session.post('https://advertising.adobe.com/playtime/brand_reports')
    soup = bs(url.text, 'html.parser')
    script = soup.find('script', text=re.compile('jsSharedGlobalVariables'))
    token = re.findall('[A-Za-z0-9]{32}', str(script))[0]
    return token


def fetch_data(session, token, start_day, end_day):
    bearer_token = 'Bearer ' + token
    headers = {
        'Authorization': bearer_token,
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'
    }
    params = {
        'start_day': start_day,
        'end_day': end_day,
        'start_hour': '00',
        'end_hour': '00',
        'timezone': 'Asia/Seoul'
    }

    params_b = {
        'start_day': start_day,
        'end_day': start_day,
        'timezone': 'Asia/Seoul'
    }

    campaigns_request = session.get('https://api.tubemogul.com/v2/reporting/campaigns', headers=headers, params=params)
    campaigns_json = campaigns_request.json()
    campaigns_df = pd.DataFrame(campaigns_json['items'])

    campaign_ids = list(np.array(campaigns_df['campaign_id'].tolist()))

    placements_data = pd.DataFrame([])
    placements_stats = pd.DataFrame([])
    placements_url = 'https://api.tubemogul.com/v3/trafficking/campaigns/{}/placements'
    for campaign_id in campaign_ids:
        url = placements_url.format(str(campaign_id))
        placements_request = session.get(url, headers=headers)
        placements_json = placements_request.json()

        placements_data = placements_data.append(pd.DataFrame(placements_json.get('items')))

        placements_df = placements_data[['placement_id']]
        placement_ids = list(np.array(placements_df['placement_id'].tolist()))

    ads_data = pd.DataFrame([])
    ads_stats = pd.DataFrame([])
    ads_url = 'https://api.tubemogul.com/v2/reporting/placements/{}/ads?'
    for placement_id in placement_ids:
        url = ads_url.format(str(placement_id))
        ads_request = session.get(url, headers=headers, params=params_b)
        ads_json = ads_request.json()

        ads_data = ads_data.append(pd.DataFrame(ads_json['items']))
        ads_stats = ads_stats.append(json_normalize(data=ads_json['items'], record_path=['stats', 'buckets']))
        excel_data = pd.concat([ads_data, ads_stats], axis=1)

    excel_data['AD ID'] = excel_data['ad_id']
    excel_data['AD name'] = excel_data['ad_name']
    excel_data['Spent'] = excel_data['data.advertiser_costs'] / 1000000
    excel_data['Gross(17%)'] = excel_data['data.advertiser_costs'] * 1.38 / 1000000
    excel_data['Impressions'] = excel_data['data.impressions']
    excel_data['Views'] = excel_data['data.views']
    excel_data['25% Views'] = excel_data['data.completions25']
    excel_data['50% Views'] = excel_data['data.completions50']
    excel_data['75% Views'] = excel_data['data.completions75']
    excel_data['100% Views'] = excel_data['data.completions100']
    excel_data['Clicks'] = excel_data['data.total_click_throughs']

    ad_summary = excel_data[['AD ID', 'AD name', 'Spent', 'Gross(17%)', 'Impressions', 'Views', '25% Views', '50% Views',
                             '75% Views', '100% Views', 'Clicks']]
    ad_summary_grouped = ad_summary.groupby(['AD ID', 'AD name']).sum()
    return pd.DataFrame(ad_summary_grouped)


def save_csv(data, filename):
    data.to_csv(filename)


def send_email(from_address, to_addresses, subject, body, files):
    msg = MIMEMultipart()
    msg['From'] = from_address
    msg['To'] = ', '.join(to_addresses)
    msg['Subject'] = subject
    msg.attach(MIMEText(body, 'plain'))

    for file in files:
        with open(file, 'rb') as attachment:
            part = MIMEBase('application', 'octet-stream')
            part.set_payload(attachment.read())
            encoders.encode_base64(part)
            part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(file))
            msg.attach(part)

    smtp_server = 'smtp.worksmobile.com' 
    smtp_port = 465
    smtp_user = from_address
    smtp_password = Sender Mail Password

    with smtplib.SMTP_SSL(smtp_server, smtp_port) as server:
        server.login(smtp_user, smtp_password)
        server.sendmail(from_address, to_addresses, msg.as_string())


def main():
    session = login()
    token = extract_token(session)

    num_days = 7
    today = datetime.date.today()
    for day in range(1, num_days + 1):
        date = today - datetime.timedelta(days=day)
        start_day = date.strftime('%Y-%m-%d')
        end_day = today.strftime('%Y-%m-%d')

        data = fetch_data(session, token, start_day, end_day)
        filename = f'{start_day}.csv'
        save_csv(data, filename)

    from_address = 'yoojihee38@gmail.com'
    to_addresses = ['yoojihee38@gmail.com','yoojihee36@gmail.com']
    subject = f'{start_day} ~ {end_day} TubeMogul AD Report'
    body = f'{start_day} ~ {end_day} TubeMogul AD Report'
    files = [f'{start_day}.csv' for day in range(2, num_days + 1)]

    send_email(from_address, to_addresses, subject, body, files)


if __name__ == '__main__':
    main()

 

 

간단한 코드 설명:

  • 웹사이트 로그인: login() 함수는 requests 라이브러리를 사용하여 특정 사용자 계정 및 비밀번호로 웹사이트에 로그인합니다. 세션을 설정하고 세션 객체를 반환합니다.
  • 토큰 추출: extract_token(session) 함수는 BeautifulSoup을 사용하여 웹페이지에서 토큰을 추출합니다. 이 토큰은 이후 API 요청에서 인증에 사용됩니다.
  • 데이터 추출: fetch_data(session, token, start_day, end_day) 함수는 세션과 토큰을 사용하여 API에서 데이터를 검색합니다. 지정된 기간 동안 데이터를 수집하고 받은 JSON 데이터를 pandas DataFrame으로 처리합니다.
    • 캠페인 API를 사용하여 캠페인 데이터를 가져오는데, 이는 배치 ID를 포함합니다. 그런 다음, 배치에서 연관된 광고 데이터를 가져 오기 위해 광고 API에 배치 ID를 사용합니다.
    • 광고 데이터는 ads_data 및 ads_stats 변수에 저장되며, 이 두 DataFrame은 excel_data로 병합됩니다.
    • 요약하자면, 이 과정은 캠페인 API를 사용하여 배치 ID 값을 추출하고 배치별로 연관된 광고 값을 가져오기 위해 배치 API를 사용하는 것입니다.
  • CSV 저장: save_csv(data, filename) 함수는 DataFrame을 지정된 파일 이름으로 CSV 파일로 저장합니다.
  • 이메일 전송: send_email(from_address, to_addresses, subject, body, files) 함수는 smtplib 라이브러리를 사용하여 첨부 파일과 함께 이메일을 전송합니다. 발신자 이메일 주소, 수신자 주소 목록, 제목, 본문 텍스트 및 첨부 파일 목록이 필요합니다.
  • 메인 함수: main() 함수는 스크립트의 진입점입니다. 웹사이트에 로그인하고 토큰을 추출하여 지난 7일간의 데이터를 가져와 CSV 파일로 저장하고, 이 파일을 이메일로 첨부하여 지정된 수신자에게 보냅니다.

결과:

화면 샘플의 데이터는 단일 날짜에 대한 결과를 나타내며, 실제로는 지난 일주일간의 정보가 포함됩니다. 이를 이해관계자들에게 편리하게 제공하기 위해 데이터를 압축하여 zip 파일로 만들고 그것을 이메일로 보냈습니다.