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

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

julio 22, 2021 0 Por admin


Los datos de rastreo de motores de búsqueda que se encuentran en 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 rastrean e interpretan su sitio web, claridad que simplemente no puede obtener cuando confía en herramientas de terceros.

Esto le permitirá:

  • Valide sus teorías proporcionando pruebas convincentes del comportamiento de los motores de búsqueda.
  • Priorice sus hallazgos ayudándole a comprender la escala de un problema y el impacto probable de su resolución.
  • Descubra problemas adicionales que no son visibles cuando se utilizan 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:

  • Acceder a los datos generalmente implica pasar por un equipo de desarrollo, que puede tomar hora.
  • Los archivos sin procesar pueden ser enormes y se proporcionan en un formato poco amigable, por lo que el análisis esfuerzo.
  • Es posible que las herramientas diseñadas para facilitar el proceso deban integrarse antes de que los datos puedan fluir a través de ellas, y Fresco puede ser prohibitivo.

Publicidad

Continuar leyendo a continuación

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

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 que lo ayudará a 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 ellos también tiene sus propios formatos.

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

Publicidad

Continuar leyendo a continuación

Si no está seguro de qué tipo de formato está tratando, servicios como Builtwith y Wappalyzer brindan una gran comprensión de la pila tecnológica de un sitio web. Pueden ayudarlo a resolver esto si no tiene acceso directo a un actor técnico.

¿Aún no eres más sabio? Intente abrir uno de los archivos sin formato.

A menudo, los comentarios se proporcionan con información sobre campos específicos, a los que luego se puede hacer referencia.

#Campos: tiempo 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 de mercado dominante del 88% en los Estados Unidos.

Empecemos.

1. Identificar archivos y determinar formatos

Para realizar un análisis SEO significativo, queremos un mínimo de alrededor de 100,000 solicitudes y de 2 a 4 semanas de datos 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.

Dado que no sabemos cuántos archivos procesaremos 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.

importación global

