웹마스터나 운영자는 웹사이트 데이터 분석 도구를 자주 사용합니다. Google Analytics, Baidu Statistics, Tencent Analytics 등이 널리 사용됩니다. 통계를 수집하려면 먼저 데이터 수집 원칙을 분석하고 구축해야 합니다. 데이터 수집 시스템.
데이터 수집 원칙 분석
간단히 말하면 웹사이트 통계 분석 도구는 대상 웹사이트를 탐색하는 사용자의 행동(예: 웹페이지 열기, 버튼 클릭, 장바구니에 제품 추가 등)을 수집해야 합니다. .) 및 추가 행동 데이터(예: 주문으로 생성된 주문 금액 등). 초기 웹사이트 통계는 종종 단 하나의 사용자 행동, 즉 페이지 열기만을 수집했습니다. 그러면 페이지에서의 사용자 행동을 수집할 수 없습니다. 이러한 수집 전략은 기본 트래픽 분석, 소스 분석, 콘텐츠 분석, 방문자 속성 등 일반적인 분석 관점을 충족할 수 있지만, Ajax 기술 및 전자상거래 웹사이트의 광범위한 사용으로 인해 전자상거래 대상에 대한 통계 분석에 대한 요구가 높아지고 있습니다. 점점 더 강해지고 있습니다. 이 전통적인 수집 전략은 그 능력을 뛰어 넘었습니다.
이후 Google은 자사 제품인 Google Analytics에 맞춤형 데이터 수집 스크립트를 혁신적으로 도입했습니다. 사용자는 소량의 자바스크립트 추적 및 분석을 작성하여 Google Analytics에서 정의한 확장 가능한 인터페이스를 통해 맞춤형 이벤트 및 맞춤형 지표를 구현할 수 있습니다. 현재 Baidu Statistics 및 Sogou Analytics와 같은 제품은 Google Analytics 모델을 복사했습니다.
사실 두 데이터 수집 모드의 기본 원리와 프로세스는 동일하지만 후자가 자바스크립트를 통해 더 많은 정보를 수집합니다. 오늘은 다양한 웹사이트 통계 도구의 데이터 수집 기본 원칙을 살펴보겠습니다.
프로세스 개요
우선, 사용자의 행동이 브라우저에서 계산 중인 페이지로 http 요청을 트리거한다고 가정해 보겠습니다. 웹페이지가 열리면 페이지에 포함된 자바스크립트 조각이 실행됩니다. 관련 도구를 사용해 본 친구라면 일반 웹사이트 통계 도구를 사용하려면 웹페이지에 작은 자바스크립트 코드 조각을 추가해야 한다는 점을 알아야 합니다. 일반적으로 스크립트 태그를 동적으로 생성하고 src를 별도의 js 파일로 지정합니다. 이때 이 별도의 js 파일(그림 1의 녹색 노드)은 브라우저에 의해 요청되고 실행되는 경우가 많습니다. 스크립트. 데이터 수집이 완료된 후 js는 백엔드 데이터 수집 스크립트(그림 1의 백엔드)를 요청합니다. 이 스크립트는 일반적으로 PHP, Python 또는 기타 서버 측에서 작성할 수 있는 이미지로 위장한 동적 스크립트 프로그램입니다. 수집된 데이터는 http 매개변수를 통해 백엔드 스크립트로 전달됩니다. 백엔드 스크립트는 매개변수를 구문 분석하고 이를 고정된 형식으로 액세스 로그에 기록합니다. 클라이언트에 대한 http 응답에 심어졌습니다.
위는 일반적인 데이터 수집 과정입니다. 다음은 Google Analytics를 예로 들어 각 단계에 대해 비교적 상세한 분석을 수행합니다.
숨겨진 스크립트 실행 단계
Google Analytics(이하 GA)를 사용하려면 Google Analytics에서 제공하는 자바스크립트 조각을 페이지에 삽입해야 합니다. 이 조각을 흔히 숨겨진 코드라고 합니다.
여기서 _gaq는 다양한 구성을 배치하는 데 사용되는 GA의 전역 배열입니다. 각 구성의 형식은 다음과 같습니다:
_gaq.push([‘Action’, ‘param1’, ‘param2’, &hellip ;]);
Action은 구성 작업과 관련 매개변수 목록을 지정합니다. GA에서 제공하는 기본 임베딩 코드는 두 가지 사전 설정을 제공합니다. _setAccount는 GA 등록 시 웹사이트 식별 ID를 설정하는 데 사용됩니다. _trackPageview는 GA에 페이지 방문을 추적하라고 지시합니다. 자세한 구성은 https://developers.google.com/analytics/devguides/collection/gajs/를 참조하세요. 실제로 이 _gaq는 FIFO 큐로 사용되며, 묻힌 코드 앞에 구성 코드가 나타날 필요는 없습니다. 자세한 내용은 위 링크의 지침을 참조하세요.
이 기사에 관한 한 _gaq의 메커니즘은 그 뒤에 있는 익명 함수의 코드에 중점을 둡니다. 이것이 바로 묻힌 코드가 수행해야 하는 작업입니다. 이 코드의 주요 목적은 document.createElement 메소드를 통해 스크립트를 생성하고 프로토콜(http 또는 https)에 따라 src를 해당 ga.js로 지정하여 외부 js 파일(ga.js)을 도입하는 것입니다. 이 요소를 추가하면 페이지의 DOM 트리에 삽입됩니다.
ga.async = true는 외부 js 파일을 비동기적으로 호출한다는 의미입니다. 즉, 브라우저의 구문 분석을 차단하지 않고 외부 js 다운로드가 완료된 후 비동기적으로 실행한다는 의미입니다. 이 속성은 HTML5에서 새로 도입되었습니다.
데이터 수집 스크립트 실행 단계
데이터 수집 스크립트(ga.js)는 요청 후 실행됩니다. 이 스크립트는 일반적으로 다음 작업을 수행합니다.
1. 페이지 제목(document.title을 통해)과 같은 브라우저에 내장된 자바스크립트 개체를 통해 정보를 수집합니다. 리퍼러( 이전 점프 URL, document.referrer를 통해), 사용자 모니터 해상도(windows.screen을 통해), 쿠키 정보(document.cookie를 통해) 및 기타 정보입니다.
2. _gaq를 구문 분석하여 구성 정보를 수집합니다. 여기에는 사용자 정의 이벤트 추적, 비즈니스 데이터(예: 전자상거래 웹사이트의 제품 번호 등)가 포함될 수 있습니다.
3. 위의 두 단계에서 수집된 데이터를 미리 정의된 형식에 따라 구문 분석하고 연결합니다.
4. 백엔드 스크립트를 요청하고 http 요청 매개변수에 정보를 넣고 이를 백엔드 스크립트로 전달합니다.
여기서 유일한 문제는 4단계입니다. javascript가 백엔드 스크립트를 요청하는 일반적인 방법은 ajax이지만 ajax는 도메인 간 요청을 할 수 없습니다. 여기서 ga.js는 계산되는 웹사이트의 도메인에서 실행되고 백엔드 스크립트는 다른 도메인에 있습니다(GA의 백엔드 통계 스크립트는 http://www.google-analytics.com/__utm.gif입니다). 그리고 아약스는 작동하지 않습니다. 일반적인 방법은 js 스크립트에서 Image 객체를 생성하고 Image 객체의 src 속성을 백엔드 스크립트에 지정하고 매개변수를 전달하는 것입니다. 이때 크로스 도메인 요청 백엔드가 구현됩니다. 이것이 백엔드 스크립트가 종종 gif 파일로 위장되는 이유입니다. http 패킷 캡처를 통해 __utm.gif에 대한 ga.js의 요청을 볼 수 있습니다.
__utm.gif를 요청할 때 ga.js가 많은 정보를 가져오는 것을 볼 수 있습니다. 예를 들어 utmsr=1280×1024는 화면 해상도이고 utmac=UA-35712773-1은 _gaq에서 구문 분석된 GA ID입니다.
숨겨진 코드가 실행될 때만 __utm.gif가 요청될 수 있다는 점은 주목할 가치가 있습니다. 이벤트 추적이 _trackEvent로 구성된 경우 이벤트가 발생할 때 이 스크립트도 요청됩니다.
ga.js는 압축되어 난독화되어 있고 가독성이 매우 낮기 때문에 향후 구현 단계에서는 유사한 기능을 가진 스크립트를 구현하지 않겠습니다.
백엔드 스크립트 실행 단계
GA의 __utm.gif는 gif로 위장한 스크립트입니다. 이러한 종류의 백엔드 스크립트는 일반적으로 다음을 완료해야 합니다.
1. http 요청 매개변수의 정보를 구문 분석합니다.
2. 클라이언트가 얻을 수 없는 방문자 IP 등의 일부 정보를 서버(WebServer)에서 얻습니다.
3. 형식에 따라 정보를 로그에 작성합니다.
4. 응답 콘텐츠로 1×1 빈 gif 이미지를 생성하고 응답 헤더의 콘텐츠 유형을 image/gif로 설정합니다.
5. Set-cookie를 통해 응답 헤더에 일부 필수 쿠키 정보를 설정합니다.
쿠키가 설정되는 이유는 고유 방문자를 추적하려는 경우 클라이언트가 요청할 때 지정된 추적 쿠키가 없는 경우 규칙에 따라 전역적으로 고유한 쿠키가 생성되어 클라이언트에 심어지는 일반적인 접근 방식이기 때문입니다. 그렇지 않으면 Set-cookie가 배치됩니다. 동일한 사용자 쿠키를 변경하지 않고 유지하기 위해 추적 쿠키가 획득됩니다(그림 4 참조).
이 접근 방식은 완벽하지는 않지만(예: 쿠키를 삭제하거나 브라우저를 변경하는 사용자는 두 명의 사용자로 간주됨) 현재 널리 사용되는 방법입니다. 사이트 전체에서 동일한 사용자를 추적할 필요가 없는 경우 js를 사용하여 계산되는 웹사이트의 도메인에 쿠키를 심을 수 있습니다(GA에서 수행함). 전체 네트워크를 균일하게 배치하려면 쿠키를 심을 수 있습니다. 백엔드 스크립트를 통해 서버 도메인에서 다음을 수행합니다(이 작업은 나중에 수행합니다).
시스템 설계 및 구현
위의 원칙을 바탕으로 접속 로그 수집 시스템을 직접 구축했습니다.
저는 이 시스템을 MyAnalytics라고 부릅니다.
수집된 정보 결정
간단함을 위해 GA의 완전한 데이터 수집 모델을 구현하지 않고 정보만 수집합니다.
코드를 묻어두세요
GA 모델에서 배우겠지만 현재는 구성 개체가 FIFO 대기열로 사용되지 않습니다.
저는 현재 ma.js라는 통계 스크립트를 사용하고 있으며 보조 도메인 이름인 analyze.codinglabs.org를 활성화했습니다. 물론 여기서는 작은 문제가 있는데, https 서버가 없기 때문에 https 사이트에 코드를 배포하면 문제가 생기겠지만 여기서는 무시하도록 하겠습니다.
프런트 엔드 통계 스크립트
완전하지는 않지만 기본 작업을 완료할 수 있는 통계 스크립트 ma.js를 작성했습니다.
(function () { var params = {}; //Document对象数据 if(document) { params.domain = document.domain || ''; params.url = document.URL || ''; params.title = document.title || ''; params.referrer = document.referrer || ''; } //Window对象数据 if(window && window.screen) { params.sh = window.screen.height || 0; params.sw = window.screen.width || 0; params.cd = window.screen.colorDepth || 0; } //navigator对象数据 if(navigator) { params.lang = navigator.language || ''; } //解析_maq配置 if(_maq) { for(var i in _maq) { switch(_maq[i][0]) { case '_setAccount': params.account = _maq[i][1]; break; default: break; } } } //拼接参数串 var args = ''; for(var i in params) { if(args != '') { args += '&'; } args += i + '=' + encodeURIComponent(params[i]); } //通过Image对象请求后端脚本 var img = new Image(1, 1); img.src = 'http://analytics.codinglabs.org/1.gif?' + args; })();
전체 스크립트는 오염되지 않도록 익명 함수에 배치됩니다. 글로벌 환경. 기능은 원리 부분에서 설명하였으므로 다시 설명하지 않겠습니다. 그 중 1.gif가 백엔드 스크립트입니다.
로그 형식
로그는 보이지 않는 문자 ^A(ascii 코드 0x01, Linux에서는 ctrl + v ctrl + a를 통해 입력할 수 있으며 아래에서는 "^A"가 사용됨)를 사용하여 한 줄에 하나의 레코드를 사용합니다. 보이지 않는 문자 0x01)을 나타내기 위해 구체적인 형식은 다음과 같습니다:
시간^AIP^AD도메인 이름^AURL^AP페이지 제목^ARreferrer^A고해상도^AResolution 와이드^A색상 깊이^ALanguage^A클라이언트 정보^A사용자 ID^ 웹사이트 로고
백엔드 스크립트
为了简单和效率考虑,我打算直接使用nginx的access_log做日志收集,不过有个问题就是nginx配置本身的逻辑表达能力有限,所以我选用了OpenResty做这个事情。OpenResty是一个基于Nginx扩展出的高性能应用开发平台,内部集成了诸多有用的模块,其中的核心是通过ngx_lua模块集成了Lua,从而在nginx配置文件中可以通过Lua来表述业务。关于这个平台我这里不做过多介绍,感兴趣的同学可以参考其官方网站http://openresty.org/,或者这里有其作者章亦春(agentzh)做的一个非常有爱的介绍OpenResty的slide:http://agentzh.org/misc/slides/ngx-openresty-ecosystem/,关于ngx_lua可以参考:https://github.com/chaoslawful/lua-nginx-module。
首先,需要在nginx的配置文件中定义日志格式:
log_format tick “$msec^A$remote_addr^A$u_domain^A$u_url^A$u_title^A$u_referrer^A$u_sh^A$u_sw^A$u_cd^A$u_lang^A$http_user_agent^A$u_utrace^A$u_account”;
注意这里以u_开头的是我们待会会自己定义的变量,其它的是nginx内置变量。
然后是核心的两个location:
location /1.gif { #伪装成gif文件 default_type image/gif; #本身关闭access_log,通过subrequest记录log access_log off; access_by_lua " -- 用户跟踪cookie名为__utrace local uid = ngx.var.cookie___utrace if not uid then -- 如果没有则生成一个跟踪cookie,算法为md5(时间戳+IP+客户端信息) uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent) end ngx.header['Set-Cookie'] = {'__utrace=' .. uid .. '; path=/'} if ngx.var.arg_domain then -- 通过subrequest到/i-log记录日志,将参数和用户跟踪cookie带过去 ngx.location.capture('/i-log?' .. ngx.var.args .. '&utrace=' .. uid) end "; #此请求不缓存 add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT"; add_header Pragma "no-cache"; add_header Cache-Control "no-cache, max-age=0, must-revalidate"; #返回一个1×1的空gif图片 empty_gif; } location /i-log { #内部location,不允许外部直接访问 internal; #设置变量,注意需要unescape set_unescape_uri $u_domain $arg_domain; set_unescape_uri $u_url $arg_url; set_unescape_uri $u_title $arg_title; set_unescape_uri $u_referrer $arg_referrer; set_unescape_uri $u_sh $arg_sh; set_unescape_uri $u_sw $arg_sw; set_unescape_uri $u_cd $arg_cd; set_unescape_uri $u_lang $arg_lang; set_unescape_uri $u_utrace $arg_utrace; set_unescape_uri $u_account $arg_account; #打开日志 log_subrequest on; #记录日志到ma.log,实际应用中最好加buffer,格式为tick access_log /path/to/logs/directory/ma.log tick; #输出空字符串 echo ''; }
要完全解释这段脚本的每一个细节有点超出本文的范围,而且用到了诸多第三方ngxin模块(全都包含在OpenResty中了),重点的地方我都用注释标出来了,可以不用完全理解每一行的意义,只要大约知道这个配置完成了我们在原理一节提到的后端逻辑就可以了。
日志轮转
日志收集系统需要处理大量的访问日志,在时间的累积下文件规模急剧膨胀,放在同一文件中管理不便。所以通常要按时间段将日志切分,例如每天或每小时切分一个日志。我这里为了效果明显,每一小时切分一个日志。通过 crontab 定时调用一个 shell 脚本,以下是该脚本的内容:
_prefix="/path/to/nginx" time=`date +%Y%m%d%H` mv ${_prefix}/logs/ma.log ${_prefix}/logs/ma/ma-${time}.log kill -USR1 `cat ${_prefix}/logs/nginx.pid`
这个脚本将ma.log移动到指定文件夹并重命名为ma-{yyyymmddhh}.log,然后向nginx发送USR1信号令其重新打开日志文件。
然后再/etc/crontab里加入一行:
59 * * * * root /path/to/directory/rotatelog.sh
在每个小时的59分启动这个脚本进行日志轮转操作。
测试
下面可以测试这个系统是否能正常运行了。我昨天就在我的博客中埋了相关的点,通过http抓包可以看到ma.js和1.gif已经被正确请求。
同时可以看一下1.gif的请求参数。
相关信息确实也放在了请求参数中。
然后我tail打开日志文件,然后刷新一下页面,因为没有设access log buffer, 我立即得到了一条新日志:
1351060731.360^A0.0.0.0^Awww.codinglabs.org^Ahttp://www.codinglabs.org/^ACodingLabs^A^A1024^A1280^A24^Azh-CN^AMozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4^A4d612be64366768d32e623d594e82678^AU-1-1
注意实际上原日志中的^A是不可见的,这里我用可见的^A替换为方便阅读,另外IP由于涉及隐私我替换为了0.0.0.0。
关于分析
通过上面的分析和开发可以大致理解一个网站统计的日志收集系统是如何工作的。有了这些日志,就可以进行后续的分析了。本文只注重日志收集,所以不会写太多关于分析的东西。
注意,原始日志最好尽量多的保留信息而不要做过多过滤和处理。例如上面的MyAnalytics保留了毫秒级时间戳而不是格式化后的时间,时间的格式化是后面的系统做的事而不是日志收集系统的责任。后面的系统根据原始日志可以分析出很多东西,例如通过IP库可以定位访问者的地域、user agent中可以得到访问者的操作系统、浏览器等信息,再结合复杂的分析模型,就可以做流量、来源、访客、地域、路径等分析了。当然,一般不会直接对原始日志分析,而是会将其清洗格式化后转存到其它地方,如MySQL或HBase中再做分析。
分析部分的工作有很多开源的基础设施可以使用,例如实时分析可以使用Storm,而离线分析可以使用Hadoop。当然,在日志比较小的情况下,也可以通过shell命令做一些简单的分析,例如,下面三条命令可以分别得出我的博客在今天上午8点到9点的访问量(PV),访客数(UV)和独立IP数(IP):
awk -F^A '{print $1}' ma-2012102409.log | wc -l awk -F^A '{print $12}' ma-2012102409.log | uniq | wc -l awk -F^A '{print $2}' ma-2012102409.log | uniq | wc -l
위 내용은 nginx lua를 사용하여 웹사이트 통계 데이터를 수집하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!