Как «забрать» целевые звонки из CoMagic в пиксель Facebook

Время чтения: 9 минут
Виталий Бахвалов
logo

Рекламу в сети Facebook можно оптимизировать по целевым событиям на сайте. Например, установить пиксель и собирать в него информацию о конверсионных действиях пользователя: когда он кладет товар в корзину, переходит к оплате или пишет сообщение через форму обратной связи. В дальнейшем алгоритмы Facebook ищут похожую аудиторию пользователей, которые с высокой долей вероятности совершат похожие действия, и показывают вашу рекламу им.

Все это здорово, но в нишах с высоким чеком основным целевым действием считается не какое-то действие на сайте, а звонок клиента. Звонки можно отследить с помощью коллтрекинга, но можно ли оптимизировать по ним рекламную кампанию в Facebook? Да! Но готового решения на рынке еще нет, поэтому сделаем его сами с помощью API CoMagic, Facebook Server-Side API, Python и пары костылей.


Шаг 1. Передаем пользовательские данные в систему CoMagic

Для начала собираем все возможные параметры о пользователе на момент совершения действия. Чем больше параметров, тем больше вероятность правильного определения пользователя алгоритмом Facebook.

С помощью диспетчера тегов — а именно Google Tag Manager — создаем две пользовательские переменные: _fbp и _fbc.

*Подробнее о параметрах на странице для разработчиков
Data Parameters
fbp and fbc Parameters

Затем с помощью Java Script API формируем пользовательский HTML-тег, который позволит отправить полученные параметры в CoMagic:


  <script type="text/javascript">
  Comagic.setProperty('fbp', {{fbp}});
  Comagic.setProperty('fbc', {{fbc}});
  </script> 

Триггером может являться загрузка страницы при условии непустого значения переменных fbc или fbp. Значения из них запишутся в CoMagic как параметры пользователя (visitor_custom_properties).


Шаг 2. Получаем данные об обращениях CoMagic

Делаем это при помощи Data API и следующих методов:

get.communications_report — получение списка всех обращений (сюда относятся не только звонки, но и чаты, цели и офлайн-обращения);

get.calls_report — получение отчета по сессиям звонков.


# Импорты
import re
import requests
import pandas as pd
import json
from datetime import datetime, timedelta
# Токен из раздела «Управление пользователями» на app.comagic.ru access_token = '9m6xfwuofjid8xb6v68h46r1g9njax1xhe8iwyw6'
# Идентификатор сайта user_id = 19774
# Дата начала выборки campaign_start = datetime(2020, 1, 1)

get.communications


# При попытке получить данные более чем за 90 дней API выдаст ошибку: Max value of requested date interval is 90 days. Несмотря на то что Facebook примет только те события, которые произошли не ранее 7 дней назад, учтем это ограничение в коде — его можно использовать для других целей.
i = 0
date_from_list = []
date_till_list = []

