手把手教你爬取妹纸图片

序:

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

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

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

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

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

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


            <li id="comment-3734604">
                <div>
                    <div class="row">
                        <div class="author"><strong
                                title="防伪码:2331a2d8338f5de26f6a2bc2c4499e507e3334e1" class="">草莓人战士</strong>                            <br>
                            <small><a href="#footer" title="@回复"
                                      οnclick="document.getElementById('comment').value += '@<a href="//jandan.net/ooxx/page-47#comment-3734604">草莓人战士</a>: '">@24 hours ago</a></span></small>
                        </div>
                        <div class="text"><span class="righttext"><a href="//jandan.net/ooxx/page-47#comment-3734604">3734604</a></span><p><img src="//img.jandan.net/img/blank.gif" οnlοad="jandan_load_img(this)" /><span class="img-hash">01efWrXKnc59BiRVcs2U49PtFIX1VkNkWMi/4fNbo547DAzz/x0SbLWsRfcUH5qCuSSSrVd2eTM3Yi8NpSNYYJ6SvedHJJGQw6P2cfTRqdlynavSJUmmrSY</span><br />
<img src="//img.jandan.net/img/blank.gif" οnlοad="jandan_load_img(this)" /><span class="img-hash">4b1doZOmnAdA2XZIZ0trocIqDEz7bqrN0jel8zSvcNRSh4xcnP0iNsojUZMKd9YuU8daGmbUBm0NtmZcAjcScM4JAMAq0tHGliVrekavVIeB8hIc+KGqdJI</span><br />
<img src="//img.jandan.net/img/blank.gif" οnlοad="jandan_load_img(this)" /><span class="img-hash">3c48IBOKg44u0mwdXOFpDFEjxNeWgYk3/o5OzbmgSi5fBz5r060SzTwPVBgPIXg9i3yFBwwztwrZYR2bjYEwesbIbGFZuml7ngJNAuASe2Xjt5AD2DRaWRU</span></p>
                        </div>
                        <div class="jandan-vote">
                            <span class="comment-report-c">
                                <a title="投诉" href="javascript:;" class="comment-report" data-id="3734604">[投诉]</a>
                            </span>
                            <span class="tucao-like-container">
                            <a title="圈圈/支持" href="javascript:;" class="comment-like like" data-id="3734604" data-type="pos">OO</a> [<span>123</span>]
                            </span>
                            <span class="tucao-unlike-container">
                            <a title="叉叉/反对" href="javascript:;" class="comment-unlike unlike" data-id="3734604" data-type="neg">XX</a> [<span>51</span>]

                            <a href="javascript:;" class="tucao-btn" data-id="3734604"> 吐槽 [9] </a>
                            </span>
                        </div>
                    </div>
                </div>
            </li>


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

核心方法是:jandan_load_img

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

<!--
  <script src="//cdn.jandan.net/static/min/2163d136d2142160d02749fa2e4a8131.51111215.js"></script>
  --> <script src="//cdn.jandan.net/static/min/1d694f08895d377af4835a24f06090d0.28100001.js"></script>

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

这段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 href="' + c.replace(/(\/\/\w+\.sinaimg\.cn\/)(\w+)(\/.+\.(gif|jpg|jpeg))/, "$1large$3") + '" target="_blank" class="view_img_link">[a]</a>');
        d.before(a);
        d.before("<br>");
        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下载队列,解密并下载。

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

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值