Quizás no lo sabías, pero hay una forma muy útil de… cuenta demo para Google analitico Puede utilizarlo para comprobar cómo funciona Google Analytics en un actual contexto empresarial (los datos son del Tienda de artículos de Google). Sin embargo, puedes acceder a la cuenta sin nada más que solo lectura acceso. Esto es molesto si desea personalizar la configuración.
No te preocupes, ¡tengo una solución para ti! Aprovecha el increíble poder de customTask
puede crear un duplicado de los datos recopilados en cualquier sitio net donde puede modificar el seguimiento (por ejemplo, a través de Administrador de etiquetas de Google). Mejor aún, los datos serán ofuscado utilizando un diccionario de palabras en inglés (puede editar esta lista) y haciendo un hash de cada cadena en la carga útil de manera predecible contra este diccionario.
Como siempre, puedes encontrar esta solución en mi Herramienta de creación de tareas personalizadas.
Muchísimas gracias a Jaakko Ojalehtomi ilustre Oveja de 8 bits colega desarrollador. Se le ocurrió el algoritmo de reemplazo de cadenas.
X
El boletín informativo de Simmer
Suscríbete a la Boletín informativo de Simmer ¡Para recibir las últimas noticias y contenidos de Simo Ahava en tu bandeja de entrada de correo electrónico!
Cómo configurarlo
Querrá obtener la última versión del código de Herramienta de creación de tareas personalizadas. Vea también las instrucciones para Cómo implementar el customTask
.
En Google Tag Supervisor, el Variable de JavaScript personalizada Terminará luciendo algo como esto:
operate () {
// customTask Builder by Simo Ahava
//
// Extra details about customTask: https://www.simoahava.com/analytics/customtask-the-guide/
//
// Change the default values for the settings under.
// obfuscate: Obfuscates your entire hit payload (utilizing a dictionary of phrases constantly) and dispatches it to the trackingId you present.
// https://bit.ly/2RectUl
var obfuscate = {
tid: 'UA-12345-1',
dict: ('tumble', 'noble', 'flourish', 'abandon', 'liberal', 'group', 'battle', 'collar', 'tiger', 'stun', 'grace', 'useful resource', 'phantom', 'think about', 'data', 'corridor', 'candy', 'agriculture', 'bingo', 'relative'),
stringParams: ('uid','ua','dr','cn','cs','cm','ck','cc','ci','gclid','dclid','dl','dh','dp','dt','cd','cg(1-5)','linkid','an','support','av','aiid','ec','ea','el','ti','ta','in','ic','iv','prd{1,3}id','prd{1,3}nm','prd{1,3}br','prd{1,3}ca','prd{1,3}va','prd{1,3}cc','prd{1,3}cdd{1,3}','tcc','pal','col','ild{1,3}nm','ild{1,3}pid{1,3}id','ild{1,3}pid{1,3}nm','ild{1,3}pid{1,3}br','ild{1,3}pid{1,3}ca','ild{1,3}pid{1,3}va','ild{1,3}pid{1,3}cdd{1,3}','promod{1,3}id','promod{1,3}nm','promod{1,3}cr','promod{1,3}ps','sn','sa','st','utc','utv','utl','exd','cdd{1,3}','xid','exp','_utmz'),
priceParams: ('tr','ts','tt','ip','prd{1,3}pr','idd{1,3}pid{1,3}pr'),
priceModifier: Math.random(),
medium: ('natural', 'referral', 'social', 'cpc'),
replaceString: operate
init: operate(){var c=();obfuscate.dict.forEach(operate
};
// DO NOT EDIT ANYTHING BELOW THIS LINE
if (typeof obfuscate === 'object' && typeof obfuscate.init === 'operate') obfuscate.init();
var readFromStorage = operate (key) {
if (!window.Storage) {
// From: https://stackoverflow.com/a/15724300/2367037
var worth = '; ' + doc.cookie;
var elements = worth.cut up('; ' + key + '=');
if (elements.size === 2) {
return elements.pop().cut up(';').shift();
}
} else {
return window.localStorage.getItem(key);
}
};
var writeToStorage = operate (key, worth, expireDays) {
if (!window.Storage) {
var expiresDate = new Date();
expiresDate.setDate(expiresDate.getDate() + expireDays);
doc.cookie = key + '=' + worth + ';expires=' + expiresDate.toUTCString();
} else {
window.localStorage.setItem(key, worth);
}
};
var globalSendHitTaskName = '_ga_originalSendHitTask';
return operate (customTaskModel) {
window(globalSendHitTaskName) = window(globalSendHitTaskName) || customTaskModel.get('sendHitTask');
customTaskModel.set('sendHitTask', operate (sendHitTaskModel) {
var originalSendHitTaskModel = sendHitTaskModel,
originalSendHitTask = window(globalSendHitTaskName),
canSendHit = true;
attempt {
if (canSendHit) {
originalSendHitTask(sendHitTaskModel);
}
// obfuscate
if (typeof obfuscate === 'object' && obfuscate.hasOwnProperty('tid') && obfuscate.hasOwnProperty('dict') && obfuscate.hasOwnProperty('stringParams') && obfuscate.hasOwnProperty('priceParams') && obfuscate.hasOwnProperty('replaceString') && obfuscate.hasOwnProperty('priceModifier')) {
var _o_hitPayload = sendHitTaskModel.get('hitPayload');
obfuscate.stringParams.forEach(operate(strParam) {
var regexParam = new RegExp('(?&)' + strParam + '=(^&)+', 'g');
var paramsInHitpayload = _o_hitPayload.match(regexParam) || ();
paramsInHitpayload.forEach(operate(keyValue) {
var elements = keyValue.cut up('=');
var urlParts = elements(1).cut up('%2F').map(operate(urlPart) {
if (/https?:/.take a look at(decodeURIComponent(urlPart))) return urlPart;
return urlPart.cut up('%20').map(operate(wordPart) {
return obfuscate.replaceString(wordPart);
}).be part of('%20');
}).be part of('%2F');
_o_hitPayload = _o_hitPayload.substitute(elements.be part of('='), elements(0) + '=' + urlParts);
});
});
obfuscate.priceParams.forEach(operate(prParam) );
_o_hitPayload = _o_hitPayload
.substitute(
'&tid=' + sendHitTaskModel.get('trackingId') + '&',
'&tid=' + obfuscate.tid + '&'
)
.substitute(/(?&)aip($|&|=(^&)*)/, '')
.substitute(/(?&)c(sm)=(^&)*/g, '')
.substitute(/(?&)uip=(^&)*/g, '');
if (Math.random() <= 0.10) {
_o_hitPayload +=
'&cs=' + obfuscate.dict(Math.ground(Math.random()*obfuscate.dict.size)) +
'&cm=' + obfuscate.medium(Math.ground(Math.random()*obfuscate.medium.size));
}
_o_hitPayload += '&uip=' +
(Math.ground(Math.random() * 255) + 1) + '.' +
(Math.ground(Math.random() * 255) + 0) + '.' +
(Math.ground(Math.random() * 255) + 0) + '.' +
(Math.ground(Math.random() * 255) + 0);
_o_hitPayload += '&aip=1';
sendHitTaskModel.set('hitPayload', _o_hitPayload, true);
originalSendHitTask(sendHitTaskModel);
}
// /obfuscate
} catch(err) {
originalSendHitTask(originalSendHitTaskModel);
}
});
};
}
Eso es bastante código, porque resulta que ofuscar los datos de manera consistente y tener cuidado con todos los demás posibles problemas que implica duplicar los datos de Google Analytics no es exactamente trivial.
De todos modos, para configurarlo, necesitarás editar el objeto de configuración dentro del var obfuscate = {...}
bloque. Aquí están las claves de configuración y cómo usarlas. ¡Nota! Se requieren todas las claves para que la solución funcione. Si elimina una de las claves, se cancelará la ofuscación.
Llave | Valor inicial | Descripción |
---|---|---|
trackingId |
UA-12345-1 |
El ID de seguimiento al que desea que se envíen los datos. En este momento, solo se admite un ID de seguimiento. |
dict |
('tumble', 'noble'...) |
El diccionario de palabras que se utilizará. No agregue demasiadas (20 deberían ser suficientes). Cuando se inicializa la función, generará automáticamente palabras compuestas a partir de cada elemento del diccionario. |
stringParams |
('uid','ua'...) |
Todos Parámetros del protocolo de medición que se tratarán como cadenas y se reemplazarán con palabras del diccionario. Los nombres de los parámetros son patrones de expresiones regulares. |
priceParams |
('tr','ts'...) |
Todos los parámetros del Protocolo de Medición que serán tratados como precios y serán modificados con el priceModifier valor (ver abajo). Los nombres de los parámetros son patrones de expresiones regulares. |
priceModifier |
Math.random() |
El modificador que se utilizará para modificar todos los precios en la carga útil. El valor inicial (Math.random() ) básicamente significa que los precios se modificarán con un porcentaje aleatorio entre 0,00 y 1,00. |
medium |
('natural', 'referral'...) |
La lista de medios de campaña que se asignarán aleatoriamente al 10 % de los resultados (para obtener alguna variación entre fuentes y medios). |
replaceString |
operate |
Función interna, No modificar. |
init |
operate |
Función interna, No modificar. |
Querrás editar trackingId
Por lo menos, las demás configuraciones tienen valores predeterminados completamente funcionales, por lo que no es necesario modificarlas a menos que lo desee. Por ejemplo, es posible que desee reescribir el dict
incluir palabras que realmente tengan que ver con alguna industria actual.
Para aprovechar al máximo sus datos, deberá agregar esto customTask
a todos los hits enviados a una propiedad de Google Analytics desde su sitio net. De esa manera, obtendrá el conjunto de datos más completo y realista.
Cómo funciona
La ofuscación en sí es bastante compleja.
Primero, cuando se ejecuta la etiqueta por primera vez, el ofuscador es inicializadoEsta inicialización básicamente toma su diccionario de palabras y genera un compuesto de cada palabra contra cada otra palabra del diccionario. Por lo tanto, la longitud last del diccionario es n + n^2
al cuadrado, donde n
es la longitud inicial del diccionario. Por ejemplo, si este es su diccionario inicial:
('child', 'rock', 'candy')
El diccionario last será:
('child', 'rock', 'candy', 'baby-baby', baby-rock', 'baby-sweet', 'rock-baby', 'rock-rock', 'rock-sweet', 'sweet-baby', 'sweet-rock', 'sweet-sweet')
La ofuscación en sí es un proceso de varios pasos.
- Primerose recorren todos los parámetros de cadena de la configuración. Si se encuentra una coincidencia en la carga útil, el valor del parámetro de cadena se convierte primero en un Base64 representación, y luego se utiliza un algoritmo easy para convertir esta cadena codificada en un número, que luego se comprime en un número de índice del diccionario.
obfuscate.stringParams.forEach(operate(strParam) {
var regexParam = new RegExp('(?&)' + strParam + '=(^&)+', 'g');
var paramsInHitpayload = _o_hitPayload.match(regexParam) || ();
paramsInHitpayload.forEach(operate(keyValue) {
var elements = keyValue.cut up('=');
var urlParts = elements(1).cut up('%2F').map(operate(urlPart) {
if (/https?:/.take a look at(decodeURIComponent(urlPart))) return urlPart;
return urlPart.cut up('%20').map(operate(wordPart) {
return obfuscate.replaceString(wordPart);
}).be part of('%20');
}).be part of('%2F');
_o_hitPayload = _o_hitPayload.substitute(elements.be part of('='), elements(0) + '=' + urlParts);
});
});
Esto significa que cada cadena tendrá una contraparte consistente en el diccionario. Algunas cadenas devolverán naturalmente la misma palabra del diccionario, pero eso no es un problema ya que no buscamos una trazabilidad perfecta y esto también hará que sea aún más difícil realizar ingeniería inversa de las cadenas traducidas para devolverlas a sus representaciones originales.
Si se encuentra que la cadena tiene una /
símbolo, cada palabra separada por la barra se traducirá por separado. De esta manera, las URL se mantendrán intactas. De manera related, si la cadena tiene http:
o https:
entonces el protocolo será no ser traducido, porque GA requiere URL válidas en ciertos parámetros.
Finalmente, si las cadenas están compuestas de palabras (separadas por espacios), cada palabra se traduce por separado.
- Próximolos parámetros de precio se comparan de manera related con la carga útil del impacto. Si se produce una coincidencia, el precio se modifica según el
priceModifier
Desde la configuración. Cada precio que utilice este rastreador se modifica con el mismo modificador.
obfuscate.priceParams.forEach(operate(prParam) ();
paramsInHitpayload.forEach(operate(keyValue) 0.00;
worth = (worth * obfuscate.priceModifier).toFixed(2);
_o_hitPayload = _o_hitPayload.substitute(elements.be part of('='), elements(0) + '=' + worth);
);
);
- Entoncesel ID de seguimiento en la carga útil se reemplaza por el que proporciona en el objeto de configuración. Al mismo tiempo, los parámetros
aip
,cs
,cm
yuip
(para anonimizar IP, origen de campaña, medio de campaña y anular IP, respectivamente) se eliminan de la carga útil.
_o_hitPayload = _o_hitPayload
.substitute(
'&tid=' + sendHitTaskModel.get('trackingId') + '&',
'&tid=' + obfuscate.tid + '&'
)
.substitute(/(?&)aip($|&|=(^&)*)/, '')
.substitute(/(?&)c(sm)=(^&)*/g, '')
.substitute(/(?&)uip=(^&)*/g, '');
- FinalmenteAl 10% de todos los resultados se les asigna una fuente de campaña aleatoria (del diccionario), con un medio aleatorio de la lista de
medium
Usted proporcionó en la configuración.
Además, se genera una dirección IP aleatoria para cada acceso. Sí, para cada acceso.
Luego, la dirección IP se anonimiza con el parámetro Anonymize IP.
if (Math.random() <= 0.10) {
_o_hitPayload +=
'&cs=' + obfuscate.dict(Math.ground(Math.random()*obfuscate.dict.size)) +
'&cm=' + obfuscate.medium(Math.ground(Math.random()*obfuscate.medium.size));
}
_o_hitPayload += '&uip=' +
(Math.ground(Math.random() * 255) + 1) + '.' +
(Math.ground(Math.random() * 255) + 0) + '.' +
(Math.ground(Math.random() * 255) + 0) + '.' +
(Math.ground(Math.random() * 255) + 0);
_o_hitPayload += '&aip=1';
Modificando las direcciones IP de esta manera obtenemos datos interesantes en la lista de proveedores de servicios:
Lo último que ocurre es que el golpe es enviado al ID de seguimiento que usted proporcionó.
sendHitTaskModel.set('hitPayload', _o_hitPayload, true);
originalSendHitTask(sendHitTaskModel);
Advertencias
No es un perfecto Duplicación de datos. A continuación, se indican algunos problemas con el script:
-
Se elimina toda la información de la campaña del hit authentic, por lo que la asignación de información de fuente/medio no seguirá la lógica de la cuenta authentic. Para contrarrestar esto, genero una fuente/medio aleatorio para el 10 % de todos los hits.
-
Los precios se modifican con el mismo porcentajeno el mismo valor. Por lo tanto, si tiene un ingreso por transacción de
10.00
y los ingresos por productos de8.00
y el modificador es0.8
el resultado last será un Ingreso por Transacción de8.00
y los ingresos por productos de6.40
Esto significa que alguien podría deducir cuál period el precio authentic, si asumieran, por ejemplo, que los Ingresos por Transacción son la suma complete de todos los Ingresos por Productos multiplicados por sus cantidades (como suele suceder). -
No se modifican los valores enteros, por lo que no se modifican las métricas personalizadas, los valores de eventos, las cantidades, and so forth. Hice esto porque no creo que los números enteros codifiquen información que pueda usarse para identificar la fuente authentic de los datos. Los precios se modifican porque con un conjunto específico de precios un usuario podría adivinar cuál period el origen de los datos, pero no tanto con los números enteros. Me complace modificar esto en el futuro si suficientes personas lo consideran necesario.
Pensamientos finales
Si esta solución es útil o no, puedo garantizar que escribirla me llevó mucho tiempo. divertido! Hubiera sido fácil simplemente ofuscar los datos. Solo había que enmascarar cada cadena con un GUID aleatorio o algo así. Pero intentar averiguar una sustitución de diccionario period mucho más difícil.
El algoritmo que elegí (con la ayuda de Jaakko Ojalehto) para el reemplazo no es perfecto. La distribución no es uniforme, pero creo que está bien. De todos modos, solo terminarás con 420 palabras por defecto, por lo que habrá MUCHA superposición, ya que incluso un sitio easy producirá mucho más de 420 cadenas únicas en los datos.
Incluso si no encuentra útil este conjunto de datos, le garantizo que se divertirá mirando las combinaciones de cadenas producidas por el algoritmo de reemplazo. De hecho, tuve que modificar el diccionario que tenía inicialmente, porque dio como resultado compuestos como beat-child sweet-laughter
Lo cual creo que podría generar sorpresa cuando los datos se muestren en una sesión de entrenamiento.
¡Déjame saber en los comentarios si esta solución necesita mejorar!