Performans Max Kampanyasında Spam Formlar Nasıl Engellenir?

P

Performans Max kampanyaları, E-ticaret odaklı kampanyalarda iyi sonuçlar versede, potansiyel müşteri kampanyalarında aynı başarıyı elde etmek oldukça zor.

E-ticarette, bir satın alma etkinliği işleminin sonunu ifade ederken, bir potansiyel müşterinin oluşturulması satış sürecinin başlangıcını temsil eder. Sadece bir formun doldurulmuş olması, onun kaliteli bir potansiyel müşteri olduğu anlamına gelmez.

Kurduğum potansiyel müşteri odaklı PMax kampanyalarında, ya doğrudan spam başvurularla karşılaşıyorum ya da başlangıçta kaliteli birkaç başvuru elde ettikten sonra kampanyam spam başvurularla dolduruluyor. Bunun nedeni, teklif stratejisinin algoritmayı ya en fazla sayıda başvuruyu elde etmeye ya da belirlenen Hedef EBM’ye uygun başvuruları getirmeye çalışmasıdır. Algoritma, bir başvurunun spam olup olmadığını ayırt edemediği için zamanla daha düşük maliyetle elde edilebilecek spam başvurulara yöneliyor.

Bu sorunu çözmek için, öncelikle spam botlarını engellememiz gerekiyor. Spam botların başarılır bir şekilde engellemesiyle, algoritmanın kaliteli ve doğru başvurulara odaklanmasını sağlayabiliriz.

Bu yazıda, PMax kampanyalarımızın dönüşüm alanlarına nasıl Captcha (robot engelleme güvenlik önlemi) ekleyeceğimizden bahsedeceğim.

Captcha Nedir?

CAPTCHA’lar, bir kullanıcının bir web sitesine veya hizmete erişimini doğrulamak için kullanılırken, kötü niyetli yazılımların ve otomatik botların bu erişimi kötü amaçlı olarak kullanmasını engellemeye yardımcı olur. Birçok capthca türü ve varyasyonu vardır.

  • Metin CAPTCHA: Metin yazma doğrulama.
  • Resim CAPTCHA: Görsel nesne seçimi.
  • Matematiksel CAPTCHA: Matematik problemleri.
  • Zaman Tabanlı CAPTCHA: Zaman sınırlı görevler.
  • İnteraktif CAPTCHA: Etkileşimli görevler (Bulmaca).
  • Vs.

Captcha uygulamaları genellikle sunucu tabanlı işlemler gerektirir. Ancak biz, sadece belirli durumlarda, örneğin Google PMax kampanyamızdan gelen kullanıcılara, Captcha’nın görünmesini istiyoruz. Bunun nedeni, bot olmayan ve satın alma niyetinde olan kullanıcılara ekstra dönüşüm adımları eklemek istemememizdir. Bu nedenle, sunucu işlevi gerektirmeyen basit bir yapı oluşturacağız.

Kurulum

Kullanacağımız captcha basit bir sürükle bırak yapbozudur, gerçek kullanıcıya bir metin girme zorundalığı vermez ve botlar için aşılması zor bir engelleme türürdür.

Adım1

Aşağıdaki kodu bir web sitenizin bulunduğu yada farklı bir sunucuda barındırın.

document.head.insertAdjacentHTML('beforeend', `
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>


  .block {
  position: absolute;
  left: 0;
  top: 0;
}

.sliderContainer {
  position: relative;
  text-align: center;
  width: 310px;
  height: 40px;
  line-height: 40px;
  margin-top: 15px;
  background: #f7f9fa;
  color: #45494c;
  border: 1px solid #e4e7eb;
}

.sliderContainer_active .slider {
  height: 38px;
  top: -1px;
  border: 1px solid #1991FA;
}

.sliderContainer_active .sliderMask {
  height: 38px;
  border-width: 1px;
}

.sliderContainer_success .slider {
  height: 38px;
  top: -1px;
  border: 1px solid #52CCBA;
  background-color: #52CCBA !important;
}

.sliderContainer_success .sliderMask {
  height: 38px;
  border: 1px solid #52CCBA;
  background-color: #D2F4EF;
}

.sliderContainer_success .sliderIcon {
  background-position: 0 0 !important;
}

.sliderContainer_fail .slider {
  height: 38px;
  top: -1px;
  border: 1px solid #f57a7a;
  background-color: #f57a7a !important;
}

.sliderContainer_fail .sliderMask {
  height: 38px;
  border: 1px solid #f57a7a;
  background-color: #fce1e1;
}

.sliderContainer_fail .sliderIcon {
  top: 14px;
  background-position: 0 -82px !important;
}

.sliderContainer_active .sliderText,
.sliderContainer_success .sliderText,
.sliderContainer_fail .sliderText {
  display: none;
}

.sliderMask {
  position: absolute;
  left: 0;
  top: 0;
  height: 40px;
  border: 0 solid #1991FA;
  background: #D1E9FE;
}

.slider {
  position: absolute;
  top: 0;
  left: 0;
  width: 40px;
  height: 40px;
  background: #fff;
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  transition: background .2s linear;
}

.slider:hover {
  background: #1991FA;
}

.slider:hover .sliderIcon {
  background-position: 0 -13px;
}

.sliderIcon {
  position: absolute;
  top: 15px;
  left: 13px;
  width: 14px;
  height: 12px;
  background: url(http://cstaticdun.126.net//2.6.3/images/icon_light.f13cff3.png) 0 -26px;
  background-size: 34px 471px;
}

.refreshIcon {
  position: absolute;
  right: 0;
  top: 0;
  width: 34px;
  height: 34px;
  cursor: pointer;
  background: url(http://cstaticdun.126.net//2.6.3/images/icon_light.f13cff3.png) 0 -437px;
  background-size: 34px 471px;
}

.container_captcha {
  width: 340px;
  margin: 100px auto;
  background: rgb(244 244 244 / 100%);
  padding: 15px;
  border-radius: 20px;
  position: fixed;
  top: 20%;
  left: 0;
  right: 0;
  z-index: 999999999999;
  
  
}

input {
  display: block;
  width: 290px;
  line-height: 40px;
  margin: 10px 0;
  padding: 0 10px;
  outline: none;
  border: 1px solid #c8cccf;
  border-radius: 4px;
  color: #6a6f77;
}

#msg {
  width: 100%;
  line-height: 40px;
  font-size: 14px;
  text-align: center;
}


.captcha-overlay {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(3px);
    z-index: 9999999999;
}

.fadeOut {
    opacity: 0;
    transition: opacity 1s;
}


</style>

`);

document.body.insertAdjacentHTML('beforeend', `
 <div class="captcha-overlay"></div>
 <div class="container_captcha">
    <div id="captcha"></div>
    <div id="msg"></div>
  </div>`)

'use strict';

var _createClass = function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

