解釋
- 這裡會引用神策資料很多的介紹,然後進行總結
歸因方法
- 自歸因
- 渠道商幫我們做歸因,有的是每個使用者開啟
app
都回傳給渠道商,渠道商自己歸因 - 有的如華為是從應用商店安裝時, 應用商店把歸因資訊寫入到
app
, 然後首次安裝啟動時能從本地儲存獲取到歸因資料
- 渠道商幫我們做歸因,有的是每個使用者開啟
- 曝光歸因
- 曝光歸因由於有資料量極大、不會使用此項
- 點選歸因(常用)
- 末次歸因模型 (常用, 因為比較好實現)
- 多個歸因源事件時,認為最後一個歸因源事件的功勞為
100%
。
- 多個歸因源事件時,認為最後一個歸因源事件的功勞為
- 首次歸因模型
- 多個歸因源事件時,認為第一個歸因源事件的功勞為
100%
。理由是第一個觸點給使用者建立了認知,與使用者形成了連線。
- 多個歸因源事件時,認為第一個歸因源事件的功勞為
- 平均歸因模型
- 多個歸因源事件時,認為每個歸因源事件平均分配此次功勞。
- 時間衰減歸因模型
- 加上了時間的影響因素,最後1次觸達的貢獻更高。
- 位置歸因模型
- 多個歸因源事件時,認為第一個歸因源事件和最後一個歸因源事件各佔
40%
功勞,其餘平分剩餘的20%
功勞。兼顧最初的線索和最終的決策。
- 多個歸因源事件時,認為第一個歸因源事件和最後一個歸因源事件各佔
- 價值加權歸因模型
- 多個歸因源事件時,對不同渠道的貢獻價值進行加權,將轉化功勞根據權重進行劃分。
匹配方式
- 精確匹配
OAID
:OAID
全稱是Open Anonymous Device Identifier
,中文名是匿名裝置識別符號。OAID
是一種非永久性裝置識別符號,最長64
位,在系統首次啟動的時候生成AndroidID
:ANDROID_ID
是裝置首次啟動時由系統隨機生成的一串64位的十六進位制數字IMEI
: 國際移動裝置識別碼(International Mobile Equipment Identity,IMEI
),即通常所說的手機序列號、手機“串號”,用於在行動電話網路中識別每一部獨立的手機等行動通訊裝置,相當於行動電話的身份證。Mac
: 手機的網路卡地址
- 模糊匹配
- 接下來會參考這個圖來整體說明
根據上圖, 我們先給一個最基礎的表結構,大家可以根據具體業務增減欄位
# 應用表(apps)
id, appid(客戶端使用), name, os, attribute_cycle_days(歸因週期), attribute_white_list(JSON 白名單列表)
# 渠道表(channels)
id, name, template_query(此欄位預先組裝好格式)
# 監測連結表(links)
id, app_id, channel_id, channel_name(自定義渠道名), events(JSON 需要回傳的事件, 方便後續動態增加回傳事件), exp(有效期)
# 廣告點選表:按天分表(click_logs)
id, appid, ad_name, [oaid, imei, android_id, mac, ip, ua](這些匹配方式看各自需要儲存), exp, callback, data(JSON冗餘欄位), attributed_at(歸因成功時間',')
# 歸因成功日誌表(這個表按各自日誌需要設計)
# 回撥日誌表(這個表按各自日誌需要設計)
根據時序圖, 來說明實際場景(以下為虛擬碼, 所有資料庫查詢自行做好快取處理)
- 點選廣告(這一步是不需要我們處理的, 使用者點選廣告的請求直達渠道商)
- 點選監測, 渠道商會請求我們的監測連結
- 監測連結說明
- 由於每一家的引數不一樣, 我的建議是不要針對每一個渠道開發, 而是應該適配一個通用的輸入
- 然後根據通用的輸入, 設計一個模板,這就是為什麼要在
渠道表
加一個template_query
欄位的原因 - 比如
oppo商店
的格式是這樣ad_id=__ADID__&android_id=__ANDROIDID__&imei_md5=__IMEI__&oaid=__OAID__
- 比如
頭條
的格式是這樣ad_name=__AID_NAME__&android_md5=__ANDROIDID__&callback=__CALLBACK_PARAM__&idfa=__IDFA__&imei_md5=__IMEI__&ip=__IP__&mac_md5=__MAC1__&oaid=__OAID__&site=__CSITE__&ua=__UA__
- 然後生成監測連線的時候,應該是生成像下面這樣的
https://api.domain.com/api/v1/links/{id}/click_logs?ad_id=__ADID__&android_id=__ANDROIDID__&imei_md5=__IMEI__&oaid=__OAID__
- 介面處理
- 當請求到達
https://api.domain.com/api/v1/links/{id}/click_logs?ad_id=__ADID__&android_id=__ANDROIDID__&imei_md5=__IMEI__&oaid=__OAID__
介面時 - 引數中的宏會替換成實際點選使用者的裝置值, 如:
https://api.domain.com/api/v1/links/{id}/click_logs?ad_id=123456789&android_id=123456789&imei_md5=123456789&oaid=123456789
- 介面虛擬碼:
// 統一的請求結構
class AdClickRequest { public $oaid;
public $imei;
public $imei_md5;
public $andoird_md5;
public $ad_name;
public $callback;
// xxx 更多欄位
}
const FIELDS = ['oaid', 'imei', 'imei_md5', 'xxx'];
function clickLogs($id)
{
// 1. 根據不同框架, 把資料解析到統一請求上
$req = new AdClickRequest(); // 2. 查詢監測連結表
$link = "select * from links where id = {$id}"; // $id
if (is_null($link)) { return 'FAIL'; } // 3. 寫入點選日誌表, 點選量大走佇列插入
$logModel = "insert into click_logs(`oaid`, `imei`, `events`, `exp`, 'xxx更多欄位') values({$req->oaid}, {$req->imei}, {$link->events}, {$link->exp}, 'xxx更多欄位')";
// 4. 寫入 redis $pipe = \Redis::pipeline();
$value = $logModel . '.' . $logModel->id;
foreach (FIELDS as $key) { $redisKey = sprintf('attributes:%d_%s', $link->app_id, $req->{$key});
$pipe->set($redisKey, $value, $logModel->exp*60*60*24);
} $pipe->exec();
return 'OK';
}
- 使用者首次開啟
app
上報介面
- 使用者首次開啟
class AppReportRequest { public $deviceKey;
public $oaid;
public $imei;
public $mac;
// xxx 更多欄位
}
const FIELDS = ['oaid', 'imei', 'imei_md5', 'xxx'];function appReport($appId)
{
// 0. 如果是 deepLink 拉起, 最好加一個延遲 10s 的佇列歸因, 防止`app`請求先於渠道商監測連結請求
// 1. 根據不同框架, 把資料解析到統一請求上
$req = new AppReportRequest(); // 2. 查詢 app $app = "select * apps where id={$appId}";
// 3. 查詢 redis $pipe = \Redis::pipeline();
$keys = []; foreach (FIELDS as $key) { $redisKey = sprintf('attributes:%d_%s', $app->app_id, $req->{$key});
$keys[] = $redisKey; $pipe->get($redisKey);
} // result 為一個陣列, 如果匹配到了裡面就是日誌表的表名和主鍵
$result = $pipe->exec();
$value = collect($result)->filter()->first();
if (is_null($value)) { return '歸因失敗';
}
$logModel = "select * from click_logs{$value->table} where id = {$value->id}";
// 接下來可以用佇列事件解耦之後的流程
\Redis::set("attribute_devices:{$appId}_{$req->deviceKey}", $logModel, 60*60*24*7);
// 儲存歸因成功日誌表
// 修改點選日誌狀態等等
// 刪除所有歸因的 $keys, 防止重複歸因
// 根據 $app->attribute_cycle_days 設定歸因週期
return 'SUCCESS';}
4.5.6. 這三步根據具體業務來實現即可
- 客戶端事件回傳
function appCallback($appId, $deviceKey)
{
$logModel = \Redis::get("attribute_devices:{$appId}_{$deviceKey}");
if (is_null($logModel)) { return 'FAIL'; } // 透過 $logModel->attributed_at 判斷次留是否有效, 判斷是否七日內付費
// 處理回傳的邏輯
return 'SUCCESS';}
客戶端架構
- 客戶端應該儘量不要發起沒必要的請求, 減少伺服器的壓力
- 比如播放回傳的時候, 不要每次都回傳, 應該用一下虛擬碼實現
// 假設這個是客戶端的方法, 在需要打點的地方每次都呼叫這個方法
function eventReport(event) {
// 從本地儲存獲取資料, 一定要存成 json 格式, 繼續反序列化
var data = storage.get('attribute_events');
// 如果已經上報過了, 不要上報
if (data[event]) {
return; }
// 上報介面
var res = api.post('/api/v1/event_callback', '引數');
if (res.code !== 200 || ! res.data.status) {
return; }
data[event] = true;
storage.set('attribute_events', data);
}
// 歸因上報的介面,
function attributeReport() {
// 請求介面
var res = api.post('/api/v1/attributes', '引數')
if (res.code !== 200 || ! res.data.status) {
return; }
// 把整個事件快取刪除掉,這樣子才能繼續上報
storage.delete('attribute_events');
}
後臺
後臺應該提供一個便捷的聯調頁面
輸入裝置號如
oaid, mac, imei
等
- 如果渠道商日誌沒傳送來, 那就輪詢
- 如果收到日誌, 和
API
相同的匹配流程查詢到日誌ID
- 設定裝置白名單, 解除重複歸因限制
- 提示投放, 是透過什麼歸因成功的(
oaid
), 等等其它資訊
- 事件回傳聯調, 把所有可能的事件列出來, 事件建議用
JSON
儲存, 存成{"event1": "status1", "event2": "status2"}
這樣
- 事件回傳成功, 整個歸因聯調完成
本作品採用《CC 協議》,轉載必須註明作者和本文連結