前言

即将迎来国庆+中秋小长假,部分同事已经请假提前回家,工作氛围感觉渐淡下来,于是开始整理最近以来的工作的总结,以及开始准备节后一个技术沙龙的议题,还有节后的工作计划,闲暇之余聊到了二哥(gainover)之前的“安全圈有多大?”于是又重温了一遍,二哥作为生物学博士,使用生物学中分子的分析方法分析了安全圈的关系。二哥通过爬取腾讯微博的数据,以自己为起始点爬取用户的关注了哪些人,通过这种可视化方法能得到很多有意思的信息,下面记录一下实践过程吧~

一、数据获取

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.javaweb.core.net.HttpRequest;
import org.javaweb.core.net.HttpResponse;
import utils.MysqlConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * Created by jeary on 2017/9/28.
 */
public class WeiboSpider {

    public static void main(String[] args) {

        Map<String[], Boolean> urls = new HashMap<String[], Boolean>();
        urls.put(new String[]{"16525***", "**"}, false);
        crawllWeibo(urls);

    }
    public static void crawllWeibo(Map<String[], Boolean> urlMap) {
        Map<String[], Boolean> weibos = new HashMap<String[], Boolean>();
        String id = "";
        String name = "";
        for (Map.Entry<String[], Boolean> url : urlMap.entrySet()) {
            id = url.getKey()[0];
            name = url.getKey()[1];

            System.out.println(id + ":" + name);
            long siteid = findID(id);

            if (siteid == 0) {
                add(new Object[]{id, name});
                siteid = findID(id);
            }
            System.out.println("新的父节点ID:" + siteid + "\t");
            Map<String, String> links = getTwoWeiboLinks(id);
            for (Map.Entry<String, String> linkMap : links.entrySet()) {
                String weibo_id = linkMap.getKey();
                String weibo_name = linkMap.getValue();

                weibos.put(new String[]{weibo_id, weibo_name}, false);
                System.out.println(weibo_id + "\t" + weibo_name);

                long ref_id = findID(weibo_id);
                if (ref_id == 0) {
                    add(new Object[]{weibo_id, weibo_name});
                    ref_id = findID(weibo_id);
                }
                System.out.println("发现新关系,准备入库:" + siteid + " -- " + ref_id);
                add_ref(new Object[]{siteid, ref_id});
            }
            System.out.println("");
        }
        crawllWeibo(weibos);

    }

    public static Map<String, String> getTwoWeiboLinks(String id) {
        Map<String, String> allLinks = new HashMap<String, String>();

        for (int i = 1; i < 11; i++) {
            String body = getHtml(id, i);
            Map<String, String> links = getWeiboLinks(body);
            allLinks.putAll(links);
        }
        return allLinks;
    }

    /**
     * 解析Json获取用户名和ID
     *
     * @param jsonStr
     * @return
     */
    public static Map<String, String> getWeiboLinks(String jsonStr) {
        Map<String, String> links = new HashMap<String, String>();
        try {
            JSONObject jsonObject = JSON.parseObject(jsonStr);
            JSONArray cards = jsonObject.getJSONArray("cards");
            JSONObject jsonObject1 = null;
            for (int i = 0; i < cards.size(); i++) {
                jsonObject1 = cards.getJSONObject(i);
                JSONArray card_group = jsonObject1.getJSONArray("card_group");
                if (card_group.size() >= 19) {
                    for (int j = 0; j < card_group.size(); j++) {
                        JSONObject cardobj = card_group.getJSONObject(j);
                        JSONObject user = cardobj.getJSONObject("user");
                        String id = user.getString("id");
                        String name = user.getString("screen_name");
                        links.put(id, name);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        return links;
    }

    /**
     * @param id
     * @return
     */
    public static String getHtml(String id, int page) {
        String url = "https://m.**.cn/api/container/getIndex?containerid=231051_-_followers_-_" + id +
                "&page=" + page;
        String body = "";
        try {

            System.out.println("发起请求:" + url);
            HttpRequest request = new HttpRequest();
            request.url(url);
            request.followRedirects(true);
            request.userAgent("Mozilla/5.0 (Linux; Android 5.1.1; Nexus 4 Build/LMY48T) AppleWebKit/537.36 (KHTML, " +
                    "like Gecko) Chrome/40.0.2214.89 Mobile Safari/537.36");
            HttpResponse response = request.get();
            body = response.body();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return body;
    }
}

核心代码已经给出,所以这里省略爬取流程。但是关于如何爬取博中的数据,这里不得不提一下:
由于
博主站做了数据限制,只显示5页,通过伪造手机端ua发现手机端可加载10页,也就是10*20,可抓取200人关注者。
(PS:如果有大神有突破数据限制抓取所有关注者请不吝赐教~)

请求之后会返回20个他关注的人,通过JSON解析可以很方便的取出,但是这里有个小坑,取了cards后,要提取的数据可能不是在第一个,所以需要循环取。
我们以“余弦”作为爬虫起始点开始爬取数据,由于没有开多线程,总过爬取了接近两个小时,大约发起了一共26万请求(2.6w用户10页),爬行的过程中需要对数据进行入库存储,还需要构造相应的关系数据。
爬取的
博用户数据如图:


关系数据:

二、导出数据

  在爬取过程中,开始出现大量非安全圈的*博账号,所以我停止了爬虫开始进行数据整理准备导入。


直接导出CSV然后将数据整理为以上格式,分别为关系数据和*博用户数据,准备好以上两个数据开始导入

导入节点数据:


导入边数据:


此时切换到“图”窗口,看到如下密集的点构成的正方形说明数据导入成功

三、使用Gephi进行可视化

选择一个布局算法,进行设置后点击应用,然后


随着时间过去,布局开始慢慢明显,等到点完全分开的时候我们就可以停止布局算法了(PS:布局算法特别消耗CPU和内存)


如图(不小心就玩得没内存了):


我们取消掉,然后停止布局算法。
此时的图应该已经处于展开状态,但是由于点太多,如果我们此时显示所有的微博名字是很吃内存的,我们可以运行一些算法和过滤来让可视化变得友好。

结果一系列操作后,可视化效果变得友好了,如图:


最后我们通过Gephi的Sigma插件生成HTML,

后话:

  对于添加新连接 添加新关系类的代码,小伙伴们请自行完善,完成的请联系小冰,我们一起愉快的玩耍。关于现成的数据查询展示页面为 Web Security&amp;Penetration Testing 交流圈小福利,有兴趣入圈的可以留言。

  后续公开信息中秘密的寻找,就靠大家去慢慢发掘了。比方说通过分析一个人的所有微博,来给目标画像包括兴趣爱好、职业、作息规律、等等。

源链接

Hacking more

...