从零实现一个高性能网络爬虫(二)应对反爬虫之前端数据混淆

摘要

  • 上一篇以知乎网为例简单分享网络请求分析。这一篇主要分享一种应对反爬虫的方法,前端数据混淆。

目的

开始

  • 打开这个网站首页,然后控制台查看ip和port的对应标签。


  • 如上图(图一),从控制台的标签中可以看出ip加了一些无关不显示的标签来混淆数据,这里混淆的原理其实很简单,通过标签的style="display:none"属性来达到混淆的目的,也就是包含这个属性的标签是不会显示在页面上的。知道了这一点就比较好处理了,只需要在解析的时候把包含style="display:none"属性的标签去掉。就可以轻松的拿到ip和port数据了。
  • 代码如下
package com.cnblogs.wycm;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.net.URL;

/**
 *
 * 数据的解析采用的是Jsoup框架,Jsoup是一个操作HTML标签的Java库,它提供了非常方便的API来提取和操纵库,支持类似jquery的选择器来查找标签。
 * 由于请求比较单一,这里的网络请求并没有采用上一篇所使用HttpClient框架。直接通过Jsoup来执行http请求的。
 * 关于Jsoup的使用可以参考http://www.open-open.com/jsoup/
 *
 */
public class Chapter1 {
    public static void main(String[] args) throws IOException {
        Document document= Jsoup.parse(new URL("http://www.goubanjia.com/"), 10000);
        //获取class='table'的table的所有子节点tr
        Elements elements = document.select("table[class=table] tr");
        for (int i = 1; i < elements.size(); i++){
            //获取td节点
            Element td = elements.get(i).select("td").first();
            /**
             * 查找所有style属性包含none字符串的标签(页面上未显示的标签),并移除
             * 包括以下两种
             * style=display: none;
             * style=display:none;
             */
            for(Element none : td.select("[style*=none;]")){
                none.remove();
            }
            //移除空格
            String ipPort = td.text().replaceAll(" ", "");
            //打印
            System.out.println(ipPort);
        }
    }
}
/*
第一次运行打印结果:
183.129.246.228:8132
222.92.136.206:8987
54.238.186.100:8988
...
第二次运行打印结果:
183.129.246.228:8377
222.92.136.206:9059
54.238.186.100:8622
...
*/
  • ip地址能够准确的拿到了,却发现port被做了混淆,而且每次返回的port还在动态改变。大家可以通过把浏览器的JavaScrip脚本关闭后,然后刷新这个网页。会发现每次的port都不一样。我们每次看到的正确port都是通过JavaScript脚本处理后的。如果采用普通爬虫的方式拿到的port都是错误的。现在要想拿到正确的port,可以通过分析它JavaScrip脚本还原数据的逻辑。
  • 同样打开控制台->选择Sources->选择一行js代码打断点(点击行编号),如下图


  • 刷新网页—>页面Paused in debugger—>选择Elements->右键td节点->Break on...->subtree modifications。这两个步骤就是在设置断点调试,也就是在td节点发生改变的时候paused。


  • 选择Sources->F8(继续执行),这个时候又会有一次pause,也就是js脚本在还原正确port的时候(如下图)


  • 函数的调用栈有好多层,如何快速定位哪一个函数的技巧就是,看它局部变量表的变量变化,因为这里是port在发生改变,然后找到对应变量和对应逻辑函数。简单分析可以确定到port发生改变的函数是一个匿名函数,如下图


  • 格式化后,代码如下:
var _$ = ['\x2e\x70\x6f\x72\x74', "\x65\x61\x63\x68", "\x68\x74\x6d\x6c", "\x69\x6e\x64\x65\x78\x4f\x66", '\x2a', "\x61\x74\x74\x72", '\x63\x6c\x61\x73\x73', "\x73\x70\x6c\x69\x74", "\x20", "", "\x6c\x65\x6e\x67\x74\x68", "\x70\x75\x73\x68", '\x41\x42\x43\x44\x45\x46\x47\x48\x49\x5a', "\x70\x61\x72\x73\x65\x49\x6e\x74", "\x6a\x6f\x69\x6e", ''];
$(function() {
    $(_$[0])[_$[1]](function() {
        var a = $(this)[_$[2]]();
        if (a[_$[3]](_$[4]) != -0x1) {
            return
        }
        ;var b = $(this)[_$[5]](_$[6]);
        try {
            b = (b[_$[7]](_$[8]))[0x1];
            var c = b[_$[7]](_$[9]);
            var d = c[_$[10]];
            var f = [];
            for (var g = 0x0; g < d; g++) {
                f[_$[11]](_$[12][_$[3]](c[g]))
            }
            ;$(this)[_$[2]](window[_$[13]](f[_$[14]](_$[15])) >> 0x3)
        } catch (e) {}
    })
})
  • 还原后如下:
var _$ = ['.port', "each", "html", "indexOf", '*', "attr", 'class', "split", " ", "", "length", "push", 'ABCDEFGHIZ', "parseInt", "join", ''];
$(function() {
    $('.port').each(function() {
        var a = $(this).html();
        if (a.indexOf('*') != -0x1) {
            return
        }
        ;var b = $(this).attr('class');
        try {
            b = (b.split(" "))[0x1];
            var c = b.split("");
            var d = c.length;
            var f = [];
            for (var g = 0x0; g < d; g++) {
                f.push('ABCDEFGHIZ'.indexOf(c[g]))
            }
            ;$(this).html(window.parseInt(f.join('')) >> 0x3)
        } catch (e) {}
    })
})
  • 这段代码的逻辑,获取port标签的class属性值,取出属性中后面的几个大写字母,遍历该字符串,找出每次字符在'ABCDEFGHIZ'这个字符串中的索引,然后parseInt转换为整数,然后进行右移3位的操作。
  • 完整代码实现
package com.cnblogs.wycm;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.URL;

public class Chapter2 {
    public static void main(String[] args) throws IOException {
        Document document= Jsoup.parse(new URL("http://www.goubanjia.com/"), 10000);
        setPort(document);
        //获取class='table'的table的所有子节点tr
        Elements elements = document.select("table[class=table] tr");
        for (int i = 1; i < elements.size(); i++){
            //获取td节点
            Element td = elements.get(i).select("td").first();
            /**
             * 查找所有style属性包含none字符串的标签(页面上未显示的标签),并移除
             * 包括以下两种
             * style=display: none;
             * style=display:none;
             */
            for(Element none : td.select("[style*=none;]")){
                none.remove();
            }
            //移除空格
            String ipPort = td.text().replaceAll(" ", "");
            //打印
            System.out.println(ipPort);
        }
    }

    /**
     * js代码port还原
     * @param doc
     */
    private static void setPort(Document doc){
        for (Element e : doc.select(".port")){//$('.port').each(function() {
            String a = e.text();//var a = $(this).html();
            if(a.indexOf("*") != -0x1){//if (a.indexOf('*') != -0x1) {
                return;
            }
            String b = e.attr("class");//var b = $(this).attr('class');
            b = b.split(" ")[0x1];//b = (b.split(" "))[0x1];
            String[] c = b.split("");//var c = b.split("");
            int d = b.length();//var d = c.length;
            StringBuilder f = new StringBuilder();//var f = [];
            for(int g = 0x0; g < d; g++){//for (var g = 0x0; g < d; g++) {
                f.append("ABCDEFGHIZ".indexOf(c[g]));//f.push('ABCDEFGHIZ'.indexOf(c[g]))
            }
            e.text(String.valueOf(Integer.valueOf(f.toString()) >> 0x3));//$(this).html(window.parseInt(f.join('')) >> 0x3)
        }
    }
}
  • maven依赖
 <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.10.2</version>
 </dependency>

总结

  • 该篇文章简单分项了下如何应对前端混淆的反爬虫。关于这种反爬虫,还有其它的一些应对方式。如采用无头浏览器的方式,比如phantomjs框架。这种无头浏览器原本是用来做自动化测试的。它是基于webkit内核的,所以它可以较容易的爬取这种前端混淆的这种网站。一般来说浏览器能够正常访问到的数据,这种方式也可以比较容易爬取这些数据。当然这种方式的最大问题就是效率比较低。因为这种方式它每加载一个页面,都需要下载它的附加资源,如js脚本,脚本下载完成后,还要去执行js脚本。
  • 我这里采用的方式是阅读js代码,得出前端混淆的逻辑,然后再通过目标语言来实现对应逻辑。这种方式如果针对一些简单的加密混淆还是很有用的。但是当遇到一些大型复杂的网站,如百度、微博等,需要抓取登录后的数据。这时候需要来手动模拟登录,相对来说,这种网站的模拟登录会更复杂,找各种登录参数来源。都会耗费大量精力。分析请求的成本会比较高。这种方式的优点就是爬取速度快,只获取目标数据。不需要额外网络请求成本。

版权声明
作者:wycm
出处:https://www.jianshu.com/p/6dc42d309e5a
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容