这么多年做爬虫,遇到了各类防爬虫的情况!这不,一个天天要抓数据的网站,突然的全部521错误,经分析发现,网站是启用了知道创宇的加速乐超强防护,导致无法通过直接请求抓取内容。浏览器打开,效果如下图所示:
防护机制分析
依靠多年经验,分析代码发现加入了JS校验机制,因为现有的HTTPREQUEST类不执行JS,也不方便执行,导致获取不到合法的COOKIES值!
第一次不带COOKIES提交抓取代码如下(JS代码):
<script>var x="challenge@1500@parseInt@location@5@toString@a@Fu@firstChild@replace@3D@x@href@43@rhh@Path@charAt@2@match@document@Expires@0@return@innerHTML@13@1@Mon@createElement@GMT@3@1465217983@join@16@substr@div@r@Jun@while@var@captcha@h@l@36@2A@dc@__phantomas@https@length@cookie@_phantom@989@function@i@06@cd@6MZF@f@dC6@setTimeout@for@__jsl_clearance@window@59@toLowerCase@if@try@addEventListener@catch@e@false@DOMContentLoaded@else@attachEvent@onreadystatechange".replace(/@*$/,"").split("@"),y="16 19=1j(){15(1t.1h||1t.1d){};16 1m,1c='1s=v.1i|m|';16 1o=[1j(c){n c},1j(c){n c;},(1j(){16 18=k.s('12');18.o='<7 d=\\'/\\'>c</7>';18=18.9.d;16 13=18.j(/1e?:\\/\\//)[m];18=18.11(13.1f).1v();n 1j(c){1r(16 1k=m;1k<c.1f;1k++){c[1k]=18.h(c[1k])};n c.w('')}})(),1j(c){1r(16 1k=m;1k<c.1f;1k++){c[1k]=3(c[1k]).6(1a)};n c.w('')}];1m=['1p',[~~[]],[(-~((-~{}-~{}^(+!+[])))+[[], ~~{}][~~[]]),[~~[]]],'c',[-~{}-~{}+5],'1n',[~~[]],[(-~{}+[[], ~~{}][~~[]])+[-~{}-~{}+5],[(-~![]<<-~![])]+(-~((-~[]<<(-~![]<<-~![])))+[[]+[], []][-~[]])],'f',[[(-~![]<<-~![])]+(-~{}+[[], ~~{}][~~[]])],'1b%',[(-~![]<<-~![])],'8',[[(-~[]+[(-~![]<<-~![])]>>(-~![]<<-~![]))]+[(-~[]+[(-~![]<<-~![])]>>(-~![]<<-~![]))]],[(-~{}+[[], ~~{}][~~[]])+(-~((-~{}-~{}^(+!+[])))+[[], ~~{}][~~[]])],[~~[]]+[-~{}/~~{}+[]+[[]][~~!{}]][m].h(~~[]),'%b'];1r(16 1k=m;1k<1m.1f;1k++){1m[1k]=1o[[q,m,i,q,m,q,m,u,q,u,q,m,q,u,i,m,q][1k]](1m[1k])};1m=1m.w('');1c+=1m;1q('4.d=4.d.a(/[\\?|&]17-1/,\\'\\')',2);k.1g=(1c+';l=r, 1l-14-10 p:1u:e t;g=/;');};1w((1j(){20{n !!1t.21;}22(23){n 24;}})()){k.21('25',19,24);}26{k.27('28',19);}",z=0,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},g=y.match(/\b\w+\b/g).sort(function(x,y){return f(x)-f(y)}).pop();while(f(g,++z)-x.length){};eval(y.replace(/\b\w+\b/g, function(y){return x[f(y,z)-1]}));</script>
HTTP HEAD信息:
HTTP/1.1 521
Date: Mon, 06 Jun 2016 13:22:32 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close
Set-Cookie: __jsluid=700bc1eef2051fae74718e053f836421; path=/
X-Cache: error
HEAD返回状态为521,并只传递了一个 __jsluid的COOKIES值,这个值要和JS中设置的一个校验值对应后才认为是合法的访问身份。
反防护破解
下面我们就来分析这个JS代码,代码看起来是加密过的,首先我们先解密。
懂点JS代码是必须的,这段代码中关键部分如下:
eval(y.replace(/\b\w+\b/g, function(y){return x[f(y,z)-1]}))
eval就是执行解密后的JS代码,首先我们想办法把解密后的代码提取出来,并且赋值给我们自己程序里的变量,C#里我们自己得有个执行JS代码并返回执行结果的函数,代码如下:
///需要添加引用COM:Microsoft Script Control 1.0
/// <summary>
/// 执行JS代码并返回结果
/// </summary>
/// <param name="sScript">JS代码</param>
/// <param name="sName">JS函数名</param>
/// <returns>结果</returns>
private static string ExecJS(string sScript, string sName)
{
//string sInitUrl = "http://ipangu.baidu.com/ipangu-hint/hint/hintCustAdd_init.action";
ScriptControlClass js = new ScriptControlClass();//使用ScriptControlClass
object result = null;
try
{
js.Language = "javascript";
js.Reset();
js.Eval(sScript);//指向js脚本
object[] obj = new object[] { };
result = js.Run(sName, ref obj);//传入参数执行
}
catch (Exception ex)
{
result = ex.Message;
}
return result.ToString();
}
然后我们把抓取到的代码,提交给这段函数执行,直接执行恐怕不行,应为程序不知道返回哪个变量值,这里我们就需要对这段JS做下修改,假设我们先把这段JS代码存在了string sHtmlJs这个字符串变量里,我们需要把eval这里执行的结果提取出来,把eval替换成 return,然后把整个代码放到一个JS函数里,方式如下:
sHtmlJs.Replace("eval", "return ");
sHtmlJs= "function getResult(){" + HtmlJs+ "};";
string sResult= ExecJS(sHtmlJs, "getResult");
执行后,我们得到了解密的代码,格式化后如下:
var l = function() {
while (window._phantom || window.__phantomas) {};
var cd, dc = '__jsl_clearance=1465220574.595|0|';
var f = [function(x) {
return x
},
function(x) {
return x;
},
(function() {
var h = document.createElement('div');
h.innerHTML = '<a href=\'/\'>x</a>';
h = h.firstChild.href;
var r = h.match(/https?:\/\//)[0];
h = h.substr(r.length).toLowerCase();
return function(x) {
for (var i = 0; i < x.length; i++) {
x[i] = h.charAt(x[i])
};
return x.join('')
}
})(),
function(x) {
for (var i = 0; i < x.length; i++) {
x[i] = parseInt(x[i]).toString(36)
};
return x.join('')
}];
cd = ['n', [[ - ~ {}] + (6 + [[], !-[]][~~ ! {}])], 'S', (5 + [] + [[]][~~ {}]), 'WHYQCpV', [[ - ~ ( + !~~ [])] + ( - ~ [ - ~ [] + ( - ~~~ {} << -~~~ {}) + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])]] + [] + [[]][~~ {}])], [(2 << -~ ! [])], [[ - ~ {}] + (5 + [] + [[]][~~ {}]), [ - ~ {}] + [ - ~ {}]], 'j%2BfUU', [[ - ~ ( + !~~ [])] + (5 + [] + [[]][~~ {}])], [ - ~ {} + [( + [])] - ( - ~ {})], 'd', ( - ~ [] / ~~ [] + []).charAt(~~ {}), 'PFA%3D'];
for (var i = 0; i < cd.length; i++) {
cd[i] = f[[1, 2, 1, 0, 1, 3, 0, 2, 1, 3, 0, 1, 0, 1][i]](cd[i])
};
cd = cd.join('');
dc += cd;
setTimeout('location.href=location.href.replace(/[\?|&]captcha-challenge/,\'\')', 1500);
document.cookie = (dc + ';Expires=Mon, 06-Jun-16 14:42:54 GMT;Path=/;');
};
if ((function() {
try {
return !! window.addEventListener;
} catch(e) {
return false;
}
})()) {
document.addEventListener('DOMContentLoaded', l, false);
} else {
document.attachEvent('onreadystatechange', l);
}
哈哈,真牛,依然加密!这里我们稍微分析下代码,我们主要事项获取它设置JS的那段代码,其他的一些跳转代码直接可以不需要执行的。所以什么windows对象,document对象的一些操作就可以直接屏蔽掉了。C#里执行里涉及到这些对象的操作也会直接报错。
注意看代码中的:
document.cookie = (dc + ';Expires=Mon, 06-Jun-16 14:42:54 GMT;Path=/;');
这里可以知道DC就是我们需要的COOKIES值了!和前面一样,执行JS,返回DC就OK了!
去掉不需要的代码,去掉的函数我就不写了,很简单,得到如下关键代码:
var l = function() {
var cd, dc = '__jsl_clearance=1465220574.7|0|';
var f = [function(x) {
return x
},
function(x) {
return x;
},
(function() {
var h = 'x';
return function(x) {
for (var i = 0; i < x.length; i++) {
x[i] = h.charAt(x[i])
};
return x.join('')
}
})(),
function(x) {
for (var i = 0; i < x.length; i++) {
x[i] = parseInt(x[i]).toString(36)
};
return x.join('')
}];
cd = ['5K8', [[ - ~ {}] + (5 + [] + [[]][~~ {}])], [(5 + [] + [[]][~~ {}]), (5 + [] + [[]][~~ {}])], 'G', [[ - ~ {}] + [ - ~ ( + !~~ [])]], 'dA', [[ - ~ ( + !~~ [])] + [ - ~ {}]], [[ - ~ {}] + ( - ~ [ - ~ [] + ( - ~~~ {} << -~~~ {}) + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])]] + [] + [[]][~~ {}])], [(2 << -~ ! [])], 'F', [[ - ~ ( + !~~ [])] + [ - ~ {}]], 'txnK%', [ - ~ ( + !~~ [])], 'B', [[ - ~ {}] + ( - ~ [] + 2 + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])] + [[]][~~ {}])], 'TuL', [( - ~ [] + 2 + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])] + [[]][~~ {}])], 'L', [[ - ~ {}] + [(2 << -~ ! [])]], '%3D'];
for (var i = 0; i < cd.length; i++) {
cd[i] = f[[1, 3, 2, 1, 2, 1, 3, 2, 0, 1, 3, 1, 0, 1, 3, 1, 2, 1, 2, 1][i]](cd[i])
};
cd = cd.join('');
dc += cd;
return dc;
};
然后把这个代码提交给我们的ExecJS函数执行:
sResult= ExecJS(sHtmlJS, "l"); //这个函数名“l”是原有JS自己的,我们可以直接调用
看返回结果:
__jsl_clearance=1465220574.595|0|nS5WHYQCpVs4j%2BfUUp9dIPFA%3D
这就是我们想要的!哈哈哈...
和前面获取到的COOKIES值进行拼接后:
__jsluid=700bc1eef2051fae74718e053f836421; __jsl_clearance=1465220574.595|0|nS5WHYQCpVs4j%2BfUUp9dIPFA%3D
带着这个COOKIES再请求服务器,服务器返回200啦!
至此,完美解决!
