爬文书网是件痛苦的事,不光要应对各种反爬技术,更要忍受极慢的响应速度,黎明前的黑暗总是漫长的,在你快要放弃的时候你就成功了。
最近有个项目要爬文书网的部分判决文书,由于这个网站用了一些反爬技术,而且网页打开非常慢,用PhantomJS来爬的话效率太低,只能另辟蹊径了。
分析网页的http数据包,发现ListContent返回的JSON数据里有文书的ID,案件名称,裁判日期等需要爬取的关键信息,也就是说通过Requests的post请求直接访问后台的URL就可以获取想要的数据了。
然而post data里的vl5x是绕不开的槛
找一个浏览器里已经用过的vl5x去post ListContent,返回的是remind key,因此这个vl5x值是实时计算生成的。
到底这个值是怎么计算的呢,既然浏览器能算出vl5x的值,那算法应该隐藏在网页的JS文件中。好吧,继续找,在Lawyee.CPWSW.List.js文件里找到了这段
$.ajax({
url: "/List/ListContent",
type: "POST",
async: true,
data: { "Param": listparam, "Index": index, "Page": page, "Order": order, "Direction": direction, "vl5x": getKey(), "number": yzm1, "guid": guid1 },
success: function (data) {
//datalist = $.parseJSON(data);
vl5x的值是是用getKey()函数算出来的,但getKey不在这个js文件里,继续找,终于,在网页的初次请求响应中找到了这段
var _fxxx = function (p, a, c, k, e, d) { e = function (c) { return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) }; if (!''.replace(/^/, String)) { while (c--) d[e(c)] = k[c] || e(c); k = [function (e) { return d[e] } ]; e = function () { return '\\w+' }; c = 1; }; while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]); return p; }
eval(_fxxx('0 2=1;0 3=\'4\'', 5, 5, 'var|5|_1|_2|ookie'.split('|'), 0, {}));
eval(_fxxx('c f(){' + (_1 + 4) + ' i=e-h-8-2;9 7=g(\'d\');9 6=7.0(i,i*' + _1 + ')+7.0((i+1)*(i+1),3);9 a=6.0(i)+6.0(-4);9 b=' + (_1 + 1) + '.0(4)+a.0(-i-1);7=j(6).0(i-1,l);k 7}', 22, 22, ('substr||||||str|c||var|||function|vjkl' + _1 + '|2' + _1 + '|getKey|getC' + _2 + '|10||hex_md5|return|' + (_1 * _1 - 1)).split('|'), 0, {}))
这是什么鬼,google之,貌似这是一种打包(pack)技术,代码开头都是eval,特征字符串是function(p,a,c,k,e,r)或者function(p,a,c,k,e,d),由dean edwards提出的, 其个人主页 ,找个 unpack工具,把上面代码全复制进去unpack必然是得不到结果的,一段一段来吧,首先把第1个eval中的_fxxx用function的定义替换,然后unpack得到如下代码
var _1 = 5;
var _2 = 'ookie'
这个和getKey又有什么关系,仔细观察,第2个eval中经常会出现 _1,_2,用第一次unpack出来的值替换_1,_2
eval(function (p, a, c, k, e, d) { e = function (c) { return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) }; if (!''.replace(/^/, String)) { while (c--) d[e(c)] = k[c] || e(c); k = [function (e) { return d[e] } ]; e = function () { return '\\w+' }; c = 1; }; while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]); return p; }('c f(){' + (5 + 4) + ' i=e-h-8-2;9 7=g(\'d\');9 6=7.0(i,i*' + 5 + ')+7.0((i+1)*(i+1),3);9 a=6.0(i)+6.0(-4);9 b=' + (5 + 1) + '.0(4)+a.0(-i-1);7=j(6).0(i-1,l);k 7}', 22, 22, ('substr||||||str|c||var|||function|vjkl' + 5 + '|2' + 5 + '|getKey|getC' + 'ookie' + '|10||hex_md5|return|' + (5 * 5 - 1)).split('|'), 0, {}))
再unpack试一下,果然,getKey()露出了正面目
function getKey() {
var i = 25 - 10 - 8 - 2;
var c = getCookie('vjkl5');
var str = c.substr(i, i * 5) + c.substr((i + 1) * (i + 1), 3);
var a = str.substr(i) + str.substr( - 4);
var b = str.substr(4) + a.substr( - i - 1);
c = hex_md5(str).substr(i - 1, 24);
return c
}
这里还有个getCookie(‘vjkl5’)需要再剥一下,在网页的初次请求时,服务器会设置响应Cookie
getCookie(‘vjkl5’)就是要获取这个Cookie值,其他的函数在md5.js这个文件中都能找到。解决了vl5x值的计算,然后就开始使劲爬吧,这个过程将是漫长的,请耐心等待。
友情提醒:同一IP频繁访问文书网很容易跳到验证码验证页,为了高效爬取所需的内容,找个稳定的代理IP POOL再爬哦。