做回流已经快两年了,遇到了不少问题,也解决了不少问题。然而总会有些问题不管难易你都不愿意去触碰的,付费推广便是其中之一。

花钱

从个人角度来看,对推广是断无好感。主要包含两方面原因

  1. 领域距离自己的太遥远,熟悉起来太麻烦。
  2. 前景不确定且太烧钱。

其实第二点占的比重会比较大,哪天你发现推广一天的钱能够抵得上你一个月工资的时候,我相信你也会跟我有一样的感觉。不过个人感受是一方面,从公司角度来看,推广这个事还是很有必要的,特别是当产品已经基本成型需要快速建立公信力的时候,还真的就是要花点钱的时候。

目前回流需要开发介入的推广主要有

  1. 抖音/头条广告推广(巨量平台)。
  2. 百度推广(百度营销平台)。
  3. Vivo推广(Vivo营销平台)。
  4. Oppo推广(Oppo营销平台)。

推广简介

总的来说推广其实还是一门策略活,技术在其中起到的作用还是相对有点少的,只是多少还是有点需要技术支持的地方。简单来说,推广就是付钱给某一个服务商(比如百度/抖音),然后让这个服务商可以在他的公开平台上面展示(广告位的形式)我们提供的素材,当用户被素材所吸引就会点击广告并下载我们的应用程序。那么现在问题来了,如果用户只下载了App那么那么对于商城来说一点意义都没有,假设我们推广的目的是拉取真实的付费用户,那么我们需要用户在看到素材之后完成以下行为

  1. 点击素材进入下载页面。
  2. 从下载页下载APP。
  3. 在App注册用户。
  4. 购买商城产品并付费。

这四个步骤将形成一条链条。只有一个用户完成这个完整的链条,才能算是一个我们期望的目标用户。

当然这会存在一个问题,现实生活中用户的行为千奇百怪,很多用户可能就只是点进来然后就离开了,也有些用户可能下载了App但是没有下文,也有些用户可能注册了但是没有购买东西。这些都要统计下来。比方说我们观察到,通过广告渠道下载了我们App,并且有注册行为的用户有10000个,其中真正购买并且付费只有10个。那么注册到付费的转化率就很低,只有1%。这种时候推广的同事可能会去调整素材以及广告投放策略,或者跟技术部商量看怎么优化购买流程以提高转化率。

技术介入

从上面的推广概述可以得知,技术需要介入的地方其实就只有给应用设置“埋点”。

  • 用户下载并安装App成功的时候设置一个埋点,等用户完成这个步骤把上送数据。
  • 用户注册的时候设置一个埋点,等用户完成注册之后上送数据。
  • 用户下单并支付后设置一个埋点,等用户完成购买之后上送数据。

这里有个问题需要特别注意一下,以上三个事件是任何付费用户都必然要触发的。然而我们广告的推广渠道很多,有抖音推广,百度推广,Vivo,Oppo等等。我们应该怎么区分用户来自哪个广告渠道呢?这就需要一点开发能力了,其实任何形式的广告都会以素材的方式展现,用户看到广告觉得有趣就会点击广告进来,并下载应用,更有甚者会走完整个支付流程。

关键在于点击素材进入下载页面的时候,为了方便后面的埋点,一般推广服务商都会有“监控链接”或“点击监控”的概念,该链接需要广告主(也就是我们)进行开发。当用户点击素材广告的时候,会顺便触发这个监控链接。一个监控链接大概是长这样

# 苹果

https://xxxxx.huiliu.net/api/v1/oceanengine_assistants/click?aid=__AID__&cid=__CID__&idfa=__IDFA__&mac=__MAC__&os=__OS__&TIMESTAMP=__TS__&callback=__CALLBACK_PARAM__&ip=__IP__&ua=__UA__
# 安卓

https://xxxxx.huiliu.net/api/v1/oceanengine_assistants/click?aid=__AID__&cid=__CID__&oaid=__OAID__&imei=__IMEI__&mac=__MAC__&os=__OS__&TIMESTAMP=__TS__&callback=__CALLBACK_PARAM__&ip=__IP__&ua=__UA__

用户一旦点击广告,广告服务商便会访问该链接,并替换掉__XXX__这些占位内容,把点击用户的一些关键信息传导过来。对于苹果用户来说比较重要的就是idfa,而对于安卓用户来说比较重要的就是imei或者oaid(最近刚发现一个androidid)。可以把这些值想像成是设备唯一标识,我们作为广告主,需要把这些信息存储下来以供下次使用。

接下来当某个用户,下载并安装了App的时候,我们理应把“激活”这个事件上送给到服务商去。这个时候我们需要查看之前的点击记录,看看某个设备(通过idfa/imei/oaid来识别),之前是否有过广告点击行为,如果有,则往该服务商上送激活事件。如果没有对应记录,表明这个用户并没有点击过服务商的广告,不能算是该推广渠道拉来的用户,则不上送数据到该服务商去。

总的来说技术要做的事情其实很简单

  1. 记录点击广告那位用户的设备信息。
  2. 查看记录的用户信息,如果记录存在表明用户点击过广告,可以向该广告商上送用户相关的行为(“激活”,“注册”, “下单”等)。

技术的挑战

上一个章节大概讲述了广告推广的过程中需要技术介入的方面,虽说看起来很简单,不过也面临了不少挑战,这里简单总结一下。手机设备都是有一定的局限性的,并且不同的厂家基于隐私的考虑,获取设备唯一标识的限制也会不同。

