无尽的任务1-仿CNZZ的流量统计,完成80%(更新1次)
终于到周末了 ,任务也快接近尾声了,这次要写的流量统计,其实思路很简单,只是一些细节问题花掉了不少时间,对于采集各个浏览器的得到的关健词的乱码问题花的时间最多,换了几种思路。
现在先来简单介绍一下,任务背景和实现思路;手头有2000+个站点,一部分是使用了CNZZ的流量统计,由于CNZZ的流量统计,针对每一个站点要一个独立的账号来管理,所以所有的都采用CNZZ的流量统计,光注册账号都是个问题 ,所以老大给了个任务,要自己弄一个念CNZZ的流量统计出来, 不需要那么专业,目前现在只做数据采集,数据分析暂时不做,所以现在有的功能就是 从各个站点上,采集到数据然后插入到数据库中就行了。对数据分析这一块,现在用了公司之前写的c/s模式的软件替代先。由于只做采集,也急着用所以就用最简的方式写的。
要采集的信息:siteDomain 站点域名, siteRootDomain 站点根域, referer 来源页面, landingpage 访问页面, visittime 访问时间,keyword 关词词, searchengine 搜索引擎, browser 浏览器, os 操作系统, screenWidth 屏幕宽高比, ip IP地址, location 地区信息,reload 是否是F5刷新采集的。
当拿到任务时,直接在后台用 context.Request.。。。。就直接取到了 ,然后马上就交给老大了,直接被老大打回来了,他说不是要采集本站的,而采集手上2000多个站点上的访问的信息。 对于.NET提供的取来源页面针对本部做流量统计很好用 ,但是要用来采集,其它站的访问信息就做不到了。最后换下的思路是这样的。
写一个JS页面:在这个JS代码页中,使用document.referrer,document.location,screen.width,screen.height 取到访问页的来源页面,本页面,屏幕宽高比(分析来源页面中各个搜索引擎的关键词参数,抓取关键词,最后由于编码问题,关键词的分析统一放到后台去处理了),
再使用var code="<"+"script src='http://localhost:2195/TotalDEMO/StatisticsHandler.ashx?urlreferrer="+urlreferrer+"&locationurl="+locationurl+"&screenwidth="+screenwidth+"&screenheight="+screenheight+"'></"+"script>";
alert(urlreferrer);
document.write(code);
将js代码输出到,引用这个JS代码的页面中。
当访问引用了这段JS代码的页面时就会将这些信息采集放到后台去处理;对于地区信息的分析,是采用了纯真IP数库.dat文件,进行分析出来的。
对于要进行数据采集的页面只要在页面上加入<script type="text/javascript" src="http://localhost:2195/TotalDEMO/Statistics_new.js"></script>
现在先将主要的代码信息分享出来,由于目前还是测试中,还有很多没注意的地方。发现问题的朋友请告诉我一下,万分感谢。
JS页面代码(statistics.js):
1 var urlreferrer = document.referrer;urlreferrer="http://www.baidu.com/s?wd=ihj%B5%C4%B5%C4%CA%C7&rsv_bp=0&rsv_spt=3&inputT=166822"; 2 //urlreferrer="http://www.baidu.com/baidu?tn=monline_5_dg&ie=utf-8&wd=%E5%9B%B4%E6%A3%8B%E7%83%ADss"; 3 if(urlreferrer=="") 4 { 5 urlreferrer="normal"; 6 } 7 8 urlreferrer= escape(urlreferrer); 9 var locationurl = document.location; 10 locationurl= escape(locationurl); 11 var screenwidth = screen.width; 12 var screenheight = screen.height; 13 var code="<"+"script src='http://localhost:2195/TotalDEMO/StatisticsHandler.ashx?urlreferrer="+urlreferrer+"&locationurl="+locationurl+"&screenwidth="+screenwidth+"&screenheight="+screenheight+"&isrelod="+isreload+"'></"+"script>"; 14 15 document.write(code);
后台处理信息C#代码:
1 using System; 2 using System.Web; 3 using System.Data; 4 using System.Data.SqlClient; 5 using System.Collections.Generic; 6 using System.Text.RegularExpressions; 7 using System.Text; 8 9 10 11 public class StatisticsHandler : IHttpHandler { 12 13 public void ProcessRequest (HttpContext context) { 14 context.Response.ContentType = "text/plain"; 15 16 int reload = 0; //是否是刷新过来的,0否,1是 17 int siteID = 0; //站点ID 18 string referer; //来源页面 19 string landingpage; //访问页面 20 string siteDomain; //域名 21 string siteRootDomain; //根域 22 string visittime2; //访问时间 23 int s_year; //年 24 int s_month; //月 25 int s_day; //日 26 int s_hour; //小时 27 string s_week=""; //星期(EN) 28 int week; //星期(DI) 29 string keyword; //关键词 30 string searchengine; //搜索引擎 31 string browser; //浏览器 32 string screen; //屏幕宽高比 33 string ip; //IP地址 34 string os; //操作系统 35 string guid; //GUID 36 string location; //总地区信息 37 string _errmsg; //地区错误信息 38 string formads; //来自页面 保留字段 39 string country="中国"; //国家 40 string province; //省份 41 string city; //城市 42 //string area; //区 保留字段 43 //string isp; //网络接入 保留字段 44 GetFieldValue(context, out referer, out landingpage, out siteDomain, out siteRootDomain, out visittime2, out s_year, out s_month, out s_day, out s_hour, out s_week, out keyword, out searchengine, out browser, out screen, out ip, out os, out guid, out reload); 45 week = SweekToIweek(s_week); 46 ConventIPToArea(ip, out location, out _errmsg); //将IP转换成地区信息的方法 47 if (!string.IsNullOrEmpty(_errmsg)) //当ip转换出现错误时地区信息填写错误信息 48 { 49 location = _errmsg; 50 } 51 52 Location _arelocal = new Location(location); //城市信息类,用来拆分地区中包含的城市信息 53 province = _arelocal.Captical; 54 city = _arelocal.City; 55 if (string.IsNullOrEmpty(province)) 56 { 57 country = string.Empty; //国家默认为中国,当省分为空的时间,国家信息留空 58 } 59 60 61 string sqltext = "insert into sts_track( siteID, siteDomain, siteRootDomain, referer, landingpage, visittime, [year], [month], [day], hour, week, keyword, searchengine, browser, os, screenWidth, ip, location,country, province, city, guid,reload) values(@siteID, @siteDomain, @siteRootDomain, @referer, @landingpage, @visittime, @year, @month, @day, @hour, @week, @keyword, @searchengine, @browser, @os, @screenWidth, @ip, @location,@country,@province,@city,@guid,@reload)"; 62 63 SqlParameter[] sqlpar = { 64 new SqlParameter("@siteID",siteID), 65 new SqlParameter("@siteDomain",siteDomain), 66 new SqlParameter("@siteRootDomain",siteRootDomain), 67 new SqlParameter("@referer",referer), 68 new SqlParameter("@landingpage",landingpage), 69 new SqlParameter("@visittime",visittime2), 70 new SqlParameter("@year",s_year), 71 new SqlParameter("@month",s_month), 72 new SqlParameter("@day",s_day), 73 new SqlParameter("@hour",s_hour), 74 new SqlParameter("@week",week), 75 new SqlParameter("@keyword",keyword), 76 new SqlParameter("@searchengine",searchengine), 77 new SqlParameter("@browser",browser), 78 new SqlParameter("@os",os), 79 new SqlParameter("@screenWidth",screen), 80 new SqlParameter("@ip",ip), 81 new SqlParameter("@location",location), 82 new SqlParameter("@country",country), 83 new SqlParameter("@province",province), 84 new SqlParameter("@city",city), 85 new SqlParameter("@guid",guid), 86 new SqlParameter("@reload",reload) 87 }; 88 int flag2= DB.ExecuteSql(sqltext, sqlpar); 89 WriteCookie(context,"GUID",guid); 90 context.Response.Write(""); 91 92 } 93 94 /// <summary> 95 /// 向浏览器端写入Cookie 96 /// </summary> 97 private void WriteCookie(HttpContext context,string key, string vlaue) 98 { 99 string cookiename = key; 100 HttpCookie cookie = new HttpCookie(cookiename); 101 cookie.Value = vlaue; 102 cookie.Expires = DateTime.Now.AddDays(30); 103 context.Response.Cookies.Set(cookie); 104 105 } 106 /// <summary> 107 /// 读取cookie 108 /// </summary> 109 private string ReadCookie(HttpContext context, string key) 110 { 111 try 112 { 113 HttpCookie cookie = context.Request.Cookies[key]; 114 return cookie.Value; 115 } 116 catch (Exception) 117 { 118 119 return ""; 120 } 121 122 } 123 124 /// <summary> 125 /// 此方法把IP地址转化为地区信息 126 /// </summary> 127 /// <param name="ip">ip地址</param> 128 /// <param name="Location">地区信息</param> 129 /// <param name="Errmsg">错误信息</param> 130 private void ConventIPToArea(string ip, out string Location, out string Errmsg) 131 { 132 IPScaner ipscaner = new IPScaner(); //转化IP的类 133 ipscaner.DataPath = HttpContext.Current.Server.MapPath("~/App_Data/CoralWry.dat"); //连接纯真数据文件的地址属性 134 ipscaner.IP = ip; 135 Location = ipscaner.IPLocation(); //地区总信息 国家+地区 格式 136 Errmsg = ipscaner.ErrMsg; //IP转换出错时信息 137 138 } 139 140 141 142 /// <summary> 143 /// 此方法将星期转换成数字表示 144 /// </summary> 145 /// <param name="weekstr">星期英文表示(自动生成)</param> 146 /// <returns></returns> 147 private int SweekToIweek(string weekstr) 148 { 149 int Iweek = 0; 150 switch (weekstr) 151 { 152 case "Monday": Iweek = 1; break; 153 case "Tuesday": Iweek = 2; break; 154 case "Wednesday": Iweek = 3; break; 155 case "Thursday": Iweek = 4; break; 156 case "Friday": Iweek = 5; break; 157 case "Saturday": Iweek = 6; break; 158 case "Sunday": Iweek = 7; break; 159 default: Iweek = 0; break; 160 } 161 return Iweek; 162 } 163 164 165 /// <summary> 166 /// 此方法用来初始各字段的值,提取出来的方法; 167 /// </summary> 168 private void GetFieldValue(HttpContext context, out string referer, out string landingpage, out string siteDomain, out string siteRootDomain, out string visittime2, out int s_year, out int s_month, out int s_day, out int s_hour, out string s_week, out string keyword, out string searchengine, out string browser, out string screen, out string ip, out string os, out string guid, out int reload) 169 { 170 string isreload = context.Request.QueryString["isrelod"]; 171 reload = Convert.ToInt32(isreload); 172 referer = context.Request.QueryString["urlreferrer"]; //页面来源 173 landingpage = context.Request.QueryString["locationurl"]; //访问页面 174 try 175 { 176 Uri landingpageuri = new Uri(landingpage); //将页面字符串URL转成C#URI类 177 siteDomain = landingpageuri.DnsSafeHost; //站点域名 178 siteRootDomain = landingpageuri.DnsSafeHost; //站点根域 179 int dotindex = siteRootDomain.IndexOf('.'); 180 if (dotindex != -1) 181 { 182 siteRootDomain = siteRootDomain.Substring(dotindex + 1); 183 } 184 185 } 186 catch (Exception) 187 { 188 siteDomain = ""; //站点域名 189 siteRootDomain = ""; //站点根域 190 191 } 192 193 194 195 DateTime visittime = context.Timestamp; //访问时间-用来拆分时间的年月日等等 196 visittime2 = visittime.ToString(); //访问时间用来直接插入 197 s_year = visittime.Year; //年 198 s_month = visittime.Month; //月 199 s_day = visittime.Day; //日 200 s_hour = visittime.Hour; //小时 201 s_week = visittime.DayOfWeek.ToString(); //星期 202 203 204 keyword = GetKeyWord(referer); //关键词 205 206 try 207 { 208 Uri referurl = new Uri(referer); //搜索引擎 即为页面来源的域名 209 searchengine = FromSearchengine(referurl.DnsSafeHost); //搜索引擎 210 } 211 catch (Exception) 212 { 213 searchengine = ""; //搜索引擎 214 215 } 216 217 218 browser = context.Request.Browser.Browser; //浏览器版本 219 string screenwidth = context.Request.QueryString["screenwidth"]; //屏幕宽度 220 string screenheight = context.Request.QueryString["screenheight"]; //屏幕高度 221 screen = screenwidth + "*" + screenheight; //屏幕宽高比 222 ip = context.Request.UserHostAddress; //远程IP 223 os = context.Request.Browser.Platform; //操作系统 224 225 string haveGUID = ReadCookie(context,"GUID"); //从cookie中取GUID如果有则不分配新的GUID 226 if (string.IsNullOrEmpty(haveGUID)) 227 { 228 guid = Guid.NewGuid().ToString("N"); //GUID 标识 229 } 230 else 231 { 232 guid = haveGUID; 233 } 234 } 235 236 237 238 /// <summary> 239 /// 获取搜索关键词 240 /// </summary> 241 /// <param name="url">来源地址</param> 242 /// <returns></returns> 243 private string GetKeyWord(string url) 244 { 245 string keyword = ""; 246 string[] _uOsr = { "google", "yahoo", "baidu", "soso", "bing", "sogou" }; //将几个搜索引擎与对应的搜索关系词写入对应的数组中 247 string[] _uOkw = { "q", "q", "wd|word", "w", "q", "query" }; 248 for (int i = 0; i < _uOsr.Length; i++) 249 { 250 if (url.Contains(_uOsr[i])) //如果URL中包含这几个搜索引擎则进入处理 251 { 252 if (_uOsr[i] == "baidu") 253 { 254 string[] temp = _uOkw[i].Split('|'); //来自百度的关系词 有WD和WORD,分开处理 255 string kwd = GetQuerystring(temp[0], url); //当以WD取不到的时候,则用WORD取词 256 if (string.IsNullOrEmpty(kwd)) //指定对应的编码来消除乱码 257 { 258 keyword = GetQuerystring(temp[1], url); //从URL中取得关键词的方法 259 260 } 261 else 262 { 263 keyword = kwd; 264 } 265 } 266
271 else 272 { 273 keyword = GetQuerystring(_uOkw[i], url); 274 } 275 break; 276 } 277 } 278 279 string ecode = GBorUTF(keyword); //获得文字的编码格式 280 281 keyword = HttpUtility.UrlDecode(keyword, Encoding.GetEncoding(ecode)); 282 return keyword; 283 } 284 285 /// <summary> 286 /// 从URL地址中通过queryname提取关键词 287 /// </summary> 288 /// <param name="queryname">wd,word,q,query,w...</param> 289 /// <param name="url">URL地址</param> 290 /// <returns></returns> 291 private string GetQuerystring(string queryname, string url) 292 { 293 string keyword = string.Empty; 294 Dictionary<string, string> dic = new Dictionary<string, string>(); 295 string re = "[?&]([^=]+)(?:=([^&]*))?"; //通进正则将URL中参数分拆 放入字典中 296 MatchCollection mc = Regex.Matches(url, re); 297 foreach (Match item in mc) 298 { 299 if (item.Success) 300 { 301 dic.Add(item.Groups[1].Value, item.Groups[2].Value); 302 } 303 } 304 if (dic.ContainsKey(queryname)) //如果字典中有传入的匹配关键词的键,则取其值返回 305 { 306 keyword = dic[queryname]; 307 } 308 return keyword; 309 310 } 311 312 /// <summary> 313 /// 分析是哪种搜索引擎 314 /// </summary> 315 /// <param name="input"></param> 316 /// <returns></returns> 317 private string FromSearchengine(string input) 318 { 319 string SEngine = ""; 320 string[] Searchengine = { "google", "yahoo", "baidu", "soso", "bing", "sogou" }; 321 for (int i = 0; i < Searchengine.Length; i++) 322 { 323 if (input.Contains(Searchengine[i])) 324 { 325 SEngine = Searchengine[i]; 326 break; 327 } 328 } 329 330 return SEngine; 331 } 332 333 /// <summary> 334 /// 获取文字的编码 335 /// </summary> 336 /// <param name="input"></param> 337 /// <returns></returns> 338 private string GBorUTF(string input) 339 { 340 string en_code = "UTF-8"; 341 //abc -> UTF-8 -> abc2 342 string R_TO_U = HttpUtility.UrlDecode(input, Encoding.GetEncoding("UTF-8")); 343 string U_TO_R = HttpUtility.UrlEncode(R_TO_U, Encoding.GetEncoding("UTF-8")); 344 if (input.ToLower() != U_TO_R.ToLower()) 345 { 346 en_code = "GB2312"; 347 } 348 349 return en_code; 350 351 } 352 362 }
其实IP地址库是使用了CoralWry.dat;IP转用使用了一个类IPScaner.cs;IPSearchHelp.cs,这三个文件在网上有找到。
这次遇到最大的问题就是关键词乱码问题 ,首先为了避免乱码,就在JS代码编了一次,然后想到后台来解码,但是遇到了一个特别的问题 ,就是 从百度首页上直接搜索时,使用的是gb2312的编码格式,从其它站点嵌入百度搜索代码,搜索过来的时候,是utf-8的编码,soso,google是utf8,sogou是gb2312,还有使用不同的浏览器也会采用不同的编码,所以经常不是解决了这个就是哪个出现了问题,最后在网上搜到了一个比较通用的方法,就是假设一段内容是通过UTF8编码的,然后对它进行UTF8解码,再UTF8编码,如果得到的结果和最开始的结果相同,那么就是UTF8的编码,否则就是其它,由于现在基本上是GB2312和UTF8,所以就简单的处理了,当然这样处理不对,编码还有很多其它格式,我这里就是假定了,不是UTF8,就是GB2312,因为照现在所采集到的信息来看,这些搜索引擎 基本是使用了这两种方式。
目前都是写在这一个处理文件中的,由于只需要插入数据库,就直接在这个页面上进行插入了。
现在里面还有很多保留字段,目前这里有一个待解决的字段要采集,就是当用户打开了含有采集信息的JS代码的网站页面时,就会进行采集,并且F5刷新也会采集 一次,所以数据中还有一个字段就是判断是F5刷新 得到的数据还是正常采集得到的数据。
在网上查了一下,JS判断页面是否是刷新导致页面卸载,还是正常退出导致页面卸载的方法,好像都有一点点问题 。哪个大侠有好的处理方法,请指点一下小弟。
先更新到这。。。
不足之处,多多包涵!