while True:
    next_period = campaign_start + timedelta(i*90
 if next_period > datetime.now():
 break
 else: 
       date_from_list.append((campaign_start + timedelta(i*90)).strftime('%Y-%m-%d %H:%M:%S'))
       date_till_list.append((campaign_start + timedelta(i*90) + timedelta(90   )).strftime('%Y-%m-%d %H:%M:%S'))
       i += 1       
print(f'date_from_list: {date_from_list}\ndate_till_list: {date_till_list}')


Получим промежутки по 90 дней, для которых будем получать данные.


  date_from_list: ['2020-01-01 00:00:00', '2020-03-31 00:00:00']
  date_till_list: ['2020-03-31 00:00:00', '2020-06-29 00:00:00']

Функция получения данных об обращениях.

Нужные поля:

ym_client_id — clientID Метрики,

date_time — дата и время звонка,

tags — теги,

communication_page_url — страница, с которой был совершен звонок,

visitor_custom_properties — параметры пользователя, куда мы передаем значения fbp и fbc,

id — идентификатор обращения.

Фильтры: только звонки и идентификатор сайта.


# https://www.comagic.ru/support/api/data-api/Reports/
   def getCommunications(datefrom,datetill):
   period_data = []
   n = 0      
   while True:
       payload = \
       {'id': 1,
       'jsonrpc': '2.0',
       'method': 'get.communications_report',
       'params': {'access_token': access_token,
                   'date_from': datefrom,
                   'date_till': datetill,
       'fields': ['ym_client_id',
                   'date_time',
                   'tags',
                   'communication_page_url',
                   'visitor_custom_properties',
                   'id'],
       'filter': {'condition':'and',
       'filters': [{'field': 'communication_type',
                    'operator': '=',
                    'value': 'call'},
                   {'field':'site_id',
                    'operator': '=',
                    'value': site_id}]},
      'limit': 10000,
      'offset': 0 + n * 10000,
      'sort': [{'field': 'date_time', 'order': 'asc'}]}}
                
      url = 'https://dataapi.comagic.ru/v2.0'
      r = requests.post(url, data=json.dumps(payload))
      data = json.loads(r.text)
      res = data['result ']['data']        
      if len(res) == 0:
         break
       
      period_data += res
      n += 1
 
      return period_data


Пройдем циклом по временным промежуткам и создадим dataframe с данными об обращениях.


data = []
for i in range(len(date_from_list)):
    try:
        data += getCommunications(date_from_list[i], date_till_list[i])
    except KeyError:
        print(getCommunications()['error' ]['message' ]) 
    break
        
  communications_df = pd.DataFrame(data)   
  communications_df = communications_df.sort_index(axis=1)
  total_communications = communications_df.shape[0]
  print(f"Обращений: {total_communications}")



[{ 'tag_type': 'manual',
   'tag_name': 'Повторный',
   'tag_user_login': 'name@domen.ru',
   'tag_id': 55332,
   'tag_employee_id': None,
   'tag_employee_full_name': None,
   'tag_change_time': '2020-04-01 10:46:18',
   'tag_user_id': 111685}]


Нам нужны только tag_name и только с определенным значением, например «Целевой».


 def get_clean_tags(tags):
     if tags != None:
        tags_list = []
        for tag in tags:
             tags_list.append(tag['tag_name'])
        if 'Целевой' in tags_list:
           return 'Целевой'
        else:
           return None
   else:
       return None
    
 communications_df['clean_tags'] = communications_df['tags'].apply(get_clean_tags)


Дальше проведем необходимые преобразования


# Дату — в формат datetime
 communications_df['date_time'] = pd.to_datetime(communications_df['date_time'], yearfirst=True) 

# Удалим параметры из URL
 communications_df['communication_page_url'] = communications_df['communication_page_url']\
    .map(lambda x: re.sub('(^.+?)\?.*', r'\1', str(x)))

# Заменим на None звонки без visitor_custom_properties
 communications_df['visitor_custom_properties'] = communications_df['visitor_custom_properties']\
    .map(lambda x: None if len(x) == 0 else x)

# Удалим строки без целевых звонков и без данных Facebook, удалим столбец tags и дубликаты строк
 communications_df_target = communications_df.sort_values(by='date_time').\
     dropna(subset=['visitor_custom_properties', 'clean_tags']).drop('tags', axis=1).\
     drop_duplicates(subset='ym_client_id', keep='first').reset_index(drop=True)


get.calls_report

Этот метод позволяет получать данные о звонках, а именно номера телефонов пользователей. Номер телефона нужен для передачи в пиксель вместе с параметрами fbp и fbc.

Поля:

id — идентификатор обращения,

contact_phone_number — номер телефона,

visitor_id — идентификатор посетителя сайта.


# https://www.comagic.ru/support/api/data-api/Reports/
   def getCalls(datefrom,datetill):
   period_data = []
   n = 0      
   while True:
       payload = \
       {'id': 1,
       'jsonrpc': '2.0',
       'method': 'get.calls_report',
       'params': {'access_token': access_token,
                  'date_from': datefrom,
                  'date_till': datetill,
       'fields': ['id',
                  'contact_phone_number',
                  'visitor_id'
                   ],
       'filter': {'condition':'and',
       'filters': [{'field': 'id',
                    'operator': 'in',
                    'value':communications_df_target[ 'id'].to_list()},
                   {'field':'site_id',
                    'operator': '=',
                    'value':site_id,}]},
      'limit': 10000,
      'offset': 0 + n * 10000,
                    
      url = 'https://dataapi.comagic.ru/v2.0'
      r = requests.post(url, data=json.dumps(payload))
      data = json.loads(r.text)
      res = data['result ']['data']        
      if len(res) == 0:
        break
       
      period_data += res
      n += 1
 
      return period_data

   data = []
   for i in range(len(date_from_list)):
       try:
            data += getCalls(date_from_list[i],date_till_list[i])
      except KeyError:
            print(getCalls()[ 'error'] ['message']) 
       break
        
  comagic_calls = pd.DataFrame(data)        
  comagic_calls = comagic_calls.sort_index(axis=1)
  comagic_calls

Получим таблицу с номерами телефонов:

Благодаря идентификатору обращения (id) можно объединить обе таблицы:


communications_calls = pd.merge(communications_df_target, comagic_calls, how='left', on='id')

Таким образом, мы получили единую таблицу обращений и звонков.

Проведем несколько необходимых преобразований во вложенном массиве visitor_custom_properties.


def get_fb_from_custom_properties(properties): # достанем данные из вложенной структуры visitor_custom_properties
    fb = {}
    for i  in properties:
        fb[i['property_name']] = i['property_value']     
    fbc = fb.get('fbc', None)
    fbp = fb.get('fbp', None)
   return fbp, fbc

communications_calls['fbp'] = communications_calls['visitor_custom_properties'].\
map(lambda x: get_fb_from_custom_properties(x)[0])

communications_calls['fbc'] = communications_calls['visitor_custom_properties'].\
map( lambda x: get_fb_from_custom_properties(x)[1])

final_table = communications_calls.drop('visitor_custom_properties', axis = 1)

# В Facebook надо отправить звонки с временем в формате timestamp
final_table['timestamp'] = final_table['date_time'].\
map( lambda x: int((x - timedelta(hours=3)).timestamp())) # часовой пояс Москвы

final_table = final_table.where(pd.notnull(final_table), None) # все пустые значения заменим на None

# Удаление лишних столбцов
final_table = final_table.drop(['clean_tags', 'id', 'visitor_id'], axis=1)

final_table = final_table.sort_index(axis=1)
final_table


Получаем таблицу с необходимыми данными для отправки в пиксель:

Поскольку не стоит отправлять одно событие несколько раз, то заведем лог отправленных звонков в формате csv.



# Загрузим лог уже отправленных событий
# или создадим, если его нет
open('sent_calls_to_facebook_pixel.csv', 'a').close()

with open('sent_calls_to_facebook_pixel.csv','r') as f:
    text = f.read()
    
    sent_events = []
    for row in text.split('\n'):
        try:
            sent_events.append(row.split(';')[6])
        except:
            continue  

# Оставим в таблице только те значения, которые еще не отправляли
# Данные об отправленных звонках хранятся в sent_calls_to_facebook_pixel.csv
# Если таблица пустая — ничего не отправится
final_table = final_table[~final_table['contact_phone_number'].isin(sent_events)]

# Удалим события старше 7 дней, иначе запрос не пройдет
final_table = final_table[final_table['date_time'] > datetime.now() - timedelta(7)]
final_table.head()



Шаг 3. Отправляем данные на Facebook Server AP

О том, как настроить и получить токен, можно подробно прочитать в блоге Дмитрия Осиюка.

Документация Facebook



from facebook_business.adobjects.serverside.event import Event
from facebook_business.adobjects.serverside.event_request import EventRequest
from facebook_business.adobjects.serverside.user_data import UserData
from facebook_business.adobjects.serverside.custom_data import CustomData
from facebook_business.api import FacebookAdsApi

fb_access_token = 'EAAEAM1ktR1gBAJkpPqJZAA81uyrfgeLp0LIFwChKbBoXlVpEgNu6GvecLh26kFbbJpfd2JsZBqtPQWrw4i1HvkzMW4Q54zPIugQPGZBfa2wSiFU2AV7ccCZAtIJzG4llSbkm6Ioyym2enmpZCyq8SdnGbwF1ym09ra46fRKUphvWvPXqSYibf1tb8J1xckiGNbwB9nejTg8QZDZD'
pixel_id = '279923445633319'

FacebookAdsApi.init(access_token=fb_access_token)



Создаем массив events для отправки, в user_data передаются значения из подготовленной таблицы:



events = []
for x, y in final_table.iterrows():
    call_url = y[final_table.columns.get_loc('communication_page_url')]
    phone = y[final_table.columns.get_loc('contact_phone_number')]
    clientID = y[final_table.columns.get_loc('ym_client_id')]
    date_time = y[final_table.columns.get_loc('date_time')]
    fbc = y[final_table.columns.get_loc('fbc')]
    fbp = y[final_table.columns.get_loc('fbp')]
    ts = y[final_table.columns.get_loc('timestamp')]

#    Пишем лог clientID, отправленных в пиксель    
    with open('sent_calls_to_facebook_pixel.csv','a') as f:
        f.write(f'{date_time};{clientID};{fbc};{fbp};{ts};{call_url};{phone}\n')
        
    print(f'data_time: {date_time}; clientID: {clientID}; fbc:{fbc}; fbp: {fbp}; ts: {ts}; call_url: {call_url}; phone: {phone}\n')
    
    # https://developers.facebook.com/docs/marketing-api/server-side-api/using-the-api
    # https://developers.facebook.com/docs/marketing-api/server-side-api/parameters/server-event
    user_data = UserData(
    fbc = fbc,
    fbp = fbp,
    phone = phone
    )
    
    event = Event(
        event_name = 'ComagicCall',
        event_time = ts, # event_time can be up to 7 days before you send an event to Facebook. If any event_time in data is greater than 7 days in the past, we return an error for the entire request and process no events.
        event_source_url = call_url, # the browser URL where the event happened.
        user_data = user_data,
    )    
    
    events.append(event)

print(f'Будет отправлено событий: {len(events)}', end='\n\n')


Получаем готовые к отправке события:

Отправляем события в тестовом режиме:



# раскомментируй test_event_code, чтобы проверить отправку событий на
# events manager → тестирование событий (test events) → серверные (server)
# test_event_code надо подставить свой
# https://business.facebook.com/events_manager/pixel/verify?business_id={}&selected_data_sources=PIXEL&selected_screen_section=DATA_SOURCES&pixel_id={}&event_type=SERVER

 if len(events) > 0:
    event_request = EventRequest(
        events=events,
        test_event_code = 'TEST34220',
        pixel_id=pixel_id)
    event_response = event_request.execute()
     print(event_response)
 else:
    print('Новых звоночков нет :(')



В разделе «Тестирование событий» можно проверить отправку. (IP-адрес и Агент пользователя в примере выше мы не передавали, но это делается по аналогии с предыдущими шагами.)

Если все прошло удачно, закомментируйте test_event_code.

P. S. Токены и идентификаторы вымышленные ;-)



(3)
5/5
Оцените статью
Поделитесь с друзьями
Содержание: