收集用戶頁面滾動停留數據

功能描述:將用戶在頁面上瀏覽時的每次滾動停留記錄下來,哪一個位置停留多久等信息不間斷的提交到服務器端保存起來,做爲除了訪問日誌外另外一種用戶行爲數據。該數據對於長頁面,缺乏a連接的頁面有具分析和參考意義。jquery

困難:沒法完美記錄每一個停留!由於考慮到效率問題,不可能每一個滾動條位置都實時提交給服務端。並且也不是每一個位置都具備意義(好比快速下拉時的數據,咱們不須要它下拉過程當中快慢如何,只關心停留在哪而已,由於停留表明用戶對該位置內容感興趣)。ajax

主要辦法:sql

    1.設置停留時間閥值(如只有停留超過1秒以上的數據纔算停留);數據庫

    2.客戶端臨時保存數據至必定數量才統一提交服務端(好比滿3個停留才提交);json

    3.停留等待時間達到10秒,強行結束等待狀態並提交(正常的停留須要下一個滾動事件觸發才能結束該停留並計算出該次停留多久,但可能存在最後一次停留由於用戶長時間離開而沒法正常結束);
數組

    4.停留等待中,出現點擊事件強行結束等待狀態並提交(中途出現新窗口打開的a點擊,也可能致使該停留也沒法結束,固然當頁跳轉是沒辦法了);
瀏覽器

雖然作了這些考慮,但仍是存在丟失數據的問題。好比臨時保存數據達到2個或某次停留等待還未達到10s,用戶忽然關閉瀏覽器或點擊當頁跳轉連接,都會丟失該頁還未說起的本次停留和臨時保存的數據。緩存

數據價值:服務器

一.統計每次pv請求中最長瀏覽並彙總,可獲知如位置0px用戶80%(打開頁面而沒有下拉頁面的0位置不會被提交),位置1000px還剩30%,位置2000px(頁尾)只剩1%。有了這數據能夠配合頁面點擊統計計算出點擊率:每100個查看該位置的人有多少人點擊(點/看),即若是某次pv的最長位置爲1000px,那麼他看過0~1000px的內容。這個「看」數據是不少數據分析求之不得的。數據結構

二.統計每次pv中那些停留點。用戶停留多少體現他對該內容感興趣,這對純圖片文字內容(無點擊數據)尤其有用。同時結合停留的時間分佈計算獲得一個關注值。

        停留1秒和停留5秒關注權重差多少,這個很難有量化值。我嘗試經過普通停留時間分佈和點擊事件的時間分佈計算一個時間和用戶關注的關係(如如下這些數據):

    1秒 總人數13087 點擊人數615 點擊率=4.7%

    2秒 總人數11772 點擊人數792 點擊率=6.7%

    3秒 總人數5626 點擊人數497 點擊率=8.8%

    4秒 總人數3374 點擊人數352 點擊率=10.4%

    5秒 總人數2174 點擊人數243 點擊率=11.1%

    6秒 總人數1411 點擊人數175 點擊率=12.4%

    7秒 總人數996 點擊人數124 點擊率=12.45%

    基本符合預想中的曲線,就能夠將這種點擊率做爲時間權重加入計算,如2個1秒停留約等同於1個3秒停留。

三.統計用戶流失點。用戶在哪些點停留後沒有繼續往下拉,是不是由於這些點的內容不適合致使用戶沒有往下瀏覽的意願。停留點做爲當前內容的關注,那麼流失點做爲以後內容的期待。流失點只需計算每一個pv中最長的位置都發生在哪裏便可,若是要橫向比較的話,用流失率(該點流失數/該點瀏覽用戶數:每100「看」的用戶中多少人沒有瀏覽下面內容)便可。

將停留率,流失率放入一張圖對比趨勢:

上面的每一個關注波峯都是一個內容點,固然想要用這些數據得出有參考意義的結論仍是須要結合頁面的具體內容。


下面是客戶端js的詳情:

  1. 頁面加載初始化:

    惟一標示scroll_id(保證多個停留屢次提交到同一次pv訪問上);

    全局時間戳global_time(進行中的停留開始時間);

    全局數據數組data(臨時存放停留數據,提交後清空);

    全局位置值last_value(進行中的停留位置);

    全局延遲執行timeout_submit(停留超過10秒強制提交)

  2. 停留數據結構: value: 位置    ms: 停留時間ms    stime: 開始時間戳    etime: 結束時間戳    url: 連接

        上述的stime,etime都是客戶端時間,可能不許確。能夠在ajax提交時傳客戶端提交時間,服務端接收時使用服務端時間戳進行對比,從而矯正stime,etime這些時間(固然還能夠加上一個100ms做爲假定中間響應時間)。

js代碼以下(依賴jquery):

(function () {

	//統計滾動條停留位置
	var rand = Math.floor(Math.random() * 99999).toString();
	var scroll_id = (new Date()).getTime() + rand;
	var data = [];
	var global_time = 0;
	var last_value = 0;
	var timeout_submit = null;

$(function(){
	//觸發滾動事件
	$(window).on('scroll', function () {
		var current_time = (new Date()).getTime();
		add_scroll(current_time);
		global_time = current_time;
		last_value = document.documentElement.scrollTop || document.body.scrollTop;
		clearTimeout(timeout_submit);
		timeout_submit = setTimeout(function () {
			submitScrollData()
		}, 10000); //停留時間超過10s嘗試提交數據
	});

	//點擊連接事件
	$("body").on('click', "a,area", function(evt){
		var link = $(evt.currentTarget).attr('href') || null;
		if(link == null){
			return;
		}
		if (global_time > 0 ) {
			var current_time = (new Date()).getTime();
			var tmp_ms = current_time - global_time;
			var tmp = {
				value: last_value,
				ms: tmp_ms,
				stime: global_time,
				etime: current_time,
				url: link
			};
			data.push(tmp);
			ajaxData();
		}
		global_time = 0;//保證該次停留只提交一次
	})
})

	

	//判斷滾動條是否停留,並寫入data和提交
	function add_scroll(current_time) {
		if (global_time > 0 && current_time > global_time + 1000) { //超過一秒視爲停留
			var tmp_ms = current_time - global_time;
			var tmp = {
				value: last_value,
				ms: tmp_ms,
				stime: global_time,
				etime: current_time
			};
			data.push(tmp);
			//console.log(tmp);
			if (data.length >= 3) { //數量超過3條就提交
				ajaxData();
			}
		}
	}

	//判斷提交數據:數據足夠多或停留時間夠長
	function submitScrollData() {
		var current_time = (new Date()).getTime();
		add_scroll(current_time);
		global_time = 0;
		//console.log("submitScrollData:"+data.length);
		if (data.length < 1) {
			return;
		}
		ajaxData();
		return;
	}


	//提交數據
	function ajaxData() {
		//console.log(data);
		//console.log("提交數據:"+data.length);

		var scroll_data = JSON.stringify(data);;
		var current_time = (new Date()).getTime();
		$.ajax({
			type: "GET",
			dataType: "jsonp",
			jsonp: "jsoncallback",
			data: {
				scroll_id: scroll_id,
				scroll_data: scroll_data,
				time: current_time,
				referrer: document.referrer
			},
			url: "http://xxxxx/page/scroll"
		}).done(function (result_data) {
			//console.log(result_data);
		})
		data = [];
	}
})();


服務端接受上面ajax提交的數據,考慮到可能提交會很是頻繁,能夠作一層cache緩存,將每一個scroll_id提交的暫時保存起來,作個定時隊列循環判斷這些cache中有某些scroll_id已經5分鐘沒有新數據提交,則批量insert進數據庫(若是scroll_id數據庫已存在更新之)。而考慮到這種滾動數據量是很是大的,能夠把一個scroll_id做爲一條數據,放一個text字段保存滾動停留list數據(如json格式),只是這樣分析時候須要用程序解析這些json,而不是用sql查詢就能夠統計了。服務端的代碼就不放了。


注:上面提到的人,用戶不少少時候指的是該次請求pv,而不是uv的意思。上述說的概念是鄙人本身嚇取的,也不知道行業標準叫法是啥。。。。

相關文章
相關標籤/搜索