手把手教你爬取妹纸图片

序:

之前为了演示定向爬取的demo.写了个简单的爬取妹纸图片的小程序(之前的代码下载不了(从明文的图片地址变成动态加载))。

为了整理下,贴出来跟大家分享下。

****************

我们略去了动态获取数据及验证码的。百度搜出来妹子图煎蛋网靠前,就用它了。

受制于爬虫与反爬虫的策略,请允许我做个悲伤的表情,本来想整个简单的,人家反扒了。

说一下思路:终点是js的破解:

手把手教你爬取妹纸图片_第1张图片

            
  • 草莓人战士
    草莓人战士: '">@24 hours ago
    3734604

    01efWrXKnc59BiRVcs2U49PtFIX1VkNkWMi/4fNbo547DAzz/x0SbLWsRfcUH5qCuSSSrVd2eTM3Yi8NpSNYYJ6SvedHJJGQw6P2cfTRqdlynavSJUmmrSY
    4b1doZOmnAdA2XZIZ0trocIqDEz7bqrN0jel8zSvcNRSh4xcnP0iNsojUZMKd9YuU8daGmbUBm0NtmZcAjcScM4JAMAq0tHGliVrekavVIeB8hIc+KGqdJI
    3c48IBOKg44u0mwdXOFpDFEjxNeWgYk3/o5OzbmgSi5fBz5r060SzTwPVBgPIXg9i3yFBwwztwrZYR2bjYEwesbIbGFZuml7ngJNAuASe2Xjt5AD2DRaWRU


  • 上面贴了段源文件,可以看出来,煎蛋网对于图片反扒采取的是JS动态获取方式。

    核心方法是:jandan_load_img

    这端代码在页面没找到。读了下源码:

    从这里看出,这个源码也是不断的在修改的,道高一尺魔高一丈。说不定哪天又改了。

    这段js是排版也是紧凑的。截取下看看

    function jandan_load_img(b) {
            var d = $(b);
            var f = d.next("span.img-hash");
            var e = f.text();
            f.remove();
            var c = f_hDkFHz230tMFyJJjrQ6QazNuBxMMbxGt(e, "tIvhVmg0AqsZl4dIwsp6EzcQXIpmSvBl");
            var a = $('[a]');
            d.before(a);
            d.before("
    "); d.removeAttr("onload"); d.attr("src", location.protocol + c.replace(/(\/\/\w+\.sinaimg\.cn\/)(\w+)(\/.+\.gif)/, "$1thumb180$3")); if (/\.gif$/.test(c)) { d.attr("org_src", location.protocol + c); b.οnlοad=function(){add_img_loading_mask(this,load_sina_gif)} } }

    我们看到传入的地址是参数b,f是img-hash,第6行c里面是变量,加密了,第7、8行将a标签插入到img之前,查看源码看到a标签就是是查看原图的链接,也就是我们接下来爬取的时候用到的地址了。第6行f_后跟着一长串字母的这个函数(简称f函数)返回的就是图片地址。第7行中replace函数的作用是当图片为gif时替换中间的一个字符串为large。

    我们节下课看看这个函数实现:

    var f_hDkFHz230tMFyJJjrQ6QazNuBxMMbxGt = function(m, r, d) {
        var e = "DECODE";
        var r = r ? r : "";
        var d = d ? d : 0;
        var q = 4;
        r = md5(r);
        var o = md5(r.substr(0, 16));
        var n = md5(r.substr(16, 16));
        if (q) {
            if (e == "DECODE") {
                var l = m.substr(0, q)
            }
        } else {
            var l = ""
        }
        var c = o + md5(o + l);
        var k;
        if (e == "DECODE") {
            m = m.substr(q);
            k = base64_decode(m)
        }
        var h = new Array(256);
        for (var g = 0; g < 256; g++) {
            h[g] = g
        }
        var b = new Array();
        for (var g = 0; g < 256; g++) {
            b[g] = c.charCodeAt(g % c.length)
        }
        for (var f = g = 0; g < 256; g++) {
            f = (f + h[g] + b[g]) % 256;
            tmp = h[g];
            h[g] = h[f];
            h[f] = tmp
        }
        var t = "";
        k = k.split("");
        for (var p = f = g = 0; g < k.length; g++) {
            p = (p + 1) % 256;
            f = (f + h[p]) % 256;
            tmp = h[p];
            h[p] = h[f];
            h[f] = tmp;
            t += chr(ord(k[g]) ^ (h[(h[p] + h[f]) % 256]))
        }
        if (e == "DECODE") {
            if ((t.substr(0, 10) == 0 || t.substr(0, 10) - time() > 0) && t.substr(10, 16) == md5(t.substr(26) + n).substr(0, 16)) {
                t = t.substr(26)
            } else {
                t = ""
            }
        }
        return t
    };

    主要是md5,base64.先

    需要朱行翻译,这里有两种思路,一种是简单的,selenium。这种开发成本低,但是爬取太耗性能,太卡了。

    另一种是我们翻译下js的逻辑,用Java实现。说白了就是把加密地址

    055bM978+WxjCf7jTh52Z6gHVYbgSqWiC9Vbkdv0gns9CxAQAPia2FBIV7QrxXm9EpkjGQN5utbzBnT7hiFg1MWvP6uTOT1oQKzu0lvhZoagOyWiUmRkNA

    翻译成:

    http://ww3.sinaimg.cn/mw600/0073tLPGgy1fp93q7o37ij30ia0mt75s.jpg

    ********************************

    3.15抽空补充测试下,结果解密失败,先补充一个点:

    jandan_load_img里面第六行的关键函数名称及常量是会变化的。

    js头部引用后面的类似时间戳也会变的,需要用正则去匹配。

    我贴一段截取代码(没用正则)

    if (response.getStatusLine().getStatusCode() == 200) {
    						//String detalall = EntityUtils.toString(response.getEntity(), "UTF-8");
    						HttpEntity entity = response.getEntity();
    						if (entity != null) {
    													
    								String jsall = EntityUtils.toString(entity,"utf-8");
    								//截取函数与常数
    								if(jsall.contains("jandan_load_img")){
    									
    									String tmp =jsall.substring(jsall.indexOf("var c=f_"), jsall.indexOf("(e,\"")+37);
    									function = tmp.substring(tmp.indexOf("=")+1, tmp.indexOf("("));
    									call = tmp.substring(tmp.indexOf("\"")+1,tmp.lastIndexOf("\""));
    									
    								}
    
    								System.out.println(url + "download OK,function="+function+"('"+call );
    							
    						}
    					}
    					EntityUtils.consume(response.getEntity());
    					response.close();

    贴一下过程:

    1 尝试了用htmlunit简单的去执行js.

    也就是

    ScriptResult  ckstr =hp.executeJavaScript("javascript:"+function+"('"+pichash+"','"+call+"');");
    		    				
    		    System.out.println(ckstr.getJavaScriptResult().toString());
    		    System.out.println(ckstr.getNewPage().getWebResponse());

    输出的解密结果为空。


    2. 尝试根据页面的js,用Java来实现

    	public static String decode(String imghash,String constant) throws IOException{
    		//1
    		int q= 4;
    		constant = md5Encode(constant);  
    		String tt = constant.substring(0, 16);
    		String o = md5Encode(tt) ;
    		String n = MD5Util.encode(constant.substring(16, 32)) ;		
            String l = imghash.substring(0,q);
    		System.out.println(l);
    		
    		//2
    		 String c = o + MD5Util.encode(o + l);				
    		 imghash = imghash.substring(q);
    		 byte[] k = Base64.decodeBase64(imghash); //不同jar的base64结果一样
    		// byte[] k = Base64.getDecoder().decode(imghash);
    		 System.out.println("K1="+k);
    		//3
    		int[] h = new int[256];
    	    for (int g = 0; g < 256; g++) {
    	        h[g] = g;
    	    }
    	    int[] b = new int[256];
    	    for (int g = 0; g < 256; g++) {
    	    	//js的charCodeAt返回指定位置字符在Unicode字符集中的编码值
    	        // b[g] = c.charCodeAt(g % c.length)
    	    	b[g] =  Character.codePointAt(c,g % c.length());
    	    	//b[g] =  (int)c.charAt(g % c.length());
    	    }
    	    for (int f =0, g = 0; g < 256; g++) {
    	        f = (f + h[g] + b[g]) % 256;
    	        int tmp = h[g];
    	        h[g] = h[f];
    	        h[f] = tmp;
    	    }
    	    
    	    //4 
    	    String t = "";
    	
    	    for (int p =0, f =0, g = 0; g < k.length; g++) {
    	        p = (p + 1) % 256;
    	        f = (f + h[p]) % 256;
    	        int tmp = h[p];
    	        h[p] = h[f];
    	        h[f] = tmp;
    	        t += (char)(k[g]^(h[(h[p]+h[f]) % 256]));
    	    }
    	    t = t.substring(0,26);
    //        if ((t.substring(0, 10).equals("0") || t.substring(0, 10) - time() > 0) && t.substring(10, 16) == MD5Util.encode(t.substring(26) + n).substring(0, 16)) {
    //            t = t.substr(26)
    //        } else {
    //            t = ""
    //        }
    	   
    	    return t;
    		
    	}

    其中,关于base64,Java有可以使用common包或者使用jdk自带包。结果是一样。MD5就是常见的。

    还是失败。我觉得是php的有些函数我不懂理解的不对。有实现的同学可以帮忙看看哪里不对。

    之前预想的思路:

    1 。匹配js,获取js关键函数及常量。

    2.  jsoup解析页面。获取目标图片列表,加入任务队列。

    3. 任务队列线程启动httpclient下载队列,解密并下载。

    *************************************************************

    结果不美好,过程记录下。

    你可能感兴趣的:(J2EE)