archivos = glob.glob (& # 39; *. txt & # 39;)

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

De hecho, a veces es posible que la extensión del archivo no sea la que reconoce. 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 estén repartidos en varias subcarpetas y no queremos perder el tiempo copiándolos en una única ubicación.

Afortunadamente, glob admite tanto búsquedas recursivas como operadores genéricos. Esto significa que podemos generar una lista de todos los archivos en una subcarpeta o subcarpetas secundarias.

Publicidad

Continuar leyendo a continuación

archivos = glob.glob (& # 39; ** / *. * & # 39;, recursivo = Verdadero)

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

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

pip instalar python-magic
pip instalar libmagic
importar magia

def tipo_archivo (ruta_archivo):
    mime = magic.from_file (file_path, mime = True)
    retorno de mimo

La comprensión de listas se puede usar para iterar a través de nuestros archivos y aplicar la función, creando un diccionario para almacenar tanto nombres como tipos.

file_types = (file_type (archivo) para archivo en archivos)

file_dict = dict (zip (archivos, tipos de archivo))

Finalmente, una función y un ciclo while para extraer una lista de archivos que devuelven un tipo MIME de texto / plano y excluyen todo lo demás.

descomprimido = ()

def file_identifier (archivo):
    para la clave, el valor en file_dict.items ():
        si archivo en valor:
            descomprimido.append (clave)

while file_identifier (& # 39; texto / plano & # 39;):
    file_identifier (& # 39; texto / plano & # 39;) en file_dict

Identificación de archivo.

2. Extraiga consultas de los motores de búsqueda

Después de filtrar los archivos en nuestras carpetas, el siguiente paso es filtrar los archivos en sí mismos extrayendo solo aquellas solicitudes que nos interesan.

Publicidad

Continuar leyendo a continuación

Esto elimina la necesidad de combinar archivos usando utilidades de línea de comandos como GREP o FINDSTR, lo que ahorra la inevitable búsqueda de 5 a 10 minutos en las pestañas de bloque: abra notas y marcadores para encontrar el comando correcto.

En este caso, dado que solo queremos consultas 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 hacer la búsqueda.

importar re

patrón = 'Googlebot'
new_file = open (& # 39; ./ googlebot.txt & # 39;, & # 39; w & # 39;, encoding = & # 39; utf8 & # 39;)

para txt_files sin comprimir:
    con open (txt_files, & # 39; r & # 39;, encoding = & # 39; utf8 & # 39;) como text_file:
        para la línea en text_file:
            if re.search (patrón, línea):
                new_file.write (línea)

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

patrón = 'Googlebot | bingbot'

3. Analizar solicitudes

En un artículo anterior, Hamlet Batista brindó consejos sobre cómo usar expresiones regulares para analizar solicitudes.

Como enfoque alternativo, utilizaremos el potente analizador CSV integrado 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 reorganice 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.

Publicidad

Continuar leyendo a continuación

Whole_url = input (& # 39; Ingrese el dominio completo con el protocolo: & # 39;) # obtenga el dominio de la entrada del usuario

df = pd.read_csv (& # 39; ./ googlebot.txt & # 39;, sep = & # 39;  s + & # 39;, error_bad_lines = False, header = None, low_memory = False) # registros; importar

df.drop ((1, 2, 4), axis = 1, inplace = True) # eliminar columnas / caracteres no deseados

df (3) = df (3) .str.replace (& # 39; (& # 39;, & # 39;) # divide la marca de tiempo por la mitad
df ((& # 39; Fecha & # 39;, & # 39; Hora & # 39;)) = df (3) .str.split (& # 39 ;: & # 39;, 1, expand = True)

df ((& # 39; Tipo de solicitud & # 39;, & # 39; URI & # 39;, & # 39; Protocolo & # 39;)) = df (5) .str.split (& # 39; & # 39;;, 2, expand = True) # divide la solicitud de uri en columnas
df.drop ((3, 5), axis = 1, inplace = True)

df.rename (columnas = {0: 'IP', 6: 'Código de estado', 7: 'Bytes', 8: 'URL de referencia' 39;, 9: 'Agente de usuario'}, inplace = Verdadero) # cambiar el nombre de las columnas

df (& # 39; URL completa & # 39;) = Whole_url + df (& # 39; URI & # 39;) # concatenar nombre de dominio

df (& # 39; Fecha & # 39;) = pd.to_datetime (df (& # 39; Fecha & # 39;)) # declarar tipos de datos
df ((& # 39; Código de estado & # 39;, & # 39; Bytes & # 39;)) = df ((& # 39; Código de estado & # 39;, & # 39; Bytes & # 39;) ). aplicar (pd.to_numeric)

df = df ((& # 39; Fecha & # 39;, & # 39; Hora & # 39;, & # 39; Tipo de solicitud & # 39;, & # 39; URL completa & # 39;, & # 39 ; URI & # 39; 39;, 'Código de estado', 'Protocolo', 'URL de referencia', 'Bytes', 'Agente de usuario', 'IP')) # columnas de reorganización

Marco de datos analizado. "Width =" 1000 "height =" 424 "tamaños =" (ancho máximo: 1000px) 100vw, 1000px "srcset =" https://cdn.seoconsem.com/wp-content/uploads/2021 / 07 /parsed-dataframe-2-2-60ed5bba3718f-sej.png 1000w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/parsed-dataframe-2-2-60ed5bba3718f-sej- 480x204 . png 480w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/parsed-dataframe-2-2-60ed5bba3718f-sej-680x288.png 680w, https://cdn.seoconsem.com/ wp-content / uploads / 2021/07 / parsed-dataframe-2-2-60ed5bba3718f-sej-768x326.png 768w "src =" https://cdn.seoconsem.com/wp-content/uploads/2021/07/ parsed-dataframe-2-2-60ed5bba3718f-sej.png

4. Validar solicitudes

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

Para hacer esto, instalaremos una biblioteca llamada dnspython y revertiremos el DNS.

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

Publicidad

Continuar leyendo a continuación

de resolución de importación de DNS, nombre inverso
def reverseDns (ip):
    probar:
        return str (resolver.query (reversename.from_address (ip), & # 39; PTR & # 39;) (0))
    excepto:
        devolver 'N / A'

logs_filtered = df.drop_duplicates ((& # 39; IP & # 39;)). copy () # crear DF ​​con ips duplicados filtrados para verificación

logs_filtered (& # 39; DNS & # 39;) = logs_filtered (& # 39; IP & # 39;). apply (reverseDns) # crea una columna DNS con el resultado DNS IP reverse

logs_filtered = df.merge (logs_filtered ((& # 39; IP & # 39;, & # 39; DNS & # 39;)), cómo = & # 39; izquierda & # 39;, en = (& # 39; IP & # 39;;)) # fusionar la columna DNS con los registros completos correspondientes a la IP

logs_filtered = logs_filtered (logs_filtered (& # 39; DNS & # 39;). str.contains (& # 39; googlebot.com & # 39;)) # filtro para Googlebot verificado

logs_filtered.drop ((& # 39; IP & # 39;, & # 39; DNS & # 39;), axis = 1, inplace = True) # eliminar columnas DNS / ip

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

En el siguiente ejemplo, se procesaron aproximadamente 4 millones de líneas de solicitud en 26 segundos.
Línea de comando de validación de registro. "Width =" 1210 "height =" 406 "tamaños =" (ancho máximo: 1210px) 100vw, 1210px "srcset =" https://cdn.seoconsem.com/wp-content/ uploads / 2021 /07/log-validation-2-1-60ed580ea284d-sej.png 1210w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/log-validation-2-1-60ed580ea284d -sej- 480x161.png 480w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/log-validation-2-1-60ed580ea284d-sej-680x228.png 680w, https: // cdn .seoconsem. com / wp-content / uploads / 2021/07 / log-validation-2-1-60ed580ea284d-sej-768x258.png 768w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/log -validation-2-1-60ed580ea284d-sej-1024x344.png 1024w "src =" https://cdn.seoconsem.com/wp-content/uploads/2021/07/log-validation-2-1-60ed580ea284d-sej .png

5. Rotar los datos

Después de la validación, nos queda un conjunto de datos limpio y bien formateado y podemos comenzar a rotar esos datos para analizar más fácilmente los puntos de datos de interés.

Primero, comencemos con una agregación simple usando las funciones groupby y agg de Pandas para hacer un recuento del número de solicitudes para diferentes códigos de estado.

Publicidad

Continuar leyendo a continuación

status_code = logs_filtered.groupby (& # 39; Código de estado & # 39;). agg (& # 39; tamaño & # 39;)

Para reproducir el tipo de recuento al que está acostumbrado en Excel, debe tenerse en cuenta que necesitamos especificar una función agregada de "tamaño" y no "número".

El uso de count invocará la función en todo las columnas en el DataFrame y los valores NULL se manejan de manera diferente.

Restablecer el índice restaurará los encabezados de ambas columnas, y esa última columna puede cambiarse de nombre a algo más significativo.

status_code = logs_filtered.groupby (& # 39; Código de estado & # 39;). agg (& # 39; tamaño & # 39;). sort_values ​​(ascendente = Falso) .reset_index ()

status_code.rename (columnas = {0: & # 39; # Solicitudes & # 39;}, inplace = True)

Código de estado de pivote. "Width =" 1020 "height =" 417 "size =" (max-width: 1020px) 100vw, 1020px "srcset =" https://cdn.seoconsem.com/wp-content / uploads / 2021 / 07 / status-code-pivot-final-3-60ed553a5460d-sej.jpg 1020w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/status-code-pivot- final-3-60ed553a5460d -sej-480x196.jpg 480w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/status-code-pivot-final-3-60ed553a5460d-sej-680x278.jpg 680w, https: / /cdn.seoconsem.com/wp-content/uploads/2021/07/status-code-pivot-final-3-60ed553a5460d-sej-768x314.jpg 768w "src =" https: // cdn. seekchenginejournal.com/wp -content / uploads / 2021/07 / status-code-pivot-final-3-60ed553a5460d-sej.jpg
Para una manipulación de datos más avanzada, las tablas dinámicas integradas de Pandas brindan una funcionalidad similar a la de Excel, lo que permite agregaciones complejas con una sola línea de código.

En su nivel más básico, la función requiere un DataFrame e índice especificados, o índices si se requiere un índice múltiple, y devuelve los valores correspondientes.

Publicidad

Continuar leyendo a continuación

pd.pivot_table (logs_filtered, index (& # 39; URL completa & # 39;))

Para mayor especificidad, se pueden declarar los valores requeridos y agregaciones: suma, promedio, etc. – aplicado usando el parámetro aggfunc.

También vale la pena mencionar el parámetro Columns, que nos permite mostrar valores horizontalmente para una salida más limpia.

status_code_url = pd.pivot_table (logs_filtered, index = (& # 39; URL completa & # 39;), columnas = (& # 39; Código de estado & # 39;), aggfunc = & # 39; tamaño & # 39;, fill_value = 0)

Código de estado de la URL dinámica. "Width =" 950 "height =" 500 "tamaños =" (ancho máximo: 950px) 100vw, 950px "srcset =" https://cdn.seoconsem.com/wp-content / uploads / 2021 /07/status-code-url-pivot-60ed569a1d87d-sej.png 950w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/status-code-url-pivot- 60ed569a1d87d-sej- 480x253.png 480w, https://cdn.seoconsem.com/wp-content/uploads/2021/07/status-code-url-pivot-60ed569a1d87d-sej-680x358.png 680w, https: // cdn.seoconsem. com / wp-content / uploads / 2021/07 / status-code-url-pivot-60ed569a1d87d-sej-768x404.png 768w "src =" https://cdn.seoconsem.com/wp-content/uploads/2021/ 07 / código-de-estado-url-pivot-60ed569a1d87d-sej.png
Aquí hay un ejemplo un poco más complejo, que proporciona un recuento de URL únicas rastreadas por agente de usuario por día, en lugar de solo una cantidad de solicitudes.

user_agent_url = pd.pivot_table (logs_filtered, index = (& # 39; User Agent & # 39;), values ​​= (& # 39; URL completa & # 39;), columnas = (& # 39; Fecha & # 39;), aggfunc = pd.Series.nunique, fill_value = 0)

Si aún tiene dificultades con la sintaxis, consulte Mito. Le permite interactuar con una interfaz visual en Jupyter cuando usa JupyterLab, pero aún genera el código correspondiente.

Publicidad

Continuar leyendo a continuación

Incorporar rangos

Para puntos de datos como bytes que pueden tener muchos valores numéricos diferentes, tiene sentido agrupar los datos.

Para hacer esto, podemos definir nuestros rangos en una lista y luego usar la función de corte para ordenar los valores en bins, especificando np.inf para capturar cualquier cosa que exceda el valor máximo declarado.

rango_byte = (0, 50000, 100000, 200000, 500000, 1000000, np.inf)

bytes_grouped_ranges = (logs_filtered.groupby (pd.cut (logs_filtered (& # 39; Bytes & # 39;), bins = byte_range, precisión = 0))
    .agg ('tamaño')
    .reset_index ()
)

bytes_grouped_ranges.rename (columnas = {0: & # 39; # Solicitudes & # 39;}, inplace = True)

La notación de intervalo se utiliza en la salida para definir rangos exactos, por ejemplo.

(50.000 100.000)

Los paréntesis indican cuando un número es no incluido y un gancho cuando se incluye. Entonces, en el ejemplo anterior, el depósito contiene puntos de datos con un valor entre 50,001 y 100,000.

6. Exportar

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

Para facilitar el análisis, tiene sentido exportarlo a un archivo de Excel (XLSX) en lugar de a un archivo 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 agregan varias hojas en el mismo libro.

Publicidad

Continuar leyendo a continuación

escritor = pd.ExcelWriter (& # 39; logs_export.xlsx & # 39;, motor = & # 39; xlsxwriter & # 39;, datetime_format = & # 39; dd / mm / aaaa & # 39;, opciones = {& # 39; strings_to_urls: False})

logs_filtered.to_excel (escritor, sheet_name = & # 39; Master & # 39;, index = False)
pivot1.to_excel (escritor, sheet_name = & # 39; Mi pivote & # 39;)

Writer.save ()

Al exportar una gran cantidad de pivotes, esto ayuda a simplificar las cosas al almacenar DataFrames y nombres de hojas en un diccionario y usar un bucle for.

sheet_names = {
    'Solicitar códigos de estado por día' : status_code_date,
    'Códigos de estado de URL' : status_code_url,
    "Solicitudes de agente de usuario por día": user_agent_date,
    "El agente de usuario solicita URL únicas": user_agent_url,
    }

para la hoja, nombre en sheet_names.items ():
    name.to_excel (escritor, sheet_name = sheet)

Una última complicación es que Excel tiene un límite de filas de 1048576. Exportamos cada solicitud, por lo que esto podría causar problemas al procesar muestras grandes.

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

Si la longitud del archivo de registro de DataFrame es mayor que 1,048,576, se exportará como CSV en su lugar, evitando que el script falle al combinar los pivotes en una exportación singular.

si len (logs_filtered) <= 1048576:
    logs_filtered.to_excel (escritor, sheet_name = & # 39; Master & # 39;, index = False)
otro:
    logs_filtered.to_csv (& # 39; ./ logs_export.csv & # 39;, index = False)

Pensamientos finales

La información adicional que se puede obtener de los datos del archivo de registro vale la pena invertir.

Si ha evitado extraer estos datos debido a las complejidades involucradas, con suerte este artículo lo convencerá de que pueden superarse.

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

Publicidad

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 adicionales: Amazon ELB y W3C (utilizado por IIS).
animación del analizador de registros "width =" 911 "height =" 538 "tamaños =" (ancho máximo: 911px) 100vw, 911px "src =" https://cdn.seoconsem.com/wp-content / uploads / 2021/07 /log_parser_animation-60ed6166f21b7-sej.gif
Para agregar en otro formato, incluya el nombre en 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 declaraciones existentes).

Por supuesto, existe un margen de maniobra considerable para ampliar esto aún más. Esté atento al artículo de la Parte 2 que cubrirá la incorporación de datos de rastreadores de terceros, pivotes más avanzados y visualización de datos.

Publicidad

Continuar leyendo a continuación

Más recursos:


Créditos de imagen

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