为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来画一些图表。