文档章节

网络机器人的识别与攻防的经典案例(也即爬虫与反爬虫的经典案例)

杨尚川
 杨尚川
发布于 2015/04/11 23:40
字数 2000
阅读 2264
收藏 22

本文我们介绍一个网络机器人的识别与攻防的经典案例(也即爬虫与反爬虫的经典案例)。使用到的代码见本人的superword项目:

https://github.com/ysc/superword/blob/master/src/main/java/org/apdplat/superword/tools/ProxyIp.java 


我们的目的是要使用机器人自动获取站点http://ip.qiaodm.com/ 和站点http://proxy.goubanjia.com/ 的免费高速HTTP代理IP和端口号。


不过他们未对机器人进行识别,如通过如下代码就可以获取网页内容:

public static void main(String[] args) {
    try {
        String url = "http://proxy.goubanjia.com/";
        HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection();
        connection.setConnectTimeout(10000);
        connection.setReadTimeout(10000);
        connection.setUseCaches(false);
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        StringBuilder html = new StringBuilder();
        String line = null;
        while ((line=reader.readLine()) != null){
            html.append(line);
        }
        LOGGER.info("HTML:"+html);
    }catch (Exception e){
        LOGGER.error(e.getMessage());
    }
}

尽管如此,但是他们却考虑到了机器人的防范,通过分析发现,两个站点的防范措施是一致的,所以破一得二。

他们是如何防范的呢?我们看看IP:163.125.217.56和端口:9797,我们利用FIREFOX的FIREBUG插件进行分析,如下图所示:

这里,如果我们直接调用选中的td节点的Jsoup的Element的text()方法,那么得到的值就会是  16363.1.125  25.21717.5.5,而不是我们在页面上看到的IP:163.125.217.56,还有<script>下面的那个6我们在源代码中是看不到的,这是<script>里面的JS执行之后动态生成的结果,对于端口9797也一样,在源代码中所有的端口全部是8080,我们这里之所以在上图中看到了6和9797,这是因为FIREBUG插件看到的是网页加载完毕且所有JS执行完毕之后的视图。

通过上面的分析,我们知道,防范的方法是将IP拆开在中间加入一些隐藏字符,并利用JS动态生成部分字符,而端口全部都是利用JS生成的

那么我们如何来应对这种防范方法呢?首先的第一个要求是我们的机器人要能动态执行JS其次是我们需要对IP字段进行逐节点分析,忽略隐藏节点中的字符。下面用代码说明:

1、动态执行JS

引入htmlunit依赖,注意的是如果你是使用slf4j日志的话,需要排除commons-logging依赖。
<dependency>
    <groupId>net.sourceforge.htmlunit</groupId>
    <artifactId>htmlunit</artifactId>
    <version>2.14</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

接下来看代码,这里获取到的html就是执行JS之后的内容:

private static final WebClient WEB_CLIENT = new WebClient(BrowserVersion.INTERNET_EXPLORER_11);
String html = ((HtmlPage)WEB_CLIENT.getPage(url)).getBody().asXml();


2、对IP字段进行逐节点分析,忽略隐藏节点中的字符

引入jsoup依赖。
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.8.1</version>
</dependency>

接下来看代码:

private static String getIps(Element element){
    StringBuilder ip = new StringBuilder();
    Elements all = element.children();
    LOGGER.info("");
    LOGGER.info("开始解析IP地址,机器读到的文本:"+element.text());
    AtomicInteger count = new AtomicInteger();
    all.forEach(ele -> {
        String html = ele.outerHtml();
        LOGGER.info(count.incrementAndGet() + "、" + "原始HTML:"+html.replaceAll("[\n\r]", ""));
        String text = ele.text();
        if(ele.hasAttr("style")
                && (ele.attr("style").equals("display: none;")
                    || ele.attr("style").equals("display:none;"))) {
            LOGGER.info("忽略不显示的文本:"+text);
        }else{
            if(StringUtils.isNotBlank(text)){
                LOGGER.info("需要的文本:"+text);
                ip.append(text);
            }else{
                LOGGER.info("忽略空文本");
            }
        }
    });
    LOGGER.info("----------------------------------------------------------------");
    LOGGER.info("解析到的ip: "+ip);
    LOGGER.info("----------------------------------------------------------------");
    Matcher matcher = IP_PATTERN.matcher(ip.toString());
    if(matcher.find()){
        String _ip = matcher.group();
        LOGGER.info("ip地址验证通过:"+_ip);
        return _ip;
    }else{
        LOGGER.info("ip地址验证失败:"+ip);
    }
    return null;
}

接着看运行过程的输出:

开始解析IP地址,机器读到的文本:61 61 . 18 18 5. 1 1 49 .1 .1 63
1、原始HTML:<div style="display:inline-block;">  <script type="text/javascript">//<![CDATA[document.write('');//]]>                </script> </div>
忽略空文本
2、原始HTML:<label style="display: none;"> 61 </label>
忽略不显示的文本:61
3、原始HTML:<span> 61 </span>
需要的文本:61
4、原始HTML:<div style="display:inline-block;">  . </div>
需要的文本:.
5、原始HTML:<div style="display:inline-block;">  <script type="text/javascript">//<![CDATA[document.write('');//]]>                </script> </div>
忽略空文本
6、原始HTML:<span style="display:inline-block;"> <script type="text/javascript">//<![CDATA[document.write('');//]]>                </script> </span>
忽略空文本
7、原始HTML:<p style="display: none;"> 18 </p>
忽略不显示的文本:18
8、原始HTML:<span> 18 </span>
需要的文本:18
9、原始HTML:<div style="display:inline-block;">  <script type="text/javascript">//<![CDATA[document.write('5.');//]]>                </script> 5. </div>
需要的文本:5.
10、原始HTML:<p style="display: none;"> 1 </p>
忽略不显示的文本:1
11、原始HTML:<span> 1 </span>
需要的文本:1
12、原始HTML:<div style="display:inline-block;">  49 </div>
需要的文本:49
13、原始HTML:<label style="display:none;"> .1 </label>
忽略不显示的文本:.1
14、原始HTML:<span> .1 </span>
需要的文本:.1
15、原始HTML:<div style="display:inline-block;">  <script type="text/javascript">//<![CDATA[document.write('');//]]>                </script> </div>
忽略空文本
16、原始HTML:<span style="display:inline-block;"> 63 </span>
需要的文本:63
----------------------------------------------------------------
解析到的ip: 61.185.149.163
----------------------------------------------------------------


下面是通过上面的分析程序获取到的部分能隐藏自己IP的代理服务器IP和端口号:

124.88.67.33:81
183.207.224.13:80
111.161.126.101:80
183.207.228.51:80
123.138.184.228:80
120.131.128.212:85
111.12.251.199:80
111.1.36.6:80
111.206.86.76:80
120.198.243.111:80
222.138.229.17:8104
123.125.104.240:80
124.88.67.25:81
202.102.22.182:80
183.207.228.114:80
162.208.49.45:8089
183.207.228.116:80
120.192.249.74:80
124.202.177.26:8118
124.88.67.32:80
111.161.126.100:80
183.207.224.14:80
183.207.224.43:80
111.206.81.248:80
183.207.224.45:80
182.118.31.110:80
124.88.67.53:80
111.13.109.52:80
190.38.26.167:8080
118.26.183.43:80
101.226.249.237:80
202.108.50.75:82
202.106.16.36:3128
111.1.36.133:80
124.88.67.24:80

有了这些IP和端口号,我们在JAVA中如何使用呢?只需要设置系统属性即可。

System.setProperty("proxySet", "true");
System.setProperty("http.proxyHost", ip);
System.setProperty("http.proxyPort", port);

设置完系统属性之后,我们如何判断有没有生效呢?我们可以通过看看在ip138的眼中,自己的IP是多少,然后和自己之前的IP作比较,看是否发生变化,如果发生变化,则认为我们的代理成功为我们向外部隐藏了自己的真实IP。

如何从ip138获取自己的外部地址呢?看如下代码

public static String getCurrentIp(){
    try {
        String url = "http://1111.ip138.com/ic.asp?timestamp="+System.nanoTime();
        String text = Jsoup.connect(url)
                .header("Accept", ACCEPT)
                .header("Accept-Encoding", ENCODING)
                .header("Accept-Language", LANGUAGE)
                .header("Connection", CONNECTION)
                .header("Host", "1111.ip138.com")
                .header("Referer", "http://ip138.com/")
                .header("User-Agent", USER_AGENT)
                .ignoreContentType(true)
                .timeout(5000)
                .get()
                .text();
        LOGGER.info("检查自身IP地址:"+text);
        Matcher matcher = IP_PATTERN.matcher(text);
        if(matcher.find()){
            String ip = matcher.group();
            LOGGER.info("自身IP地址:"+ip);
            return ip;
        }
    }catch (Exception e){
        LOGGER.error(e.getMessage());
    }
    LOGGER.info("检查自身IP地址失败,返回之前的IP地址:"+ previousIp);
    return previousIp;
}

最后看看程序运行的部分截图如下:

尝试使用新的代理:186.91.60.155:8080
检查自身IP地址:您的IP地址 您的IP是:[186.91.60.155] 来自:委内瑞拉
自身IP地址:186.91.60.155
Thread[main,5,main]自动更换代理成功!
Thread[main,5,main]更换代理耗时:4025毫秒
将66条代理IP地址写入本地
将81条能隐藏自己的代理IP地址写入本地
将108条不能隐藏自己的代理IP地址写入本地
Thread[main,5,main]请求重新更换代理
Thread[main,5,main]开始重新更换代理
尝试使用新的代理:117.158.98.214:80
检查自身IP地址:您的IP地址 您的IP是:[117.158.98.214] 来自:河南省许昌市 移动
自身IP地址:117.158.98.214
Thread[main,5,main]自动更换代理成功!
Thread[main,5,main]更换代理耗时:176毫秒
将66条代理IP地址写入本地
将81条能隐藏自己的代理IP地址写入本地
将108条不能隐藏自己的代理IP地址写入本地
Thread[main,5,main]请求重新更换代理
Thread[main,5,main]开始重新更换代理
尝试使用新的代理:120.131.128.212:85
检查自身IP地址:您的IP地址 您的IP是:[111.200.10.82] 来自:北京市 联通
自身IP地址:111.200.10.82
Thread[main,5,main]自动更换代理成功!
Thread[main,5,main]更换代理耗时:240毫秒
将66条代理IP地址写入本地
将81条能隐藏自己的代理IP地址写入本地
将108条不能隐藏自己的代理IP地址写入本地



完整的代码见本人的superword项目:https://github.com/ysc/superword/blob/master/src/main/java/org/apdplat/superword/tools/ProxyIp.java 

© 著作权归作者所有

杨尚川

杨尚川

粉丝 1103
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
网络机器人出没,如何利用大数据技术实时识别和防护 | 硬创公开课(总结+视频)

保守估计,互联网约50%~60%的流量由网络机器人贡献,航空票务、政府公示、社保信息、公积金信息、招聘简历等,都是网络爬虫的重灾区。网络爬虫不但虚耗资源,影响正常用户体验,还容易导致信...

又田
2018/01/08
0
0
京东安全策略分析: 黑产 AI 用得 666,怎么怼回去

无人机送货、叮咚家庭助手、智慧物流、智慧供应链……近两年,主打“技术开启商业智能化”概念的京东,落地了众多相关产品和实际应用,其中大数据成为重要的助推力。 当大数据渗透到越来越多...

郭佳
2017/12/24
0
0
假评论“捅了”马蜂窝,办事的真不是人?

毫无疑问,马蜂窝刚刚度过一个黑色周末。 10月20号微信公众号“小声比比”发表了一篇名为《独家|估值175亿的旅游独角兽,是一座僵尸和水军构成的鬼城》的文章,直指旅游网站马蜂窝评论抓取抄...

又田
2018/10/23
0
0
前端工程师需要知道的反爬虫知识

爬虫技术 :网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有...

刘志强
2018/07/19
0
0
网络机器人出没,如何利用大数据技术实时识别和防护 | 硬创公开课预告

保守估计,互联网约50%~60%的流量由网络机器人贡献,航空票务、政府公示、社保信息、公积金信息、招聘简历等,都是网络爬虫的重灾区。网络爬虫不但虚耗资源,影响正常用户体验,还容易导致信...

又田
2018/01/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Excel数据透视表基础:数据透视表的正确数据格式

1.数据透视表:是一种数据分析方法, 一.使用数据透视表必须用正确的数据格式: 1.数据必须有标题: 2.数据表不能有相同的标题 3.数据表中不能有合并单元格 4.不能有 小计、合计、空行、分类...

东方墨天
昨天
10
0
聊聊nacos的NacosDiscoveryAutoConfiguration

序 本文主要研究一下nacos的NacosDiscoveryAutoConfiguration NacosDiscoveryAutoConfiguration nacos-spring-boot-project/nacos-discovery-spring-boot-autoconfigure/src/main/java/com/a......

go4it
昨天
10
0
如何保证消息的顺序性?

面试题 如何保证消息的顺序性? 面试官心理分析 其实这个也是用 MQ 的时候必问的话题,第一看看你了不了解顺序这个事儿?第二看看你有没有办法保证消息是有顺序的?这是生产系统中常见的问题...

米兜
昨天
18
0
网络安全市场需求

最近,网络安全技能差距的热门话题流传开来。技能差距经常被紧急讨论,可以看出它在实践中的作用是很大的。但信息安全是一门广泛的学科,所以在谈论“技能差距”时需要更具体。有专家表示,真...

linuxCool
昨天
12
0
饿了么快应用初体验

作者:饿了么 顾诚 为什么我们选择了快应用 在很长一段时间里,原生饿了么应用对于新用户来说体验成本略高,对于迫切想要点餐的老用户操作有点繁琐;而 Web 版的饿了么应用在体验、速度、功能...

前端老手
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部