Cómo utilizar Python para analizar y pivotar archivos de registro del servidor para SEO

Los datos de rastreo de motores de búsqueda que se encuentran dentro de los archivos de registro son una fuente fantástica de información para cualquier profesional de SEO.

Al analizar los archivos de registro, puede comprender exactamente cómo los motores de búsqueda están rastreando e interpretando su sitio web, claridad que simplemente no puede obtener cuando confía en herramientas de terceros.

Esto le permitirá:

  • Valide sus teorías proporcionando evidencia indiscutible de cómo se están comportando los motores de búsqueda.
  • Priorice sus hallazgos ayudándole a comprender la escala de un problema y el impacto probable de solucionarlo.
  • Descubra problemas adicionales que no son visibles cuando utiliza otras fuentes de datos.

Pero a pesar de la multitud de beneficios, los datos de los archivos de registro no se utilizan con tanta frecuencia como deberían. Las razones son comprensibles:

  • El acceso a los datos generalmente implica pasar por un equipo de desarrollo, lo que puede tomar tiempo.
  • Los archivos sin procesar pueden ser enormes y se proporcionan en un formato poco amigable, por lo que el análisis de los datos requiere esfuerzo.
  • Es posible que las herramientas diseñadas para facilitar el proceso deban integrarse antes de que los datos puedan ser canalizados, y el costos puede ser prohibitivo.

Anuncio publicitario

Continuar leyendo a continuación

Todos estos problemas son barreras de entrada perfectamente válidas, pero no tienen por qué ser insuperables.

Con un poco de conocimiento básico de codificación, puede automatizar todo el proceso. Eso es exactamente lo que haremos en esta lección paso a paso sobre el uso de Python para analizar los registros del servidor para SEO.

También encontrará un guión para comenzar.

Consideraciones iniciales

Uno de los mayores desafíos al analizar los datos de los archivos de registro es la gran cantidad de formatos potenciales. Apache, Nginx e IIS ofrecen una variedad de opciones diferentes y permiten a los usuarios personalizar los puntos de datos devueltos.

Para complicar aún más las cosas, muchos sitios web ahora utilizan proveedores de CDN como Cloudflare, Cloudfront y Akamai para ofrecer contenido desde la ubicación de borde más cercana a un usuario. Cada uno de estos también tiene sus propios formatos.

Nos centraremos en el formato de registro combinado para esta publicación, ya que es el predeterminado para Nginx y una opción común en los servidores Apache.

Anuncio publicitario

Continuar leyendo a continuación

Si no está seguro de qué tipo de formato está tratando, servicios como Builtwith y Wappalyzer brindan información excelente sobre la pila tecnológica de un sitio web. Pueden ayudarlo a determinar esto si no tiene acceso directo a un interesado técnico.

¿Aún no te das cuenta? Intente abrir uno de los archivos sin formato.

A menudo, los comentarios se proporcionan con información sobre los campos específicos, que luego pueden tener referencias cruzadas.

#Fields: time c-ip cs-method cs-uri-stem sc-status cs-version 
17:42:15 172.16.255.255 GET /default.htm 200 HTTP/1.0

Otra consideración es qué motores de búsqueda queremos incluir, ya que esto deberá tenerse en cuenta en nuestro filtrado y validación inicial.

Para simplificar las cosas, nos centraremos en Google, dada su participación dominante en el mercado estadounidense del 88%.

Empecemos.

1. Identificar archivos y determinar formatos

Para realizar un análisis SEO significativo, queremos un mínimo de ~ 100k solicitudes y datos de 2 a 4 semanas para el sitio promedio.

Debido al tamaño de los archivos involucrados, los registros generalmente se dividen en días individuales. Está prácticamente garantizado que recibirá varios archivos para procesar.

Como no sabemos con cuántos archivos trataremos a menos que los combinemos antes de ejecutar el script, un primer paso importante es generar una lista de todos los archivos en nuestra carpeta usando el módulo glob.

Esto nos permite devolver cualquier archivo que coincida con un patrón que especifiquemos. Como ejemplo, el siguiente código coincidiría con cualquier archivo TXT.

import glob

files = glob.glob('*.txt')

Los archivos de registro se pueden proporcionar en una variedad de formatos de archivo, sin embargo, no solo TXT.

De hecho, a veces la extensión del archivo puede no ser una que usted reconozca. Aquí hay un archivo de registro sin procesar del servicio de entrega de registros de Akamai, que lo ilustra perfectamente:

bot_log_100011.esw3c_waf_S.202160250000-2000-41

Además, es posible que los archivos recibidos se dividan en varias subcarpetas y no queremos perder el tiempo copiándolos en una ubicación única.

Afortunadamente, glob admite tanto búsquedas recursivas como operadores comodín. Esto significa que podemos generar una lista de todos los archivos dentro de una subcarpeta o subcarpetas secundarias.

Anuncio publicitario

Continuar leyendo a continuación

files = glob.glob('**/*.*', recursive=True)

A continuación, queremos identificar qué tipos de archivos están dentro de nuestra lista. Para hacer esto, se puede detectar el tipo MIME del archivo específico. Esto nos dirá exactamente con qué tipo de archivo estamos tratando, independientemente de la extensión.

