Бесплатная реализация подсчета пользовательских сессий из журнала веб-сервера?

Анализаторы журнала веб-сервера (например, Urchin) часто отображают несколько "сеансов". Сеанс определяется как серия посещений / кликов страниц, совершенных отдельным лицом в течение ограниченного непрерывного отрезка времени. Предпринята попытка идентифицировать эти сегменты, используя IP-адреса и часто дополнительную информацию, такую ​​как пользовательский агент и ОС, а также порог времени ожидания сеанса, например 15 или 30 минут.

Для определенных веб-сайтов и приложений пользователь может войти в систему и / или отследить его с помощью файла cookie, что означает, что сервер может точно знать, когда начинается сеанс. Я говорю не об этом, а об эвристическом выводе сессий (" реконструкция сессий"), когда веб-сервер их не отслеживает.

Я мог бы написать некоторый код, например, на Python, чтобы попытаться восстановить сеансы на основе критериев, упомянутых выше, но я бы не стал изобретать велосипед. Я просматриваю файлы журнала размером около 400К строк, поэтому мне нужно быть осторожным, чтобы использовать масштабируемый алгоритм.

Моя цель здесь состоит в том, чтобы извлечь список уникальных IP-адресов из файла журнала, и для каждого IP-адреса определить количество сеансов, выведенных из этого журнала. Абсолютная точность и точность не нужны... довольно хорошие оценки в порядке.

На основании этого описания:

новый запрос помещается в существующий сеанс, если выполняются два условия:

  • IP-адрес и пользовательский агент уже совпадают с запросами
    вставлен в сессию,
  • запрос выполняется менее чем через пятнадцать минут после последнего вставленного запроса.

теоретически было бы просто написать программу на Python для создания словаря (с ключом IP) словарей (с ключом user-agent), значением которого является пара: (количество сеансов, последний запрос последнего сеанса).

Но я бы предпочел использовать существующую реализацию, если она доступна, так как в противном случае я бы рискнул потратить много времени на настройку производительности.

К вашему сведению, чтобы кто-то не спросил пример ввода, вот строка нашего файла журнала (очищенная):

#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

1 ответ

Решение

Хорошо, если нет другого ответа, вот моя реализация Python. Я не эксперт по Python. Предложения по улучшению приветствуются.

#!/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)

Обновление: на ввод и обработку 342 тыс. Записей и запись 21 тыс. Записей ушло 39 секунд. Это достаточно хорошая скорость для моих целей. Видимо, 3/4 этого времени было проведено в strptime()!

Другие вопросы по тегам