(function(window) {
  var l = 42,

    r = 9,

    w = 310,

    h = 155,

    PI = Math.PI;
  var L = l + r * 2 + 3;

  function getRandomNumberByRange(start, end) {
    return Math.round(Math.random() * (end - start) + start);
  }

  function createCanvas(width, height) {
    var canvas = createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    canvas.style.borderRadius = '8px';
    return canvas;
  }

  function createImg(onload) {
    var img = createElement('img');
    img.crossOrigin = "Anonymous";
    img.onload = onload;
    img.onerror = function() {
      img.src = getRandomImg();
    };
    img.src = getRandomImg();
    return img;
  }

  function createElement(tagName, className) {
    var elment = document.createElement(tagName);
    elment.className = className;
    return elment;
  }

  if (!("classList" in document.documentElement)) {
    Object.defineProperty(HTMLElement.prototype, 'classList', {
      get: function() {
        var self = this;
        function update(fn) {
          return function(value) {
            var classes = self.className.split(/\s+/g),
              index = classes.indexOf(value);

            fn(classes, index, value);
            self.className = classes.join(" ");
          }
        }

        return {
          add: update(function(classes, index, value) {
            if (!~index) classes.push(value);
          }),

          remove: update(function(classes, index) {
            if (~index) classes.splice(index, 1);
          }),

          toggle: update(function(classes, index, value) {
            if (~index)
              classes.splice(index, 1);
            else
              classes.push(value);
          }),

          contains: function(value) {
            return !!~self.className.split(/\s+/g).indexOf(value);
          },

          item: function(i) {
            return self.className.split(/\s+/g)[i] || null;
          }
        };
      }
    });
  }

  function addClass(tag, className) {
    tag.classList.add(className);
  }

  function removeClass(tag, className) {
    tag.classList.remove(className);
  }

  function getRandomImg() {
    return 'https://picsum.photos/300/150/?image=' + getRandomNumberByRange(0, 1084);
  }

  function _draw(ctx, x, y, operation) {
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);
    ctx.lineTo(x + l, y);
    ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI);
    ctx.lineTo(x + l, y + l);
    ctx.lineTo(x, y + l);
    ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);
    ctx.lineTo(x, y);
    ctx.lineWidth = 2;
    ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
    ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
    ctx.stroke();
    ctx[operation]();
    ctx.globalCompositeOperation = 'overlay';
  }

  function sum(x, y) {
    return x + y;
  }

  function square(x) {
    return x * x;
  }

  var jigsaw = function() {
    function jigsaw(_ref) {
      var el = _ref.el,
        onSuccess = _ref.onSuccess,
        onFail = _ref.onFail,
        onRefresh = _ref.onRefresh;

      _classCallCheck(this, jigsaw);

      el.style.position = el.style.position || 'relative';
      this.el = el;
      this.onSuccess = onSuccess;
      this.onFail = onFail;
      this.onRefresh = onRefresh;
    }

    _createClass(jigsaw, [{
      key: 'init',
      value: function init() {
        this.initDOM();
        this.initImg();
        this.bindEvents();
      }
    }, {
      key: 'initDOM',
      value: function initDOM() {
        var canvas = createCanvas(w, h);
        var block = canvas.cloneNode(true);
        var sliderContainer = createElement('div', 'sliderContainer');
        var refreshIcon = createElement('div', 'refreshIcon');
        var sliderMask = createElement('div', 'sliderMask');
        var slider = createElement('div', 'slider');
        var sliderIcon = createElement('span', 'sliderIcon');
        var text = createElement('span', 'sliderText');

        block.className = 'block';
        text.innerHTML = 'Sağa doğru sürükleyin';

        var el = this.el;
        el.appendChild(canvas);
        el.appendChild(refreshIcon);
        el.appendChild(block);
        slider.appendChild(sliderIcon);
        sliderMask.appendChild(slider);
        sliderContainer.appendChild(sliderMask);
        sliderContainer.appendChild(text);
        el.appendChild(sliderContainer);


        if (typeof Object.assign != 'function') {
          Object.defineProperty(Object, "assign", {
            value: function assign(target, varArgs) {
              'use strict';
              if (target == null) {
                throw new TypeError('Cannot convert undefined or null to object');
              }

              var to = Object(target);

              for (var index = 1; index < arguments.length; index++) {
                var nextSource = arguments[index];

                if (nextSource != null) {
                  for (var nextKey in nextSource) {
                    if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                      to[nextKey] = nextSource[nextKey];
                    }
                  }
                }
              }
              return to;
            },
            writable: true,
            configurable: true
          });
        }

        Object.assign(this, {
          canvas: canvas,
          block: block,
          sliderContainer: sliderContainer,
          refreshIcon: refreshIcon,
          slider: slider,
          sliderMask: sliderMask,
          sliderIcon: sliderIcon,
          text: text,
          canvasCtx: canvas.getContext('2d'),
          blockCtx: block.getContext('2d')
        });
      }
    }, {
      key: 'initImg',
      value: function initImg() {
        var _this = this;

        var img = createImg(function() {
          _this.canvasCtx.drawImage(img, 0, 0, w, h);
          _this.draw();
          _this.blockCtx.drawImage(img, 0, 0, w, h);
          var y = _this.y - r * 2 - 1;


          if (navigator.userAgent.indexOf("MSIE") > -1) {
            _this.block.style.marginLeft = '-' + (_this.x - 3) + 'px';
          } else {
            var ImageData = _this.blockCtx.getImageData(_this.x - 3, y, L, L);
            _this.block.width = L;
            _this.blockCtx.putImageData(ImageData, 0, y);
          }

        });
        this.img = img;
      }
    }, {
      key: 'draw',
      value: function draw() {

        this.x = getRandomNumberByRange(L + 10, w - (L + 10));
        this.y = getRandomNumberByRange(10 + r * 2, h - (L + 10));
        _draw(this.canvasCtx, this.x, this.y, 'fill');
        _draw(this.blockCtx, this.x, this.y, 'clip');
      }
    }, {
      key: 'clean',
      value: function clean() {
        this.canvasCtx.clearRect(0, 0, w, h);
        this.blockCtx.clearRect(0, 0, w, h);
        this.block.width = w;
      }
    }, {
      key: 'bindEvents',
      value: function bindEvents() {
        var _this2 = this;

        this.el.onselectstart = function() {
          return false;
        };
        this.refreshIcon.onclick = function() {
          _this2.reset();
          typeof _this2.onRefresh === 'function' && _this2.onRefresh();
        };

        var originX = void 0,
          originY = void 0,
          trail = [],
          isMouseDown = false;

        var handleDragStart = function handleDragStart(e) {
          originX = e.clientX || e.touches[0].clientX;
          originY = e.clientY || e.touches[0].clientY;
          isMouseDown = true;
        };

        var handleDragMove = function handleDragMove(e) {
          if (!isMouseDown) return false;
          var eventX = e.clientX || e.touches[0].clientX;
          var eventY = e.clientY || e.touches[0].clientY;
          var moveX = eventX - originX;
          var moveY = eventY - originY;
          if (moveX < 0 || moveX + 38 >= w) return false;
          _this2.slider.style.left = moveX + 'px';
          var blockLeft = (w - 40 - 20) / (w - 40) * moveX;
          _this2.block.style.left = blockLeft + 'px';

          addClass(_this2.sliderContainer, 'sliderContainer_active');
          _this2.sliderMask.style.width = moveX + 'px';
          trail.push(moveY);
        };

        var handleDragEnd = function handleDragEnd(e) {
          if (!isMouseDown) return false;
          isMouseDown = false;
          var eventX = e.clientX || e.changedTouches[0].clientX;
          if (eventX == originX) return false;
          removeClass(_this2.sliderContainer, 'sliderContainer_active');
          _this2.trail = trail;

          var _verify = _this2.verify(),
            spliced = _verify.spliced,
            verified = _verify.verified;

          if (spliced) {
            if (verified) {
              addClass(_this2.sliderContainer, 'sliderContainer_success');
              typeof _this2.onSuccess === 'function' && _this2.onSuccess();
            } else {
              addClass(_this2.sliderContainer, 'sliderContainer_fail');
              _this2.text.innerHTML = '再试一次';
              _this2.reset();
            }
          } else {
            addClass(_this2.sliderContainer, 'sliderContainer_fail');
            typeof _this2.onFail === 'function' && _this2.onFail();
            setTimeout(function() {
              _this2.reset();
            }, 1000);
          }
        };
        this.slider.addEventListener('mousedown', handleDragStart);
        this.slider.addEventListener('touchstart', handleDragStart);
        document.addEventListener('mousemove', handleDragMove);
        document.addEventListener('touchmove', handleDragMove);
        document.addEventListener('mouseup', handleDragEnd);
        document.addEventListener('touchend', handleDragEnd);
      }
    }, {
      key: 'verify',
      value: function verify() {
        var arr = this.trail;
        var average = arr.reduce(sum) / arr.length;
        var deviations = arr.map(function(x) {
          return x - average;
        });
        var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length);
        var left = parseInt(this.block.style.left);
        return {
          spliced: Math.abs(left - this.x) < 10,
          verified: stddev !== 0
        };
      }
    }, {
      key: 'reset',
      value: function reset() {
        this.sliderContainer.className = 'sliderContainer';
        this.slider.style.left = 0;
        this.block.style.left = 0;
        this.sliderMask.style.width = 0;
        this.clean();
        this.img.src = getRandomImg();
      }
    }]);

    return jigsaw;
  }();

  window.jigsaw = {
    init: function init(opts) {
      return new jigsaw(opts).init();
    }
  };
})(window);


