¿Implementación gratuita de conteo de sesiones de usuario desde un registro de servidor web?

Los analizadores de registro del servidor web (por ejemplo, Urchin) a menudo muestran una serie de “sesiones”. Una sesión se define como una serie de visitas / clics de página realizados por un individuo dentro de un segmento de tiempo continuo y limitado. Se intenta identificar estos segmentos mediante direcciones IP y, a menudo, información complementaria como el agente de usuario y el sistema operativo, junto con un umbral de tiempo de espera de sesión de 15 o 30 minutos.

Para ciertos sitios web y aplicaciones, un usuario puede iniciar sesión y / o rastrearse con una cookie, lo que significa que el servidor puede saber con precisión cuándo comienza una sesión. No estoy hablando de eso, sino de inferir sesiones heurísticamente (” reconstrucción de sesión “) cuando el servidor web no las rastrea.

Podría escribir algún código, por ejemplo, en Python para intentar reconstruir las sesiones según los criterios mencionados anteriormente, pero preferiría no reinventar la rueda. Estoy viendo archivos de registro de un tamaño de alrededor de 400K líneas, así que tendría que tener cuidado de usar un algoritmo escalable.

Mi objective aquí es extraer una lista de direcciones IP únicas de un archivo de registro, y para cada dirección IP, tener el número de sesiones deducidas de ese registro. La precisión absoluta y la precisión no son necesarias … las estimaciones bastante buenas están bien.

Basado en esta descripción :

una nueva solicitud se coloca en una sesión existente si dos condiciones son válidas:

  • La dirección IP y el agente de usuario son los mismos de las solicitudes ya
    insertado en la sesión,
  • la solicitud se realiza menos de quince minutos después de la última solicitud insertada.

en teoría, sería simple escribir un progtwig Python para construir un diccionario (con clave por IP) de diccionarios (con clave por usuario-agente) cuyo valor sea un par: (número de sesiones, última solicitud de la última sesión).

Pero preferiría usar una implementación existente si está disponible, ya que de lo contrario me arriesgaría a pasar mucho tiempo ajustando el rendimiento.

Para su información, para que alguien solicite una entrada de muestra, aquí hay una línea de nuestro archivo de registro (saneado):

#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status 2010-09-21 23:59:59 215.51.1.119 GET /graphics/foo.gif - 80 - 128.123.114.141 Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+en-US;+rv:1.9.2)+Gecko/20100115+Firefox/3.6+(.NET+CLR+3.5.30729) http://www.mysite.org/blarg.htm 200 0 0 

OK, en ausencia de cualquier otra respuesta, aquí está mi implementación de Python. No soy un experto en Python. Las sugerencias de mejora son bienvenidas.

 #!/usr/bin/env python """Reconstruct sessions: Take a space-delimited web server access log including IP addresses, timestamps, and User Agent, and output a list of the IPs, and the number of inferred sessions for each.""" ## Input looks like: # Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status # 2010-09-21 23:59:59 172.21.1.119 GET /graphics/foo.gif - 80 - 128.123.114.141 Mozilla/5.0+(Windows;+U;+Windows+NT+5.1;+en-US;+rv:1.9.2)+Gecko/20100115+Firefox/3.6+(.NET+CLR+3.5.30729) http://www.site.org//baz.htm 200 0 0 import datetime import operator infileName = "ex100922.log" outfileName = "visitor-ips.csv" ipDict = {} def inputRecords(): infile = open(infileName, "r") recordsRead = 0 progressThreshold = 100 sessionTimeout = datetime.timedelta(minutes=30) for line in infile: if (line[0] == '#'): continue else: recordsRead += 1 fields = line.split() # print "line of %d records: %s\n" % (len(fields), line) if (recordsRead >= progressThreshold): print "Read %d records" % recordsRead progressThreshold *= 2 # http://www.dblab.ntua.gr/persdl2007/papers/72.pdf # "a new request is put in an existing session if two conditions are valid: # * the IP address and the user-agent are the same of the requests already # inserted in the session, # * the request is done less than fifteen minutes after the last request inserted." theDate, theTime = fields[0], fields[1] newRequestTime = datetime.datetime.strptime(theDate + " " + theTime, "%Y-%m-%d %H:%M:%S") ipAddr, userAgent = fields[8], fields[9] if ipAddr not in ipDict: ipDict[ipAddr] = {userAgent: [1, newRequestTime]} else: if userAgent not in ipDict[ipAddr]: ipDict[ipAddr][userAgent] = [1, newRequestTime] else: ipdipaua = ipDict[ipAddr][userAgent] if newRequestTime - ipdipaua[1] >= sessionTimeout: ipdipaua[0] += 1 ipdipaua[1] = newRequestTime infile.close() return recordsRead def outputSessions(): outfile = open(outfileName, "w") outfile.write("#Fields: IPAddr Sessions\n") recordsWritten = len(ipDict) # ipDict[ip] is { userAgent1: [numSessions, lastTimeStamp], ... } for ip, val in ipDict.iteritems(): # TODO: sum over on all keys' values [(v, k) for (k, v) in d.iteritems()]. totalSessions = reduce(operator.add, [v2[0] for v2 in val.itervalues()]) outfile.write("%s\t%d\n" % (ip, totalSessions)) outfile.close() return recordsWritten recordsRead = inputRecords() recordsWritten = outputSessions() print "Finished session reconstruction: read %d records, wrote %d\n" % (recordsRead, recordsWritten) 

Actualización: Esto tomó 39 segundos para ingresar y procesar registros 342K y escribir registros 21K. Esa es la velocidad suficiente para mis propósitos. ¡Aparentemente 3/4 de ese tiempo se gastó en tiempo de strptime() !