项目需要爬 http://www.gc-zb.com/search/index.html,发现每次都是521失败,返回的结果是一段JS代码。
<script>
var x = "@@Dec@catch@@@charCodeAt@@@10@06@challenge@g@@__jsl_clearance@@href@length@0@GMT@e@match@@replace@@new@false@split@@String@@pathname@@@if@422@@@div@DOMContentLoaded@captcha@RegExp@Array@36@5@@attachEvent@Expires@@@firstChild@@@@1544427786@@headless@document@else@@join@eval@@@@@a@@charAt@Mon@@https@location@JgSe0upZ@1500@function@@08@search@window@@reverse@@2@@addEventListener@@Path@toString@0xFF@@@createElement@8@0xEDB88320@chars@setTimeout@fromCharCode@var@@43@f@while@@substr@1@innerHTML@for@@cookie@@@@rOm9XFMtA3QKV7nYsPGT4lifyWwkq5vcjH2IdxUoCbhERLaz81DNB6@parseInt@@return@@@try@@@18@7@onreadystatechange@@d@toLowerCase".replace(/@*$/, "").split("@"),
y = "5e 3f=48(){5c('45.10=45.1f+45.4b.17(/[\\?|&]27-c/,\\'\\')',47);37.68='f=34.22|12|'+(48(){5e 1e=[48(3f){6f 3b('1d.5d('+3f+')')},(48(){5e 3f=37.58('25');3f.65='<3g 10=\\'/\\'>8</3g>';3f=3f.30.10;5e 1e=3f.15(/44?:\\/\\//)[12];3f=3f.63(1e.11).79();6f 48(1e){66(5e 8=12;8<1e.11;8++){1e[8]=3f.41(1e[8])};6f 1e.3a('')}})()],8=[[(-~[]+[(+[])]-(-~[])+[]+[[]][12])+[~~{}],[-~{}]+[~~{}]+(4g+[]),[-~[-~~~!/!/-~~~!/!/+2b]]+[~~{}]],[[(-~-~!!4c.36<<-~!!4c.36)]],[[-~[-~~~!/!/-~~~!/!/+2b]]+[(4g^-~[])],(75+[[]][12])+(4g+[]),(75+[[]][12])+(-~[]+[(+[])]-(-~[])+[]+[[]][12]),(75+[[]][12])+[(4g^-~[])],(75+[[]][12])+(75+[[]][12]),[-~-~!!4c.36+(-~[]|-~~~!/!/-~~~!/!/)]+(4g+[]),(75+[[]][12])+(75+[[]][12])],[(75+[[]][12])],[[-~{}]+[~~{}]+(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[]),(75+[[]][12])+(75+[[]][12]),[-~[-~~~!/!/-~~~!/!/+2b]]+[-~-~!!4c.36+(-~[]|-~~~!/!/-~~~!/!/)],[-~-~!!4c.36+(-~[]|-~~~!/!/-~~~!/!/)]+(4g+[])],[[~~{}]],[(75+[[]][12])+[(4g^-~[])]],[(75+[[]][12])],[(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[])+(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[]),(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[])+(75+[[]][12]),[(4g^-~[])]+(75+[[]][12]),[-~-~!!4c.36+(-~[]|-~~~!/!/-~~~!/!/)]+[~~{}],(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[])+(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[]),[-~[-~~~!/!/-~~~!/!/+2b]]+[~~{}],(75+[[]][12])+[-~[-~~~!/!/-~~~!/!/+2b]],[-~[-~~~!/!/-~~~!/!/+2b]]+[~~{}]],[[-~{}]+[-~{}]],[(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[])+(-~[]+[(+[])]-(-~[])+[]+[[]][12]),[(4g^-~[])]+(75+[[]][12]),[-~-~!!4c.36+(-~[]|-~~~!/!/-~~~!/!/)]+[-~{}],(-~[(-~[]+[~~!/!/])/[(-~{}<<-~{})]]+[])+[-~[-~~~!/!/-~~~!/!/+2b]]]];66(5e 3f=12;3f<8.11;3f++){8[3f]=1e.4e()[[-~{}]](8[3f])};6f 8.3a('')})()+';2e=42, a-3-74 4a:5g:b 13;53=/;'};21((48(){71{6f !!4c.51;}4(14){6f 1a;}})()){37.51('26',3f,1a)}38{37.2d('76',3f)}",
f = function (x, y) {
var a = 0,
b = 0,
c = 0;
x = x.split("");
y = y || 99;
while ((a = x.shift()) && (b = a.charCodeAt(0) - 77.5)) c = (Math.abs(b) < 13 ? (b + 48.5) : parseInt(a, 36)) + y * c;
return c
},
z = f(y.match(/\w/g).sort(function (x, y) {
return f(x) - f(y)
}).pop());
while (z++) try {
eval(y.replace(/\b\w+\b/g, function (y) {
return x[f(y, z) - 1] || ("_" + y)
}));
break
} catch (_) {}
</script>
原来这段JS代码是生成一个参数(__jsl_clearance)并放到cookie中,然后重新发送一次请求,这样第二次请求带上这个参数将返回新的JS代码。
那么首先我们先得到这个参数,如何得到?很简单,执行上面一段JS代码。怎么执行?用J2V8,
String html = "拿到上面那段代码";
// 处理从服务器返回的JS,并执行
String js = html.trim().replace("<script>", "").replace("</script>", "").replace(
"while(z++)try{eval(y.replace(/\\b\\w+\\b/g, function(y){return x[f(y,z)-1]||(\"_\"+y)}));break}catch(_){}",
"while(z++)try{y.replace(/\\b\\w+\\b/g, function(y){return x[f(y,z)-1]||(\"_\"+y)});break}catch(_){}");
V8 runtime = V8.createV8Runtime();
String result = runtime.executeStringScript(js);
System.out.println(result);
执行成功后打印结果如下:
var _73 = function () {
setTimeout('location.href=location.pathname+location.search.replace(/[\?|&]captcha-challenge/,\'\')', 1500);
document.cookie = '__jsl_clearance=1544428642.357|0|' + (function () {
var _72 = [
function (_73) {
return eval('String.fromCharCode(' + _73 + ')')
}, (function () {
var _73 = document.createElement('div');
_73.innerHTML = '<a href=\'/\'>_121</a>';
_73 = _73.firstChild.href;
var _72 = _73.match(/https?:\/\//)[0];
_73 = _73.substr(_72.length).toLowerCase();
return function (_72) {
for (var _121 = 0; _121 < _72.length; _121++) {
_72[_121] = _73.charAt(_72[_121])
};
return _72.join('')
}
})()
],
_121 = [
[
[-~{}] + [-~{}] + [-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)], (7 + [
[]
][0]) + (7 + [
[]
][0])
],
[
[-~{}] + [-~{}],
[-~{}] + [-~{}]
],
[
[-~{}] + [-~{}] + [~~{}],
[-~{}] + [~~{}] + (2 + []), [-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)] + (7 + [
[]
][0]), [-~[-~~~!/!/ - ~~~!/!/ + 5]] + [-~{}],
[-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)] + (2 + []), (7 + [
[]
][0]) + [-~[-~~~!/!/ - ~~~!/!/ + 5]],
[(2 ^ -~[])] + (7 + [
[]
][0]), [-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)] + [~~{}], (-~[(-~[] + [~~!/!/]) / [(-~{} << -~{})]] + []) + (-~[(-~[] + [~~!/!/]) / [(-~{} << -~{})]] + []), (7 + [
[]
][0]) + (-~[] + [(+[])] - (-~[]) + [] + [
[]
][0]), [-~[-~~~!/!/ - ~~~!/!/ + 5]] + [(2 ^ -~[])], (7 + [
[]
][0]) + [(-~-~!!window.headless << -~!!window.headless)],
[-~[-~~~!/!/ - ~~~!/!/ + 5]] + (-~[] + [(+[])] - (-~[]) + [] + [
[]
][0]), [-~{}] + [~~{}] + (2 + []), [-~[-~~~!/!/ - ~~~!/!/ + 5]] + (2 + []), [-~{}] + [~~{}] + [-~{}], (7 + [
[]
][0]) + [(-~-~!!window.headless << -~!!window.headless)],
[-~[-~~~!/!/ - ~~~!/!/ + 5]] + [~~{}], (7 + [
[]
][0]) + [~~{}],
[-~{}] + [~~{}] + (2 + []), [-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)] + (7 + [
[]
][0]), [-~[-~~~!/!/ - ~~~!/!/ + 5]] + [~~{}],
[-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)] + [~~{}],
[-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)] + (-~[(-~[] + [~~!/!/]) / [(-~{} << -~{})]] + []), [-~[-~~~!/!/ - ~~~!/!/ + 5]] + (-~[] + [(+[])] - (-~[]) + [] + [
[]
][0]), [(2 ^ -~[])] + (7 + [
[]
][0]), [-~-~!!window.headless + (-~[] | -~~~!/!/ - ~~~!/!/)] + [-~{}], (-~[(-~[] + [~~!/!/]) / [(-~{} << -~{})]] + []) + [-~[-~~~!/!/ - ~~~!/!/ + 5]]
]
];
for (var _73 = 0; _73 < _121.length; _73++) {
_121[_73] = _72.reverse()[[-~{}]](_121[_73])
};
return _121.join('')
})() + ';Expires=Mon, 10-Dec-18 08:57:22 GMT;Path=/;'
};
if ((function () {
try {
return !!window.addEventListener;
} catch (e) {
return false;
}
})()) {
document.addEventListener('DOMContentLoaded', _73, false)
} else {
document.attachEvent('onreadystatechange', _73)
}
那么怎么提取__jsl_clearance?可见__jsl_clearance由两部分组成,我们暂且叫做 jsl_pre 和 jsl_bac,其中jsl_bac需要手工变形为JS的function()函数再执行一遍得到,分别提取如下:
String jsl_pre = result.substring(result.indexOf("__jsl_clearance=") + 16,
result.indexOf("|'+(function(){var") + 1);
String bac = (result.substring(result.indexOf("|'+(function(){"), result.indexOf("+';Expires=") - 23)
+ result.substring(result.indexOf("+';Expires=") - 16, result.indexOf("+';Expires=") - 4))
.replace("|'+(function(){", "").replaceAll("window", "'Chrome'");
String jsl_bac = runtime.executeStringScript(bac);
String jsl_clearance = jsl_pre + jsl_bac;
由此我们已经得到第一个__jsl_clearance!
下一步,我们带上该参数 __jsl_clearance(注意不要带其他东西,尤其时间,否则失效)再次发出请求重新得到一段新的JS代码:
按第一同样的方式提取出新的 __jsl_clearance , 同时从对应的第二次请求返回的Cookie中取出配套 __jsluid ,好了,该拿到的都拿到了,那么第三次直接带上这两个参数起飞,完美解决!
反扒策略更新!!
正常使用一段时间后,网站反扒机制变了,自然要更新咯(本次采用国家企业信用信息公示系统讲解)。
变更的地方:第一次JS处理后发现了 document 这个参数,导致J2V8无法解析
解决思路:
第一次JS处理后,就是下面酱紫:
var _23 = function () {
setTimeout('location.href=location.pathname+location.search.replace(/[\?|&]captcha-challenge/,\'\')', 1500);
document.cookie = '__jsl_clearance=1546595407.801|0|' + (function () {
var _7c = [
function (_23) {
return _23
},
function (_7c) {
return _7c
}, (function () {
var _23 = document.createElement('div');
_23.innerHTML = '<a href=\'/\'>_d</a>';
_23 = _23.firstChild.href;
var _7c = _23.match(/https?:\/\//)[0];
_23 = _23.substr(_7c.length).toLowerCase();
return function (_7c) {
for (var _d = 0; _d < _7c.length; _d++) {
_7c[_d] = _23.charAt(_7c[_d])
};
return _7c.join('')
}
})(),
function (_23) {
for (var _7c = 0; _7c < _23.length; _7c++) {
_23[_7c] = parseInt(_23[_7c]).toString(36)
};
return _23.join('')
}
],
_23 = ['F', [-~[2] - ~[2]], '6pc', [((-~{} | ((+!-{}) << (+!-{}))) + [] + [])], 'x', [(-~!{} + [] + [
[]
][0]) + ((-~{} | ((+!-{}) << (+!-{}))) + [] + [])], 'ELCKlGBXWCg', (2 + (((+!-{}) << (+!-{})) ^ -~!{}) + [
[]
][0]), 'V', [(~~[] + [
[]
][0])], 'qK', [(2 + []) + (2 + [])], '%', (2 + []), 'BQ%3D'];
for (var _d = 0; _d < _23.length; _d++) {
_23[_d] = _7c[[1, 0, 1, 2, 1, 3, 1, 0, 1, 2, 1, 3, 1, 0, 1][_d]](_23[_d])
};
return _23.join('')
})() + ';Expires=Fri, 04-Jan-19 10:50:07 GMT;Path=/;'
};
if ((function () {
try {
return !!window.addEventListener;
} catch (e) {
return false;
}
})()) {
document.addEventListener('DOMContentLoaded', _23, false)
} else {
document.attachEvent('onreadystatechange', _23)
}
那么,要想办法去除document , 而此时替换的方法是不可能了,只能老老实实解析这个小片段js代码的意思。
大意就是新建一个页面元素然后利用其中子元素的href等等等各种转化操作,。。。。。等等等
不过嘛,好像有更好的方法,就是把这个小代码片段直接放到浏览器目标网站执行js(参看:chrome浏览器控制台创建js脚本并执行),直接看最终效果,其实就是返回了目标网址然后参与下一步运算。
那么我们直接把下面包含document的这部分代码块替换为最终结果(目标网址)即可,对往下的运算无影响(其实不要怕js真的在页面上新建了什么你不知道的东西,你要相信它建这些东西纯粹是为了忽悠你,其实也不过就是拿一下属性值啊什么的,比如这次就是换着法的拿一下目标网址嘛哈)。
var _23 = document.createElement('div');
_23.innerHTML = '<a href=\'/\'>_d</a>';
_23 = _23.firstChild.href;
var _7c = _23.match(/https?:\/\//)[0];
_23 = _23.substr(_7c.length).toLowerCase();
Java代码:
String jsl_pre = result.substring(result.indexOf("__jsl_clearance=") + 16,
result.indexOf("|'+(function(){var") + 1);
String bac = (result.substring(result.indexOf("|'+(function(){"), result.indexOf("+';Expires=") - 23)
+ result.substring(result.indexOf("+';Expires=") - 16, result.indexOf("+';Expires=") - 4))
.replace("|'+(function(){", "").replaceAll("window", "'Chrome'");
// 新增解决document问题
// String varDocument = bac.substring(bac.indexOf("},(function(){var "),
// bac.indexOf("=document.createElement")).replace("},(function(){var ", "");
String varTem = bac
.substring(bac.indexOf(".firstChild.href;var "), bac.indexOf(".match(/https?:\\/\\//)[0]"))
.replace(".firstChild.href;var ", "").split("=")[0];
String varDocument = bac
.substring(bac.indexOf(".firstChild.href;var "), bac.indexOf(".match(/https?:\\/\\//)[0]"))
.replace(".firstChild.href;var ", "").split("=")[1];
String varA = bac.substring(bac.indexOf("<a href=\\'/\\'>"), bac.indexOf("</a>';"))
.replace("<a href=\\'/\\'>", "");
//取任意一次作为模板
String documentModel = "document.createElement('div');_15.innerHTML='<a href=\\'/\\'>_t</a>';_15=_15.firstChild.href;var _1L=_15.match(/https?:\\/\\//)[0];_15=_15.substr(_1L.length).toLowerCase()";
documentModel = documentModel.replace("_15", varDocument).replace("_1L", varTem).replace("_t", varA);
bac = bac.replace(documentModel, "'www.gsxt.gov.cn/'");
String jsl_bac = runtime.executeStringScript(bac);
String jsl_clearance = jsl_pre + jsl_bac;
注意里面的.replace()问题,千万不要用replaceAll(),否则像这种复杂情况替换失败(参看:replace与replaceAll的区别)
ok.第二次请求同样。