jigsaw.init({
  el: document.getElementById('captcha'),
  onSuccess: function() {
    document.getElementById('msg').innerHTML = 'Başarılı!';
    document.querySelector('.container_captcha').classList.add('fadeOut');
    document.querySelector('.captcha-overlay').classList.add('fadeOut');

    setTimeout(function() {
      document.querySelector('.container_captcha').style.display = 'none';
      document.querySelector('.captcha-overlay').style.display = 'none';
    }, 1000);
  }
  ,
  onFail: cleanMsg,
  onRefresh: cleanMsg
})
function cleanMsg() {
  document.getElementById('msg').innerHTML = ''
}

Adım2

Kodumuzu GTM ile web sitemize ekleyelim. Tag manager hesabımızda yeni bir HTML etiketi oluşturalım ve aşağıdaki script içerisindeki src alanına barındığımız kodumuzun url adresini ekleyelim.

Gelişmiş ayarlar kısmından etiket etkinleştirme seçeneklerinden “sayfa başına bir defa” seçeneğini seçin seçin.

<script src="https://abdulazizgolca.com/captcha-popup/captcha-popup.js"></script>

Adım3

Tetikleyici olarak, form doldurma adımına başlarken kullanıcının ilk tıklama işlemini gerçekleştireceği “input” alanının “Click” değişkenlerinden birini girebilirsiniz.

Sadece performans max kampanyalarında kullanmak istiyorsak, pmax kampanyamızda kullandığımız UTM parametresini tetikleyicide belirtebilirsiniz.

Test

Bu adımları başarılı bir şekilde tamamladığınızda, potansiyel müşteri kampanyalarınızda spam başvurularını azaltmak ve daha yüksek kaliteli başvurular elde etmek için Captcha kullanabilirsiniz. Ancak, kullanıcı deneyimini olumsuz etkilememek için Captcha’nın kullanımını dengelemeye dikkat etmelisiniz.

Yazar hakkında

Yorum Ekle

Kategoriler

Son Yazılar

360° Dijital Pazarlama

Reklam stratejileri markaların ihtiyaçlarını ve pazar koşullarını karşılamak için tamamen analitik yöntemler, rakip analizleri, testler ve optimizasyon süreçleri ile tasarlanır.

Dijital dönüşümü çok yakından takip ediyor, Dijital dünyada var olmak isteyen markalara destek veriyorum.