解决爬虫因JavaScript导致的521问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28202661/article/details/84941065

项目需要爬 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 ,好了,该拿到的都拿到了,那么第三次直接带上这两个参数起飞,完美解决!


反扒策略更新!!

扫描二维码关注公众号,回复: 5838341 查看本文章

  正常使用一段时间后,网站反扒机制变了,自然要更新咯(本次采用国家企业信用信息公示系统讲解)。

  变更的地方:第一次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.第二次请求同样。

猜你喜欢

转载自blog.csdn.net/qq_28202661/article/details/84941065