Установка и настройка контейнера Google Optimize. Сравнение вариантов.

Давно вы залезали в доки гугла? Иногда мне кажется, что разночтений у них больше, чем у Библии, и они точно так же плодятся с добавлением новых глав и не слишком качественными переводами с языка оригинала. Сегодня попробуем разобраться в некоторых статьях, а заодно замутим ролевую игру с собаками и Павловым.

Тонкости настройки аналитики в Telegram канале

Содержание

  1. Как создать эксперимент в Google Optimize
  2. Установка Optimize через Tag Manager
  3. Установка Optimize через сниппет Analytics
  4. Сравнение скорости загрузки
  5. В заключение
  6. Комментарии

Как создать эксперимент в Google Optimize

Сперва конечно же нужно в нем зарегистрироваться. После простановки всех галочек вы попадете на страницу контейнера

Контейнер Google Optimize

Здесь справа написано, что нужно потыкать в интерфейсе, и лучше я объяснить не в силах, а вот что мы рассмотрим подробно, так это установку на сайт. Есть две категории людей два варианта:

  1. Подключить контейнер Optimize в коде сниппета установки Google Analytics
  2. Создать тег в Google Tag Manager (но в код сайта придется лезть все равно)

Установка Optimize через Tag Manager

Страница в доках https://support.google.com/tagmanager/answer/7164339?hl=ru
Возьмите идентификатор контейнера Optimize, который скрывается под первым пунктом на предыдущем скрине, и создайте вот такой тег:

Подключение Google Optimize через Google Tag Manager

Теперь идем в настройки тега Google Analytics и указываем, что перед ним нужно активировать тег Optimize:

Подключение Google Optimize через Google Tag Manager

Если ваш эксперимент каким-то образом переделывает внешний вид сайта, то скорее всего пользователь при загрузке увидит скачущие элементы, и это может повлиять на результат. Чтобы подобного не произошло, Google предлагает вот такой костыль:

<!-- Page-hiding snippet (recommended)  -->
<style>
  .async-hide {
    opacity: 0 !important;
  }
</style>
<script>
  ;(function (a, s, y, n, c, h, i, d, e) {
    s.className += ' ' + y
    h.start = 1 * new Date()
    h.end = i = function () {
      s.className = s.className.replace(RegExp(' ?' + y), '')
    }
    ;(a[n] = a[n] || []).hide = h
    setTimeout(function () {
      i()
      h.end = null
    }, c)
    h.timeout = c
  })(window, document.documentElement, 'async-hide', 'dataLayer', 4000, {
    'GTM-XXXXXX': true,
  })
</script>
// здесь мог быть id именно вашего контейнера GTM

Он скрывает страницу на время внесения изменений. Обратите внимание, что в коде указывается id контейнера GTM, а не Optimize, если Optimize вы подключаете через GTM. Этот код рекомендуется вставлять сразу после meta тегов на сайте. Если изменение страницы занимает слишком много времени, то оно прерывается, пользователю показывается исходный вариант, и его участие в эксперименте отменяется.

Установка Optimize через сниппет Analytics

**Страница в доках ** https://support.google.com/360suite/optimize/answer/6262084?hl=ru
В этом случае просто подключите свой контейнер, добавив строку между созданием счетчика и отправкой pageview:

<script>
  ;(function (i, s, o, g, r, a, m) {
    i['GoogleAnalyticsObject'] = r
    ;(i[r] =
      i[r] ||
      function () {
        ;(i[r].q = i[r].q || []).push(arguments)
      }),
      (i[r].l = 1 * new Date())
    ;(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0])
    a.async = 1
    a.src = g
    m.parentNode.insertBefore(a, m)
  })(
    window,
    document,
    'script',
    'https://www.google-analytics.com/analytics.js',
    'ga'
  )

  ga('create', 'UA-XXXXXXXX-X', 'auto')
  ga('require', 'GTM-XXXXXX') // код контейнера Optimize
  ga('send', 'pageview')
