国内大多数站长可能都在用百度统计。统计的使用方法也很简单,只要在需要统计的页面底部加上统计代码即可。然后打开百度统计的后台,就能看到访客信息,包括入口页面,停留时间,跳出率等等,很是方便。
使用当然是会用,但是随着不断成长,逐渐开始考虑一些事情,比如百度统计只是一行JS怎么就实现统计了呢?如果不打开网页,是不是能利用脚本模拟这一过程呢?
与是就开始上网查资料,开始了新一轮的折腾。网上的研究百度统计的不多,我找到了李鑫博客的原理分析和模拟访问
原理分析
统计代码
首先,百度统计要求网站必须嵌入一段JS代码,大概是长这个样子的。
var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?65e1c6689693082cffb3b7e1f2d8027f"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })();
去除掉多余的代码,得到最重要的一行
https://hm.baidu.com/hm.js?65e1c6689693082cffb3b7e1f2d8027f
即引入这个JS文件就能实现统计了,问题后边的32位字符串就是每个站点特有的统计ID
hm.js做了些什么
该代码加载时,会往用户浏览器中写入名为“HMACCOUNT”的永久cookie,有效期至2038年,以此来区分用户身份。
同时会获取客户端环境,包括浏览器版本,屏幕分辨率,色深,语言等信息。
总结如下:
cc: 不知道,一般为1
cf:url参数hmsr的值
ci:url参数hmci的值
ck:是否支持cookie 1:0
cl:颜色深度 如 “32-bit”
cm:url参数hmmd的值
cp:url参数hmpl的值
cw:url参数hmkw的值
ds:屏幕尺寸,如 ‘1024×768’
ep:初始值为’0’,时间变量,反映页面停留时间,格式大概是:现在时间-载入时间+“,”+另一个很小的时间值
et:初始值为’0’,如果ep时间变量不是0的话,它会变成其他
fl:flash版本
ja:java支持 1:0
ln:语言 zh-cn
lo: 不知道,一般为0
lt:日期 time.time(),如“1327847756”, 在首次请求没有
nv: 不知道,一般为1或者0
rnd:十位随机数字
sb:如果是360se浏览器该值等于‘17’
se: 和搜索引擎相关
si:统计代码id
st:
su:上一页document.referrer
sw: 不知道,估计和搜索引擎有关,一般为空
sse:不知道,估计和搜索引擎有关,一般为空
v:统计代码的版本 ,目前该值为“1.2.30”
发送请求
当获取到所有信息的时候,就可以向服务器提交请求了,将之前获取到的参数进行整合,并以这些参数为后缀请求hm.gif,大概是这样一个地址
https://hm.baidu.com/hm.gif?cc=0&ck=1&cl=24-bit&ds=1440x900&vl=372&ep=7626%2C3009&et=3&fl=29.0&ja=0&ln=zh-cn&lo=0<=1524041576&rnd=1839110354&si=65e1c6689693082cffb3b7e1f2d8027f&su=https%3A%2F%2Fyantuz.cn%2F275.html&v=1.2.30&lv=3&sn=28783&u=https%3A%2F%2Fyantuz.cn%2F
模拟访问
既然了解了请求原理,那能不能用脚本实现模拟访问呢?答案当然是可以的
百度统计的4个请求
通过控制台我们可以发现,每访问一个页面都会产生4个请求
- 加载hm.js
- 加载完毕时候出发两次请求,并传递参数
- 退出页面时候,发出一次请求,并传递参数
实际模拟时,只需要前三次请求即可在访问记录中看到访客记录。
python模拟访问
import urllib2 import urllib import random import math import urlparse import time import cookielib ######################################################################## class Baidu: """""" Referer='http://www.lixin.me' TargetPage='/www.lixin.me' BaiduID='' Hjs="http://hm.baidu.com/h.js?" Hgif="http://hm.baidu.com/hm.gif?" UserAgent='Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)' #IE9 MyData={'cc':'1','ck':'1','cl':'32-bit','ds':'1024x768','et':'0','ep':'0','fl':'11.0','ja':'1','ln':'zh-cn','lo':'0','nv':'1','st':'3','v':'1.0.17'} #---------------------------------------------------------------------- def __init__(self,baiduID,targetPage=None,refererPage=None): """Constructor""" self.TargetPage=targetPage or self.TargetPage self.Referer=refererPage or self.Referer self.BaiduID=baiduID self.MyData['si']=self.BaiduID self.MyData['su']=urllib.quote(self.Referer) pass def run(self,timeout=5): cj=cookielib.CookieJar() opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) opener.addheaders=[("Referer",self.TargetPage),("User-Agent",self.UserAgent)] try: response=opener.open(self.Hjs+self.BaiduID).info() self.MyData['rnd']=int(random.random()*2147483647 ) self.MyData['lt']=int(time.time()) fullurl=self.Hgif+urllib.urlencode(self.MyData) response2=opener.open(fullurl,timeout=timeout).info() self.MyData['rnd']=int(random.random()*2147483647 ) self.MyData['et']='3' self.MyData['ep']='2000,100' response3=opener.open(self.Hgif+urllib.urlencode(self.MyData),timeout=timeout).info() pass except urllib2.HTTPError ,ex: print ex.code pass except urllib2.URLError,ex: print ex.reason pass pass if __name__ =="__main__": a=Baidu('百度统计id','https://bak.yantuz.cn:8000','yantuz.cn') a.run()
php模拟访问
function baiduVisit($bdId,$targetUrl,$referer = 'yantuz.cn',$visitTimes=1){ $bdjs = 'https://hm.baidu.com/hm.js?'; $bdgif = 'https://hm.baidu.com/hm.gif?'; //$bdId = '65e1c6689693082cffb3b7e1f2d8027f'; $arr=array( 'cc'=> '0', 'ck'=> '1', 'cl'=>'24-bit', 'ds'=> '1440x900', 'vl'=> '747', #'ep'=> '6346,2301',#时间 'et'=> '0', #3 'fl'=> '29.0', 'ja'=> '0', 'ln'=> 'zh-cn', 'lo'=> '0', 'lt'=> time(), 'rnd'=> rand(1000000000,2000000000), #random 'si'=> $bdId, 'v'=> '1.2.30', 'lv'=> '3', 'sn'=> '25573',#25581 'su'=> $referer, #请求来源 #'ct'=> '!!', #'tt'=> '页面标题' ); //echo http_build_query($arr); // Create a stream $opts = array( 'http'=>array( 'method'=>"GET", 'header'=>"Accept-language: cn\r\n"."referer:$targetUrl", 'timeout'=>3, ) ); $context = stream_context_create($opts); $url1 = $bdjs.$bdId; //echo $url1.'<br />'; $arr1 = array_merge($arr,array('ep'=> '2302,153','u'=> $referer)); $arr1['et'] = 3; $url2 = $bdgif.http_build_query($arr1); //echo $url2.'<br />'; $arr2 = array_merge($arr,array('ct'=> '!!','tt'=> 'title')); $url3 = $bdgif.http_build_query($arr2); //echo $url3.'<br />'; for ($x=0; $x<$visitTimes; $x++) { file_get_contents($url1, false, $context); file_get_contents($url2, false, $context); file_get_contents($url3, false, $context); //echo $x; } } baiduVisit('百度ID','https://bak.yantuz.cn:8000/');
博主实测
首先准备好代码,以PHP为例,如果要得到一个来自xxoo.com的访问记录,那么调用方法为
baiduVisit('65e1c6689693082cffb3b7e1f2d8027f','https://bak.yantuz.cn:8000','https://xxoo.com');
此法纯属YY装逼,再次感谢大佬李鑫的博客研究成果。其余用法可自行开发,有好想法的欢迎留言交流。