Esto se puede lograr usando python-magic, un envoltorio alrededor de la biblioteca libmagic C y creando una función simple.

pip install python-magic
pip install libmagic
import magic

def file_type(file_path):
    mime = magic.from_file(file_path, mime=True)
    return mime

La comprensión de listas se puede utilizar para recorrer nuestros archivos y aplicar la función, creando un diccionario para almacenar tanto los nombres como los tipos.

file_types = [file_type(file) for file in files]

file_dict = dict(zip(files, file_types))

Finalmente, una función y un bucle while para extraer una lista de archivos que devuelven un tipo de texto / plano MIME y excluyen cualquier otra cosa.

uncompressed = []

def file_identifier(file):
    for key, value in file_dict.items():
        if file in value:
            uncompressed.append(key)

while file_identifier('text/plain'):
    file_identifier('text/plain') in file_dict

Identificación de archivo.

2. Extraiga las solicitudes del motor de búsqueda

Después de filtrar los archivos en nuestra (s) carpeta (s), el siguiente paso es filtrar los archivos en sí mismos extrayendo solo las solicitudes que nos interesan.

Anuncio publicitario

Continuar leyendo a continuación

Esto elimina la necesidad de combinar los archivos utilizando utilidades de línea de comandos como GREP o FINDSTR, lo que ahorra una búsqueda inevitable de 5 a 10 minutos a través de las pestañas y marcadores abiertos del cuaderno para encontrar el comando correcto.

En este caso, como solo queremos solicitudes de Googlebot, la búsqueda de “Googlebot” coincidirá con todos los agentes de usuario relevantes.

Podemos usar la función abierta de Python para leer y escribir nuestro archivo y el módulo de expresiones regulares de Python, RE, para realizar la búsqueda.

import re

pattern = 'Googlebot'
new_file = open('./googlebot.txt', 'w', encoding='utf8')

for txt_files in uncompressed:
    with open(txt_files, 'r', encoding='utf8') as text_file:
        for line in text_file:
            if re.search(pattern, line):
                new_file.write(line)

Regex hace que esto sea fácilmente escalable mediante un operador OR.

pattern = 'Googlebot|bingbot'

3. Solicitudes de análisis

En una publicación anterior, Hamlet Batista brindó orientación sobre cómo usar expresiones regulares para analizar solicitudes.

Como enfoque alternativo, utilizaremos el potente analizador CSV incorporado de Pandas y algunas funciones básicas de procesamiento de datos para:

  1. Elimina las columnas innecesarias.
  2. Formatee la marca de tiempo.
  3. Cree una columna con URL completas.
  4. Cambie el nombre y reordene las columnas restantes.

En lugar de codificar un nombre de dominio, la función de entrada se puede utilizar para solicitar al usuario y guardarlo como una variable.

Anuncio publicitario

Continuar leyendo a continuación

whole_url = input('Please enter full domain with protocol: ') # get domain from user input

df = pd.read_csv('./googlebot.txt', sep='s+', error_bad_lines=False, header=None, low_memory=False) # import logs

df.drop([1, 2, 4], axis=1, inplace=True) # drop unwanted columns/characters

df[3] = df[3].str.replace('[', '') # split time stamp into two
df[['Date', 'Time']] = df[3].str.split(':', 1, expand=True)

df[['Request Type', 'URI', 'Protocol']] = df[5].str.split(' ', 2, expand=True) # split uri request into columns
df.drop([3, 5], axis=1, inplace=True)

df.rename(columns = {0:'IP', 6:'Status Code', 7:'Bytes', 8:'Referrer URL', 9:'User Agent'}, inplace=True) #rename columns

df['Full URL'] = whole_url + df['URI'] # concatenate domain name

df['Date'] = pd.to_datetime(df['Date']) # declare data types
df[['Status Code', 'Bytes']] = df[['Status Code', 'Bytes']].apply(pd.to_numeric)

df = df[['Date', 'Time', 'Request Type', 'Full URL', 'URI', 'Status Code', 'Protocol', 'Referrer URL', 'Bytes', 'User Agent', 'IP']] # reorder columns

Marco de datos analizado.

4. Validar solicitudes

Es increíblemente fácil suplantar a los agentes de usuario de los motores de búsqueda, lo que hace que la validación de solicitudes sea una parte vital del proceso, para que no terminemos sacando conclusiones falsas al analizar nuestros propios rastreos de terceros.

Para hacer esto, instalaremos una biblioteca llamada dnspython y realizaremos un DNS inverso.

Pandas se puede utilizar para eliminar IP duplicadas y ejecutar las búsquedas en este DataFrame más pequeño, antes de volver a aplicar los resultados y filtrar las solicitudes no válidas.

Anuncio publicitario

Continuar leyendo a continuación

from dns import resolver, reversename
def reverseDns(ip):
    try:
        return str(resolver.query(reversename.from_address(ip), 'PTR')[0])
    except:
        return 'N/A'

logs_filtered = df.drop_duplicates(['IP']).copy() # create DF with dupliate ips filtered for check

