内容简介:我的网站托管在VPS上,使用Nginx提供服务。Nginx每天产生的日志有数千行,但大多数是搜索引擎爬虫和DNS服务器的记录,真实用户(用浏览器访问的)只占一小部分。我想把真实用户的访问记录提取出来,毕竟这份数据对网站来说还是非常重要的。有了这份数据,我能够获得比Google Analytics更详细的统计信息。为了便于管理和检索,不能像以前那样用文本保存了,必须要把它存放在数据库中。于是便有了本文的记录。Nginx的日志默认存放在日志的每一行为一个访问记录,遵从特定的格式,默认的格式配置如下:
我的网站托管在VPS上,使用Nginx提供服务。Nginx每天产生的日志有数千行,但大多数是搜索引擎爬虫和DNS服务器的记录,真实用户(用浏览器访问的)只占一小部分。我想把真实用户的访问记录提取出来,毕竟这份数据对网站来说还是非常重要的。有了这份数据,我能够获得比Google Analytics更详细的统计信息。为了便于管理和检索,不能像以前那样用文本保存了,必须要把它存放在数据库中。于是便有了本文的记录。
日志过滤
日志格式
Nginx的日志默认存放在 /var/log/nginx
目录下, access.log
文件是当天的访问记录。Nginx每天定时将 access.log
压缩,文件名加上日期信息,这便是目录下 .gz
后缀的文件,然后 access.log
文件的内容归零,重新记录。为了防止日志占用太多空间,会定期清理旧的日志文件,在我的服务器上,只保存最近十天的日志。
日志的每一行为一个访问记录,遵从特定的格式,默认的格式配置如下:
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
下面是一个访问记录示例(为保护用户隐私,已修改IP),请把各字段对号入座:
1.1.1.1 - - [18/Apr/2019:01:57:50 +0800] "GET / HTTP/1.1" 200 65567 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36" "-"
下面是我感兴趣的内容:
remote_addr time_local request status http_user_agent
我用 Python 语言进行文本处理,从一行访问记录中提取出上述感兴趣的信息。接下来是重要的信息过滤。
内容过滤
我们要过滤出真实用户的有效访问,有如下几个特征:
200 GET
HTTP状态码和 HTTP方法 这两个过滤条件非常简单,这里就不多说。 User-Agent 主要用来过滤搜索引擎的爬虫和DNS服务器的访问,主要通过关键字识别完成,需用到正则表达式。正规爬虫的Agent一般都包含Bot、Spider、Crawler、Fetcher等关键词。下面是这个部分的实现代码:
def agent_filter(agent): # 排除掉网络蜘蛛 if re.search(r"[Bb]ot", agent): return False if re.search(r"[Ss]pider", agent): return False if re.search(r"[Cc]rawler", agent): return False if re.search(r"[Ff]etcher", agent): return False # 排除掉DNS服务器 if re.search(r"DNS", agent): return False # 排除掉空的UA if re.search(r"^\-", agent): return False return True
上面的方法只能过滤掉知名的、容易辨别的网络爬虫,其它的诸如RSS订阅器的零星爬取,需要仔细甄别,并添加额外的规则。为了减小工作量,我用IP过滤的方法排除它们。一般来讲,爬虫有个特点,只抓取HTML,不抓取JavaScript代码。我的实现方法是,将通过前面过滤条件的记录保存在一个list中,遍历整个表,将抓取JavaScript的IP看作是正常的浏览器用户,保存到一个set中。再将list遍历一次,只保留set中的IP地址的访问记录。部分代码如下:
valid_user = set() for i in range(len(rlist)): url = rlist[i][2] addr = rlist[i][0] runjs = re.search(r"^\/js\/.*\.js", url) or \ re.search(r"^\/lib\/.*\.js", url) if runjs: if addr not in valid_user: valid_user.add(addr)
通过上面UserAgent和IP过滤的方法,几乎能排除全部的爬虫访问,但也有漏网之鱼。我在日志中发现,偶尔有来自同一网段的多个IP的访问,而且都请求了JavaScript文件。在 ipip.net 上查询它们的地理位置,发现一般都是国内的机房。要排除这种类型的用户,要用更高级的方法,考虑到投入产出比不高,我就没管,这点误差可以容忍,毕竟连Google Analytics都经常识别不出爬虫。
一般来讲,还要排除特定IP的访问,比如自己的IP。方法和User-Agent的类似,就不多说了。
我将这部分代码命名为 filter.py
,输入为Nginx的日志,输出打印格式化的访问记录。完整代码在 这里
。
数据库
数据库软件我选择最常用的MySQL。首先用如下的 SQL 语句在数据库中创建如下的数据表(Table):
CREATE DATABASE IF NOT EXISTS website; USE website; CREATE TABLE IF NOT EXISTS record ( count INT(32) NOT NULL PRIMARY KEY AUTO_INCREMENT, addr VARCHAR(20) NOT NULL, time DATETIME(0) NOT NULL, page VARCHAR(128) NOT NULL );
插入记录的SQL语句如下:
INSERT INTO record (addr, time, page) VALUES ('1.2.3.4', NOW(), '/about/'), ('5.6.7.8', NOW(), '/series/');
现在要用Python操作数据库,我用的是MySQLdb模块。由于Nginx的时间格式和 MySQL 的默认格式不一样,要做一个格式转换。这一过程的代码文件命名为 mysql.py
,将 filter.py
的输出作为输入,将数据插入到数据库中。代码如下:
!/usr/bin/python3 import sys import datetime import MySQLdb def time_format(t): dt = datetime.datetime.strptime(t, '%d/%b/%Y:%H:%M:%S') return str(dt) def insert_record(db, cursor, addr, time, page): sql = "INSERT INTO record (addr, time, page) \ VALUES ('%s', '%s', '%s')" % (addr, time, page) try: cursor.execute(sql) db.commit() except: db.rollback() def run(): db = MySQLdb.connect('localhost', 'username', 'password', 'dbname', charset = 'utf8') cursor = db.cursor() if len(sys.argv) != 2: print("Usage: ./mysql.py record.log") sys.exit() fin = open(sys.argv[1], 'r') while True: line = fin.readline() if not line: break splits = line.split() addr = splits[0] time = time_format(splits[1]) page = splits[2] insert_record(db, cursor, addr, time, page) db.close(); if __name__ == "__main__": run()
定时任务
最好要让整个过程自动化,步骤依次为:
-
Nginx每天特定时刻将
access.log
打包为.gz
文件,文件名中包含当天的日期,以今天为例,20190425 -
将压缩日志从Nginx的目录拷贝到当前目录,例如文件名为
access.log-20190425.gz
-
解压文件,得到
access.log-20190425
-
过滤日志,得到格式化输出,运行
./filter.py access.log-20190425 > record.log-20190425
-
将访问记录插入到MySQL中,运行
./mysql.py record.log-20190425
- 删除处理完的文本文件
由于 shell 的语法过于恶心,就不奉陪了。我用Python的os模块运行命令,文件命名为 cmd.py
,所有脚本文件放在 /root/script/traffic/
目录。代码如下:
#!/usr/bin/python3 import os import datetime log_dir = '/var/log/nginx/' current_dir = '/root/script/traffic/' today = datetime.datetime.now().strftime("%Y%m%d") gzfile = log_dir + 'access.log-' + today + '.gz' # cp here cp_cmd = 'cp ' + gzfile + ' ' + current_dir # unzip nowgzfile = current_dir + 'access.log-' + today + '.gz' unzip_cmd = 'gunzip ' + nowgzfile # filter logfile = current_dir + 'access.log-' + today recordfile = 'record.log-' + today filter_cmd = current_dir + 'filter.py ' + logfile + ' > ' + recordfile # save to mysql sql_cmd = current_dir + 'mysql.py ' + recordfile # rm files rm_cmd = 'rm ' + recordfile + ' ' + logfile os.system(cp_cmd) os.system(unzip_cmd) os.system(filter_cmd) os.system(sql_cmd) os.system(rm_cmd)
在我的服务器上,Nginx每天凌晨04:00左右打包 access.log
文件。我需要在每天凌晨04:00自动运行上面的 cmd.py
脚本,运行 crontab -e
命令添加如下的定时任务:
00 04 * * * /root/script/traffic/cmd.py
这样,网站每天的访问记录就会自动添加到MySQL数据库中了。我用下面的SQL语句查询了一下昨天的访问记录:
SELECT addr, time, page FROM record WHERE time BETWEEN STR_TO_DATE('2019-04-24 00:00:00', '%Y-%m-%d %H:%i:%s') AND STR_TO_DATE('2019-04-24 23:59:59', '%Y-%m-%d %H:%i:%s');
页面访问量和Google Analytics的统计相差不大。
总结
其实本文就是用简单的技术实现了自己的需求。数据库是非常重要的技术,而且实践性强,以前在工作中没机会接触,自己又找不到合适的数据库练手。从今天起,我的网站访问记录不仅会自动保存下来,还给我提供了一份非常不错的数据用来练手,这应该是最大的收获吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 日志与日志不一样:五种不能忽略的日志源
- 从简单日志到全链路日志 我们应该怎么打日志
- iOS关于日志模式及日志级别
- Logback 之 MDC 日志跟踪、日志自定义
- Logback 之 MDC 日志跟踪、日志自定义
- 使用 logstash 作为 Docker 日志驱动收集日志
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
并行算法的设计与分析
陈国良 / 2009-8 / 66.00元
第3版在修订版的基础上进行了大幅度的修订,新增加3章、重写3章,改写8章。《普通高等教育十一五国家级规划教材·并行算法的设计与分析(第3版)》系统深入地讨论了计算机领域中诸多计算问题的并行算法的设计和分析方法。在着重介绍各种并行计算模型上的常用和典型的并行算法的同时,也力图反映本学科的最新成就、学科前沿和发展趋势。 全书共分二十章,包括基础篇4章(绪论、设计技术、前缀计算、排序和选择网络),......一起来看看 《并行算法的设计与分析》 这本书的介绍吧!