</script>

Код, делающий хорошо, в этом случае такой же, только вместо id GTM, в нем нужно указать идентификатор контейнера Optimize.

<!-- Page hiding snippet (recommended)  -->
<style>
  .async-hide {
    opacity: 0 !important;
  }
</style>
<script>
  ;(function (a, s, y, n, c, h, i, d, e) {
    s.className += ' ' + y
    h.start = 1 * new Date()
    h.end = i = function () {
      s.className = s.className.replace(RegExp(' ?' + y), '')
    }
    ;(a[n] = a[n] || []).hide = h
    setTimeout(function () {
      i()
      h.end = null
    }, c)
    h.timeout = c
  })(window, document.documentElement, 'async-hide', 'dataLayer', 4000, {
    'GTM-XXXXXX': true,
  })
</script>
// идентификатор контейнера Optimize

Сравнение скорости загрузки

Конечно все эти эксперименты на клиентской стороне по большей части полная ерунда, но если уж пришлось строить из говна и палок,  то давайте хотя бы выберем сорта получше. Чтобы отвлечься от тонны отчетов на работе определить оптимальный вариант, я написал скрипт для автоматического тестирования, сделал несколько сотен прогонов и вот результаты:

GA GTM
Тестов 165 162
Среднее время (мс) 414 753

Таблица с результатами

Хотя вариант с GTM и грузится в два раза дольше, в реальности разница составляет всего 0,3 секунды, так что можно спокойно забить. Тем не менее вот инструкция, как повторить тест в домашних условиях:

  1. создаем две страницы с установкой через сниппет GA и через контейнер GTM
  2. рассчитываем время от начальной загрузки страницы до отработки изменения в контейнере Optimize
  3. собираем результаты в Google Spreadsheet
  4. зацикливаем, чтобы было весело, запускаем и смотрим

Код страницы с GTM

<html>
  <head>
    <script>
      window.a = new Date().getTime()
    </script>
    <style>
      .async-hide {
        opacity: 0 !important;
      }
    </style>
    <script>
      ;(function (a, s, y, n, c, h, i, d, e) {
        s.className += ' ' + y
        h.start = 1 * new Date()
        h.end = i = function () {
          s.className = s.className.replace(RegExp(' ?' + y), '')
        }
        ;(a[n] = a[n] || []).hide = h
        setTimeout(function () {
          i()
          h.end = null
        }, c)
        h.timeout = c
      })(window, document.documentElement, 'async-hide', 'dataLayer', 4000, {
        'GTM-XXXXXXXX': true,
      })
    </script>
    // укажите свой id GTM, не Optimize

    <!-- Google Tag Manager -->
    <script>
      ;(function (w, d, s, l, i) {
        w[l] = w[l] || []
        w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' })
        var f = d.getElementsByTagName(s)[0],
          j = d.createElement(s),
          dl = l != 'dataLayer' ? '&l=' + l : ''
        j.async = true
        j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl
        f.parentNode.insertBefore(j, f)
      })(window, document, 'script', 'dataLayer', 'GTM-XXXXXXXX')
    </script>
    // укажите свой id GTM
    <!-- End Google Tag Manager -->
  </head>
  <body>
    <!-- Google Tag Manager (noscript) -->
    <noscript
      ><iframe
        src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXXX"
        height="0"
        width="0"
        style="display:none;visibility:hidden"
      ></iframe
    ></noscript>
    <!-- End Google Tag Manager (noscript) -->
  </body>
</html>

Код страницы c GA

