【问题标题】:free implementation of counting user sessions from a web server log?从 Web 服务器日志中计算用户会话的免费实现?
【发布时间】:2010-09-22 21:41:19
【问题描述】:

Web 服务器日志分析器(例如 Urchin)通常会显示许多“会话”。会话定义为个人在有限的连续时间段内进行的一系列页面访问/点击。尝试使用 IP 地址识别这些网段,通常是用户代理和操作系统等补充信息,以及 15 或 30 分钟等会话超时阈值。

对于某些网站和应用程序,可以使用 cookie 登录和/或跟踪用户,这意味着服务器可以准确地知道会话何时开始。我不是在谈论这个,而是在网络服务器不跟踪会话时启发式地推断会话(“session reconstruction”)。

我可以写一些代码,例如在 Python 中尝试根据上述标准重建会话,但我宁愿不重新发明轮子。我正在查看大小约为 400K 行的日志文件,因此我必须小心使用可扩展算法。

我的目标是从日志文件中提取唯一 IP 地址列表,并为每个 IP 地址从该日志中推断出会话数。绝对精度和准确度不是必需的……相当好的估计是可以的。

基于this description

一个新的请求被放入一个现有的 如果两个条件有效,则会话:

  • IP 地址和用户代理已与请求相同
    插入到会话中,
  • 请求在最后一次后不到十五分钟内完成 已插入请求。

从理论上讲,编写一个 Python 程序来构建一个字典(由 IP 键控)的字典(由用户代理键控)是很简单的,其值是一对:(会话数,最新会话的最新请求) .

但如果有可用的实现,我宁愿尝试使用现有实现,否则我可能会冒着花费大量时间调整性能的风险。

仅供参考,以免有人要求输入示例,这是我们的日志文件中的一行(已清理):

#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 session web-analytics web-analytics-tools


    【解决方案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)
    

    更新:这需要 39 秒来输入和处理 342K 记录并写入 21K 记录。这对我的目的来说足够好。显然 3/4 的时间花在strptime()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多