logs_filtered['DNS'] = logs_filtered['IP'].apply(reverseDns)  # create DNS column with the reverse IP DNS result

logs_filtered = df.merge(logs_filtered[['IP', 'DNS']], how='left', on=['IP'])  # merge DNS column to full logs matching IP

logs_filtered = logs_filtered[logs_filtered['DNS'].str.contains('googlebot.com')]  # filter to verified Googlebot

logs_filtered.drop(['IP', 'DNS'], axis=1, inplace=True)  # drop dns/ip columns

Adoptar este enfoque acelerará drásticamente las búsquedas, validando millones de solicitudes en minutos.

En el siguiente ejemplo, se procesaron ~ 4 millones de filas de solicitudes en 26 segundos.
Línea de comando de validación de registros.

5. Girar los datos

Después de la validación, nos quedamos con un conjunto de datos limpios y bien formateados y podemos comenzar a pivotar estos datos para analizar más fácilmente los puntos de interés de datos.

En primer lugar, comencemos con una agregación simple utilizando las funciones groupby y agg de Pandas para realizar un recuento del número de solicitudes para diferentes códigos de estado.

Anuncio publicitario

Continuar leyendo a continuación

status_code = logs_filtered.groupby('Status Code').agg('size')

Para replicar el tipo de recuento al que está acostumbrado en Excel, vale la pena señalar que necesitamos especificar una función agregada de ‘tamaño’, no de ‘recuento’.

El uso de recuento invocará la función en no incluido y un corchete cuando está incluido. Entonces, en el ejemplo anterior, el depósito contiene puntos de datos con un valor entre 50.001 y 100.000.

6. Exportar

El paso final de nuestro proceso es exportar nuestros datos de registro y pivotes.

Para facilitar el análisis, tiene sentido exportar esto a un archivo de Excel (XLSX) en lugar de un CSV. Los archivos XLSX admiten varias hojas, lo que significa que todos los DataFrames se pueden combinar en el mismo archivo.

Esto se puede lograr usando para sobresalir. En este caso, también se debe especificar un objeto ExcelWriter porque se agrega más de una hoja al mismo libro.

Anuncio publicitario

Continuar leyendo a continuación

writer = pd.ExcelWriter('logs_export.xlsx', engine="xlsxwriter", datetime_format="dd/mm/yyyy", options={'strings_to_urls': False})

logs_filtered.to_excel(writer, sheet_name="Master", index=False)
pivot1.to_excel(writer, sheet_name="My pivot")

writer.save()

Al exportar una gran cantidad de pivotes, ayuda a simplificar las cosas almacenando DataFrames y nombres de hojas en un diccionario y usando un bucle for.

sheet_names = {
    'Request Status Codes Per Day': status_code_date,
    'URL Status Codes': status_code_url,
    'User Agent Requests Per Day': user_agent_date,
    'User Agent Requests Unique URLs': user_agent_url,
    }

for sheet, name in sheet_names.items():
    name.to_excel(writer, sheet_name=sheet)

Una última complicación es que Excel tiene un límite de filas de 1.048.576. Estamos exportando todas las solicitudes, por lo que esto podría causar problemas al tratar con muestras grandes.

Debido a que los archivos CSV no tienen límite, se puede emplear una instrucción if para agregar una exportación CSV como respaldo.

Si la longitud del DataFrame del archivo de registro es mayor que 1.048.576, se exportará en su lugar como un CSV, evitando que el script falle mientras se combinan los pivotes en una exportación singular.

if len(logs_filtered) <= 1048576:
    logs_filtered.to_excel(writer, sheet_name="Master", index=False)
else:
    logs_filtered.to_csv('./logs_export.csv', index=False)

Pensamientos finales

Vale la pena invertir algo de tiempo en los conocimientos adicionales que se pueden obtener de los datos del archivo de registro.

Si ha estado evitando aprovechar estos datos debido a las complejidades involucradas, espero que esta publicación lo convenza de que estos se pueden superar.

Para aquellos que ya tienen acceso a herramientas que estén interesados ​​en la codificación, espero que el desglose de este proceso de un extremo a otro les haya brindado una mayor comprensión de las consideraciones involucradas al crear un script más grande para automatizar tareas repetitivas que requieren mucho tiempo.

Anuncio publicitario

Continuar leyendo a continuación

El script completo que creé se puede encontrar aquí en Github.

Esto incluye extras adicionales como la integración de la API de GSC, más pivotes y compatibilidad con dos formatos de registro más: Amazon ELB y W3C (utilizado por IIS).
animación del analizador de registros
Para agregar en otro formato, incluya el nombre dentro de la lista log_fomats en la línea 17 y agregue una declaración elif adicional en la línea 193 (o edite una de las existentes).

Por supuesto, existe un margen enorme para ampliar esto aún más. Esté atento a la publicación de la segunda parte que cubrirá la incorporación de datos de rastreadores de terceros, pivotes más avanzados y visualización de datos.

Anuncio publicitario

Continuar leyendo a continuación

Todas las capturas de pantalla fueron tomadas por el autor, julio de 2021