<html>
  <head>
    <script>
      window.a = new Date().getTime()
    </script>
    <style>
      .async-hide {
        opacity: 0 !important;
      }
    </style>
    <script>
      ;(function (a, s, y, n, c, h, i, d, e) {
        s.className += ' ' + y
        h.start = 1 * new Date()
        h.end = i = function () {
          s.className = s.className.replace(RegExp(' ?' + y), '')
        }
        ;(a[n] = a[n] || []).hide = h
        setTimeout(function () {
          i()
          h.end = null
        }, c)
        h.timeout = c
      })(window, document.documentElement, 'async-hide', 'dataLayer', 4000, {
        'GTM-XXXXXXX': true,
      })
    </script>
    // id контейнера Optimize

    <script>
      ;(function (i, s, o, g, r, a, m) {
        i['GoogleAnalyticsObject'] = r
        ;(i[r] =
          i[r] ||
          function () {
            ;(i[r].q = i[r].q || []).push(arguments)
          }),
          (i[r].l = 1 * new Date())
        ;(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0])
        a.async = 1
        a.src = g
        m.parentNode.insertBefore(a, m)
      })(
        window,
        document,
        'script',
        'https://www.google-analytics.com/analytics.js',
        'ga'
      )
      ga('create', 'UA-AAAAAAAA', 'auto') // id счетчика GA
      ga('require', 'GTM-YYYYYYY') // id контейнера Optimize
      ga('send', 'pageview')
    </script>
  </head>
  <body>
    Hello, test!
  </body>
</html>

В настройках эксперимента в Optimize нужно выставить распределение 99,9% трафика на измененный вариант, чтобы скрипт всегда отрабатывал. Вот сам скрипт:

const b = new Date().getTime() - window.a
const c = ~document.location.href.indexOf('gtm') ? 'GTM' : 'Site'
const s = 'GTM' === c ? 'site' : 'gtm'
const t = !!~document.location.href.indexOf('test')

var promise = new Promise(function (resolve, reject) {
  var img = new Image()
  img.src =
    'https://script.google.com/macros/s/AKfycbzSWcVj6kDobWCEK0qzljfNsfqCrpqpjBBVvd2GHyzohRp9N7W0/exec?s=' +
    c +
    '&Time=' +
    b
  resolve()
})
promise.then(() => {
  if (!t) location.replace('https://burgerdata.com/exp/' + s + '/')
})

В этом куске я беру глобальную переменную window.a, которая определяется при создании самих страниц и содержит время в миллисекундах  на тот момент, и вычитаю из него текущее время. Результат отправляется в созданное приложение в Apps Script, который записывает его в таблицы Spreadsheet. Обратите внимание, что я использую вкладки GTM и Site, куда направляются результаты со страницы /gtm/ и /site/. Присутствует одна колонка с заголовком Time. Если захотите сравнить что-то еще, добавьте новую колонке и новый параметр в вызове скрипта.

Код приложения Apps Script:

var SHEET_KEY = "16FnXWKhtvGHyfqMjWy-PqIWpbm83M7VxGloAUNlrv2M"; // идентификатор таблицы, берется из url

function doGet(e){
  SHEET_NAME = e.parameter.s;
  var lock = LockService.getDocumentLock();
  lock.waitLock(3000);
  try {
    var doc = SpreadsheetApp.openById(SHEET_KEY);
    var sheet = doc.getSheetByName(SHEET_NAME);
    var headRow = e.parameter.header || 1; //параметр header определяет ряд заголовков, по умолчанию первая строка
    var headers = sheet.getRange(headRow, 1, 1, sheet.getLastColumn()).getValues()[0];
    var row = [];
    for (i in headers){
      var l = e.parameter[headers[i]];
      row.push(l);
    }
    sheet.getRange(sheet.getLastRow() + 1, 1, 1, row.length).setValues(\[row\]);
  } catch(e){
    return ContentService
          .createTextOutput(JSON.stringify({"result":"error", "error": e}))
          .setMimeType(ContentService.MimeType.JSON);
  } finally {
    lock.releaseLock();
  }
}

В заключение

Все это ерунда, читайте про серверный вариант  https://developers.google.com/analytics/solutions/experiments-server-side