Publicado por Lino Uruñuela el 1 de febrero del 2021
Desde hace unos cuántos años Google está produciendo grandes avances en su capacidad rastrear y obtener el contenido cargado por JavaScript, tal como lo haría un usuario navegando desde un dispositivo móvil. Para ello deduzco que ha debido hacer grandes cambios en su propia infraestructura ya que los recursos necesarios para realizar el renderizado del contenido conlleva un aumento muy considerable de los costes de Google.
Si antes para valorar esa página y decidir si añadirla o no a su índice le bastaba con acceder a la URL de la que obtenía el código HTML (y las imágenes que en ese código encontraba), ahora tiene que acceder tanto a la URL, como a las imágenes, como a todos los demás recursos (ficheros .js,, hojas de estilo .css, fuentes de texto, etc), es lo que fue llamado "Segunda Ola de Indexación". Y no solo eso, sino que tiene que ejecutar un navegador para renderizar y poder obtener lo mismo que obtendría un usuario desde un dispositivo móvil.
Aunque sabemos que actualmente Google es capaz de indexar el contenido cargado con JavaScript, también sabemos que no lo hace en todas las URLs ni con la misma frecuencia que cuando rastrea sin renderizar el contenido. Google renderiza del contenido <u>en aquellas URLs que detecta que hay una diferencia significativa cuándo lo hace ejecutando JavaScript y cuando no lo ejecuta</u>, y tiene sentido..¿para qué gastar recursos en ejecutar algo si sabe que el resultado final será el mismo que si no ejecuta esa serie de procesos necesarios para ello?.
Ya compartí cómo analizando los logs del servidor se podía saber cuándo Google renderiza una URL y así tener más visibilidad de lo que el buscador hace en nuestro site. Hoy quiero compartir una manera de monitorizar en Google Analytics qué URLs renderiza Google.
El método es relativamente sencillo, al menos para cualquier equipo técnico de un negocio y casi para cualquier webmaster que haya hecho alguna web en PHP o similar. Se compone de tres pasos,
Vamos a definir una función que se ejecuta en el evento onLoad de la página, es decir, cuando termina de cargar todos los elementos de la página. Esta función comprobará si es Google quién está ejecutando ese código JavaScript y si lo es, carga una imagen (ImagenRenderizadaGoogle.gif).
window.addEventListener("load", function(){ //compruebo si el User Agent del usuario coincide con alguna de estas palabras var botPattern = "googlebot|Googlebot-Mobile|Googlebot-Image|Google favicon|Mediapartners-Google"; var re = new RegExp(botPattern, 'i'); var userAgent = navigator.userAgent; //Si existe coincidencia, podría ser Google, así que cargamos el pixel transparente ImagenRenderizadaGoogle.gif if (re.test(userAgent)) { var client = new XMLHttpRequest(); //A la URL del pixel transparente le paso dos parámetros, UrlOrigen y UA, con los valores de la URL actual y del User Agent del Usuario var ErroresURL='https://modelode.com/ImagenRenderizadaGoogle.gif?UrlOrigen='+window.location.href+'&UA='+unescape(encodeURIComponent(userAgent)); client.open('GET',ErroresURL); client.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); client.send(null); } });
Ahora cada vez que Googlebot acceda a la URL y ejecute JavaScript ejecutará nuestra función la cual cargará la imagen "ImagenRenderizadaGoogle.gif" añadiendo a la URL de la imagen, la URL actual del site al a que ha entrado el usuario y el User Agent de su navegador. Eso en el código de arriba lo hacemos con esta línea concreta
var ErroresURL='https://modelode.com/ImagenRenderizadaGoogle.gif?UrlOrigen='+window.location.href+'&UA='+unescape(encodeURIComponent(userAgent));
El siguiente paso es configurar nuestro servidor para que cuándo se solicite la URL de la imagen "ImagenRenderizadaGoogle.gif" en vez de devolver la imagen ejecute un fichero PHP (RenderizadoJS.php). Si usamos un servidor Apache, lo haríamos desde el fichero .htaccess añadiendo las siguientes líneas.
RewriteCond %{REQUEST_URI} ImagenRenderizadaGoogle.gif RewriteRule ImagenRenderizadaGoogle.gif(.*)$ https://www.mecagoenlos.com/RenderizadoJS.php$1
Como podréis intuir voy propagando los parámetros de la URL de la imagen, así los recojo con el fichero PHP "RenderizadoJS.php".
Por último creamos un fichero PHP "RenderizadoJS.php" que será el que se ejecute cuándo alguien solicite la imagen "ImagenRenderizadaGoogle.gif". Este fichero se encargará de
php header("Pragma-directive: no-cache"); header("Cache-directive: no-cache"); header("Cache-control: no-cache"); header("Pragma: no-cache"); header("Expires: 0"); $src = $_SERVER['HTTP_REFERER']; $UA=$_GET["UA"]; $tipo = getimagesize("ImagenRenderizada.gif"); $imagen = "ImagenRenderizada.gif"; //Función que comprobará si realmente es Googlebot o es un bot Fake function comprobarGoogle($Ip){ $hostname=gethostbyaddr($Ip); $ip_by_hostname=gethostbyname($hostname); if(preg_match("/googlebot/i",$hostname)) if ($ip_by_hostname == $Ip) return true; else return false; else return false; } if(comprobarGoogle($_SERVER['REMOTE_ADDR'])) $esGoogle="Real"; else $esGoogle="Fake"; //No solo queremos saber que es Google sino qué tipo de bot es, Mobile, Imagenes, Ads... $botname="inicial"; $bots = array('Mediapartners-Google[ /]([0-9.]{1,10})' => 'Google Mediapartners', 'Mediapartners-Google' => 'Google Mediapartners', 'Googl(e|ebot)(-Image)/([0-9.]{1,10})' => 'Google Image', 'Googl(e|ebot)(-Image)/' => 'Google Image', '^gsa-crawler' => 'Google', 'Googl(e|ebot)(-Sitemaps)/([0-9.]{1,10})?' => 'Google-Sitemaps', 'GSiteCrawler[ /v]*([0-9.a-z]{1,10})?' => 'Google-Sitemaps', 'Googl(e|ebot)(-Sitemaps)' => 'Google-Sitemaps', 'Mobile.*Googlebot' => 'Google-Mobile', '^AdsBot-Google' => 'Google-AdsBot', '^Feedfetcher-Google' => 'Google-Feedfetcher', 'compatible; Google Desktop' => 'Google Desktop', 'Googlebot' => 'Googlebot'); foreach( $bots as $pattern => $bot ) { if ( preg_match( '#'.$pattern.'#i' , $UA) == 1 ) { $botname = preg_replace ( "/\\s{1,}/i" , '-' , $bot ); break; } } //con esta clase enviaremos los datos a Google Analytics mediante un evento class BotTracker { static function track($s, $params) $bot = ""; $data = array( 'v' => 1, 'tid' => 'UA-XXXXXX-4', 'cid' => self::generate_uuid(), 't' => 'event', 'dh' => $s['HTTP_HOST'], 'dl' => $s['REQUEST_URI'], 'dr' => $s['HTTP_REFERER'], 'dp' => $s['REQUEST_URI'], 'dt' => $params['page_title'], 'ck' => $s['HTTP_USER_AGENT'], 'uip' => $s['REMOTE_ADDR'], 'ni' => 1, 'ec' => 'RenderizadoGooglebot', 'el' => $params['UA']." -" .$params['botname']." - ".$params['esGoogle'], 'ea' => $params['URLrenderizada'] ); $url = 'http://www.google-analytics.com/collect'; $content = http_build_query($data); $ch = curl_init(); curl_setopt($ch, CURLOPT_USERAGENT, $s['HTTP_USER_AGENT']); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 0); curl_setopt($ch, CURLOPT_TIMEOUT_MS, 0); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch,CURLOPT_ENCODING , "gzip"); curl_setopt($ch, CURLOPT_POSTFIELDS, $content); $result = curl_exec($ch); $info= curl_getinfo($ch); curl_close($ch); } static private function generate_uuid() { return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0x0fff ) | 0x4000, mt_rand( 0, 0x3fff ) | 0x8000, mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) ); } } BotTracker::track($_SERVER, array("page_title"=>"Renderizado JS Google","URLrenderizada"=>$src,"esGoogle"=>$esGoogle,"botname"=>$botname,"UA"=>$UA)); ?>
Y ahora podemos ver en Google Analytics cada URL que renderiza Google. Como veréis no es contado como un usuario activo en el site, eso es porque hemos añadido el parámetro "nonInteraction" ('ni' en el protocolo de medición) por lo que el evento no generará interacción.
Y si hacemos click en la categoría del evento "RenderizadoGooglebot" veremos el User Agent, la IP y si lo hemos identificado como Real o como Fake