就拿iPhone手机来做个例子,iPhone手机在广告推广中常用的唯一性标识就是IDFA。然而苹果公司基于用户隐私的考虑,不会让我们随意获取这个值,要获取这个值需要经过用户的同意。具体可以这里

想象一下,当我们通过巨量引擎来做推广的时候,广告一般都会被展示在抖音App或者头条App,当用户点击该广告并下载我们的App之后,广告追踪的过程就可能有以下这几种情况

  1. 抖音/头条App开启了广告追踪权限,广告主App也开启了广告追踪。那么用户从广告点击到后面的一系列行为都能跟踪得到。
  2. 抖音/头条App开启了广告追踪权限,广告主App没有开启广告追踪。那么用户点击广告的时候会把它的设备标识记录下来,然而后面的一系列步骤由于找不到一对一的匹配记录,则无法追踪得到。
  3. 抖音/头条关闭了广告追踪权限,广告主App开启广告追踪权限。跟第二种情况类似,只不过这次是点击广告的时候,没有记录到唯一标识,哪怕后面用户使用我们自家App的时候开启广告追踪,依旧是没办法把前后的两个用户通过设备标识联系起来。
  4. 抖音/头条关闭了广告追踪,广告主App也关闭了广告追踪权限。如果用户少的情况下,在某种意义上确实能够把两者匹配起来(可能前后的设备标识都是空字符串,或者前后设备标识都是'00000-00000'这种形式),但设备一多就没用了。

在某种意义上来说,只有第一种场景(相对理想)广告追踪才有意义。然而其实大部分用户都不太愿意开启广告追踪权限的,这会导致广告追踪的效果不如预期。头条没有提供解决方案,不过百度倒是给了我们一个比较不错的建议。

匹配转化数据过程中因imei、oaid、idfa不能100%获取,建议在设备匹配的基础上增加ip+ua方式进行激活数据拼接。

简单来说就是先通过设备标识来匹配,设备标识存在且能找得到记录的,用户点击广告以及后面的行为就都能追踪得到。当找不到对应记录的时候,可以通过用户使用的IP地址以及UA来找。

确实有一些道理,因为UA(User-Agent)跟IP地址这套组合相对固定,虽说没有设备标识的唯一性靠谱,不过作为追踪的辅助还是可以的。UA这个信息相对固定,一般都不会变。IP却会有点问题,比如用户在点击广告,准备下载App的时候觉得网络不好,切换了网络,再去下载APP,这样IP地址可能会变动,如此UA+IP的组合还是无法追踪得到,不过把这当作一个补充方案还是不错的。以下是我写的一部分代码,是给百度写的,原理其实大同小异

广告点击

class BaiduAssistantsController < ApplicationController
  def click
    # 存储关键信息
    assistant = BaiduAssistant.where(idfa: params[:idfa], os: params[:os], oaid: params[:oaid], imei: params[:imei]).first_or_initialize
    assistant.ua = params[:ua]
    assistant.ip = params[:ip]
    assistant.ip_type = params[:ip_type]
    assistant.click_id = params[:click_id]
    assistant.ext_info = params[:ext_info]
    assistant.device_info = params[:device_info]
    assistant.save!

    json_response({ msg: '缓存成功' })
  end
end

查找广告点击记录

class BaiduAssistantsController < ApplicationController
  def query
    assistant = nil
    assistants = []

    # 根据oaid或者idfa查找
    case params[:os].to_i
    when 1
      assistants = BaiduAssistant.where(os: params[:os], idfa: params[:device_id])
      assistant = assistants.last
    when 2
      assistants = BaiduAssistant.where(os: params[:os]).where('oaid = ? or imei = ?', params[:device_id], params[:device_id])
      assistant = assistants.last
    end

    # 需要前端手动传过来
    user_agent = request.headers['X-ORIGINAL-UA']
    # 如果相同的记录太多,那可能是空字符串的场景很多,则需要在原来记录的基础上根据 ip + ua进行筛选
    if assistants.count > 1
      replace_object = assistants.where(ip: request.remote_ip, ua: user_agent).first
      assistant = replace_object if replace_object
    end

    # 依旧没有记录则通过 ip + ua 进行查找
    assistant ||= BaiduAssistant.where(ip: request.remote_ip, ua: user_agent).last

    return json_response({ msg: '找不到资源' }, 404) unless assistant

    # 根据不同的 a_type(用户行为)进行 数据上送。
    res = RestClient.get(assistant.package_click_callback_url(
                     a_type: params[:a_type],
                     a_value: params[:a_value]
                   ))

    data = JSON.parse(res.body)

    raise Exceptions::InvalidAction, data['error_msg'] unless data['code'].to_i.zero?

    json_response({ msg: '请求成功' })
  end
end

技术也只能支持到这里,这代码不一定是最好的,只能边跑边调整了,可以通过分析日志渐渐去优化代码。

PS: 后来发现头条/抖音的做法比较流氓,只要用户在他们的App开启一次广告追踪的权限,哪怕后面关闭了,即便卸载App重装,点击广告的时候都能获取到设备的标识。具体怎么做的我们还没破解,虽然有点流氓,不过其实很有效。

尾声

这篇文章简单总结了一下近期给推广部门做技术支持的一些心得。总的来说过程说不上顺畅,毕竟两个不同性质的部门所使用的“语言”都是不一样的,磨合起来确实要花点时间,需要双方都多一些耐心了。总的来看,最早对接的是巨量,同时也最痛苦,百度就还行,Oppo跟Vivo一言难尽。

参考资料