бесплатная реализация подсчета пользовательских сеансов из журнала веб-сервера?
-
04-10-2019 - |
Вопрос
Анализаторы журналов веб-сервера (например,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
Решение
Хорошо, в отсутствие какого-либо другого ответа, вот моя реализация на 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)
Обновить:Потребовалось 39 секунд, чтобы ввести и обработать 342 тыс. записей и записать 21 тыс. записей.Это достаточно хорошая скорость для моих целей.Очевидно, 3/4 этого времени было потрачено на strptime()
!