Bob's Blog

Web开发、测试框架、自动化平台、APP开发、机器学习等

返回上页首页

为Django网站增加访客记录功能



搭建好自己的网站后呢,肯定会想要知道谁来看了,看了什么页面,看了多少次,还有限制各种条件查询。于是访客记录的功能对自己来说就显得比较重要了。

也有现成的可以用,比如Google search console或者百度统计。不过百度统计这种并不能完全满足我的需求,少许访问没有记录,而且我想要的信息还差一点。于是打算自己做一个访客记录统计存入数据库。

我打算记录的信息有:ip, 国家,城市,设备agent,访问页面,访问次数,当天第一次访问时间,当天最后一次访问时间,从哪里跳转过来。

首先在models.py中新增visitor的类,如下:

class Visitor(models.Model):
    ip = models.CharField(max_length=30)
    region = models.CharField(max_length=1000, blank=True, null=True)
    agent = models.CharField(max_length=1000)
    page = models.CharField(max_length=100)
    referer = models.CharField(max_length=500, blank=True, null=True)
    views = models.PositiveIntegerField(default=0)
    record_date = models.DateTimeField(default=timezone.now)
    update_date = models.DateTimeField(default=timezone.now)

    def increase_views(self):
        self.update_date = timezone.now()
        self.views += 1
        self.save(update_fields=['views', 'update_date'])

然后需要执行python manage.py migrate来应用数据表的变动。这时查看数据库就能看到新的表包含了visitor类的信息作为列名。

对于刚才上面列出的各种想记录的信息,其实在django传递的request里就有包含了。比如:

 - request.META中的'HTTP_X_FORWARDED_FOR'或'REMOTE_ADDR'就包含了ip信息;

- request.META中的'HTTP_REFERER'就包含了从哪里跳转来的信息,当然直接访问就不存在这个key

- request.get_full_path()就是当前访问的页面。

- request.META中的'HTTP_USER_AGENT'就包含了设备agent的信息,可以看出访问者的设备、系统,以及查看是否爬虫。

- 访问量则是采用计数自增的方式更新值。

- 对于访问者国家和城市的定位,我采用了两种方式。一是自己配置maxmind的地理位置数据库,地址是http://dev.maxmind.com/geoip/geoip2/geolite2,需要下载相关数据文件到网站目录,然后下载一个python库用来查询数据: pip install geoip2, 这样本地处理地理位置会比较快,只是数据可能会过时需要偶尔更新一下数据文件。二是使用公共服务的api,比如我用了ipify(也可获取ip),可以参考https://www.ipify.org/. 注册一个免费用户一个月可以有1000次免费查询。虽然数据不用自己更新,不过难免会慢一点。我是先本地查询,有异常再访问ipify的服务。

 

在了解了需要的信息从哪里获取后,就可以在views.py中添加相关的方法,并在需要记录的页面上调用这个方法即可。以下是我的代码:

def record_visit(request):
    try:
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            current_ip = request.META.get('HTTP_X_FORWARDED_FOR')
        else:
            current_ip = request.META.get('REMOTE_ADDR')
        if "HTTP_USER_AGENT" in request.META:
            current_agent = request.META["HTTP_USER_AGENT"]
        else:
            current_agent = "no agent key in request"
        current_page = request.get_full_path()
        today = timezone.now()

        visitor_exist = Visitor.objects.filter(ip=str(current_ip), page=current_page, record_date__range=(today.date(), today.date() + timezone.timedelta(days=1)))
        if visitor_exist:
            current_visitor = visitor_exist[0]
            current_visitor.increase_views()
        else:
            current_visitor = Visitor()
            current_visitor.ip = current_ip
            ip_exist = Visitor.objects.filter(ip=str(current_ip)).order_by('-id')
            generate_new_location = True
            if ip_exist:
                generate_new_location = False
                temp_visitor = ip_exist[0]
                if (today - temp_visitor.record_date).days >= 7:
                    generate_new_location = True
                if temp_visitor.region:
                    current_visitor.region = temp_visitor.region
            if generate_new_location:
                if current_ip not in ["127.0.0.1", "localhost"]:
                    try:
                        current_visitor.region = GeoIpHelper.get_location(current_ip)
                    except Exception as e:
                        print("error when get location from ipify, message: %s" % str(e))
            current_visitor.agent = current_agent
            current_visitor.page = current_page
            if 'HTTP_REFERER' in request.META.keys():
                temp_referer = request.META["HTTP_REFERER"]
                temp_host = request.get_host()
                if temp_host not in temp_referer.split("/"):
                    current_visitor.referer = temp_referer
            current_visitor.record_date = today
            current_visitor.update_date = today
            current_visitor.views = 1
            current_visitor.save()
    except Exception as e:
        print("get error when record visitor, message: %s" % str(e))

这里调用到了一个我自己定义的GeoIpHelper,代码如下:

class GeoIpHelper:

    @classmethod
    def get_ip(cls):
        try:
            ip = load(urlopen('https://api.ipify.org/?format=json'))["ip"]
        except URLError:
            ip = "network error"
        return ip

    @classmethod
    def get_location(cls, ip=None):
        if not ip:
            ip = cls.get_ip()
        try:
            city_client = Reader("./utils/geoip2/GeoLite2-City.mmdb")
            response = city_client.city(ip)
            if response.city.name:
                location = ",".join([response.country.iso_code, response.city.name])
            else:
                location = ",".join([response.country.iso_code, ""])
        except Exception as e:
            print("fail to get location with geoip2: %s" % str(e))
            try:
                api_key = 'replace your key here'
                api_url = 'https://geo.ipify.org/api/v1?'
                url = api_url + 'apiKey=' + api_key + '&ipAddress=' + ip
                temp_region = loads(urlopen(url).read().decode('utf8'))["location"]
                try:
                    location = ",".join([temp_region["country"], temp_region["city"]])
                except [KeyError, ValueError]:
                    location = temp_region
            except URLError:
                location = "network error"
        return location

此时就已经完成了网站访客的记录啦。重新部署网站后每次打开页面,就能看到访客信息的更新了。

后续可以用echarts或者highchart来画一些图表。

下一篇:  openstf支持ios设备和问题解决
上一篇:  Levenshtein Distance编辑距离算法

共有0条评论

添加评论

暂无评论