使用Java爬蟲今日頭條武漢疫情統計數據信息

這幾天在用手機版的APP頭條時會發現,它開放了一個疫情數據的展示頁面,作爲一個有理想有抱負的,從事數據工作方面的程序員來說,想到了把他的數據拿下來就好了,這樣我們自己也可以做一些數據處理的工作,從而進行數據的二次加工和歷史留存,這個東西仔細想一下有很大益處的,可以提高對問題的分析能力,對視野的擴充,對頭條系的程序員開發頁面的思路和相關設計都是可以進行學習的。接下來分析一下我的簡易過程。

1、首先通過代理或分享等方式,獲取到該頁面的URL,這裏獲取到的URL如下所示:

https://i.snssdk.com/feoffline/hot_list/template/hot_list/forum_tab.html?activeWidget=1

2、然後在瀏覽器端,查看網頁的源代碼可以知道這個頁面的渲染方式爲客戶端渲染頁面佈局,調用服務端提供的數據接口返回JSON數據,再使用開發者工具對該頁面的請求進行分析,最終獲取到了如下相關的URL信息:

頁面佈局相關處理JS文件:

https://s3.pstatp.com/toutiao/feoffline/hot_list/resource/hot_list/js/forum_tab.6ed55a07.chunk.js

中國各個省份數據簡易API:

https://i.snssdk.com/ncov2019/hospital/district?activeWidget=1&adcode=110000&level=1

包含了各個省份地市區的明細數據和一些統計數據API:

https://i.snssdk.com/forum/home/v1/info/?activeWidget=1&forum_id=1656784762444839

3、使用瀏覽器的開發者工具,查看佈局附近相關的class的樣式爲:epidemic-brief,如下圖所示:

武漢加油!使用Java爬取頭條疫情統計數據

然後根據這個樣式名稱,在頁面佈局及處理的JS文件中,查詢這個字符串,可以找到相關佈局處理代碼如下所示:

核心處理代碼1,進行頁面的渲染處理:

e.a = function(t) {
    var e = t.updateTime,
    i = t.confirmedCount,
    a = t.curedCount,
    s = t.deadCount,
    o = t.suspectedCount,
    c = t.deadDiff,
    l = t.confirmedDiff,
    h = t.curedDiff,
    p = t.suspectedDiff;
    return r.a.createElement("div", {
        className: "epidemic-brief"
    },
    r.a.createElement("div", {
        className: "epidemic-brief-header"
    },
    r.a.createElement("h2", null, "\\u5168\\u56fd\\u75ab\\u60c5\\u72b6\\u51b5"), r.a.createElement("p", {
        className: "epidemic-brief-label"
    },
    "\\u66f4\\u65b0\\u65f6\\u95f4 ", n()(e, "Y/MM/dd hh:mm"))), r.a.createElement("div", {
        className: "epidemic-brief-body"
    },
    r.a.createElement("div", {
        className: "epidemic-brief-stats"
    },
    r.a.createElement("span", {
        className: "epidemic-brief-stats-num confirmed"
    },
    i || "-"), r.a.createElement("span", {
        className: "epidemic-brief-stats-label"
    },
    "\\u786e\\u8bca\\u4eba\\u6570"), r.a.createElement("div", {
        className: "epidemic-brief-stats-label-small"
    },
    r.a.createElement("span", {
        className: "epidemic-brief-stats-label-small title"
    },
    "\\u8f83\\u6628\\u65e5"), r.a.createElement("span", {
        className: "epidemic-brief-stats-label-small left"
    },
    "+", l || 0))), r.a.createElement("div", {
        className: "epidemic-brief-stats"
    },
    r.a.createElement("span", {
        className: "epidemic-brief-stats-num suspected"
    },
    o || "-"), r.a.createElement("span", {
        className: "epidemic-brief-stats-label"
    },
    "\\u7591\\u4f3c\\u75c5\\u4f8b"), r.a.createElement("div", {
        className: "epidemic-brief-stats-label-small"
    },
    r.a.createElement("span", {
        className: "epidemic-brief-stats-label-small title"
    },
    "\\u8f83\\u6628\\u65e5"), r.a.createElement("span", {
        className: "epidemic-brief-stats-label-small center2"
    },
    "+", p || 0))), r.a.createElement("div", {
        className: "epidemic-brief-stats"
    },
    r.a.createElement("span", {
        className: "epidemic-brief-stats-num dead"
    },
    s || "-"), r.a.createElement("span", {
        className: "epidemic-brief-stats-label"
    },
    "\\u6b7b\\u4ea1\\u4eba\\u6570"), r.a.createElement("div", {
        className: "epidemic-brief-stats-label-small"
    },
    r.a.createElement("span", {
        className: "epidemic-brief-stats-label-small title"
    },
    

核心處理代碼2,對返回的數據進行解析與處理:

case 3:
    e = t.sent,
    a = e[0],
    r = e[1],
    a.forum.extra.ncov_image_url = a.forum.extra.ncov_image_url.replace("http://p3.pstatp.com/large/", ""),
    s = JSON.parse(a.forum.extra.ncov_string_list);
    try {
        n = JSON.parse(a.forum.extra.ncov_image_url)
    } catch(d) {
        n = JSON.parse('[{"min":1,"max":9,"color":"rgba(250,243,187,1)"},{"min":10,"max":99,"color":"rgba(246,192,82,1)"},{"min":99,"max":499,"color":"rgba(226,110,28,1)"},{"min":500,"max":999,"color":"rgba(146,55,35,1)"},{"min":1000,"max":null,"color":"rgba(99,37,22,1)"}]')
    }
    o = s.nationwide,
    c = o[0],
    l = o[1],
    console.log("are", s, l, c, c.confirmedNum - l.confirmedNum),
    h = {
        seriesIndex: 0
    },
    [a.forum.extra.gps_city_code, a.forum.extra.cure_city_code, a.gps_city_code, a.cure_city_code].some(function(t) {
        if (t) {
            var e = C(r, "" + t.slice(0, 2));
            return e && (h.name = e.name),
            !!e
        }
    }),
    h.name || (h.name = "\\u6e56\\u5317"),
    p = {
        brief: {
            updateTime: 1e3 * s.updateTime,
            confirmedCount: c.confirmedNum,
            suspectedCount: c.suspectedNum,
            curedCount: c.curesNum,
            deadCount: c.deathsNum,
            confirmedDiff: l ? c.confirmedNum - l.confirmedNum: 0,
            nationwide curedDiff: l ? c.curesNum - l.curesNum: 0,
            deadDiff: l ? c.deathsNum - l.deathsNum: 0,
            suspectedDiff: l ? c.suspectedNum - l.suspectedNum: 0
        },
        map: {
            chinaData: s.provinces.filter(function(t) {
                return t.confirmedNum > 0 || t.suspectedNum > 0
            }).map(function(t) {
                var e = r.find(function(e) {
                    return t.id === String(e.id)
                });
                return e ? {
                    name: e.name,
                    value: t.confirmedNum,
                    confirmedCount: t.confirmedNum,
                    deadCount: t.deathsNum
                }: null
            }).filter(function(t) {
                return !! t
            }),
            ranges: n.map(function(t) {
                return {
                    start: t.min,
                    end: t.max,
                    color: t.color,
                    label: 0 === t.max ? "\\u7591\\u4f3c\\u611f\\u67d3": "undefined" === typeof t.max || null === t.max ? t.min + "\\u4eba\\u4ee5\\u4e0a": t.min + "-" + t.max + "\\u4eba"
                }
            }),
            defaultTipData: h
        },
        

從中找到了幾個相關的變量:confirmedCount,curedCount,deadCount,suspectedCount,t.deadDiff,

confirmedDiff,curedDiff,suspectedDiff。並且知道了從某個接口中的返回數據中取出了forum中的extra中的ncov_string_list的nationwide數據,這是一個數組類型的數據,表示了最近幾天的統計數據。

不言而喻,這幾個變量就是頁面的主要的幾項指標的數據。然後根據這幾個關鍵字和返回結構,最後確定瞭如下接口爲我們需要的相關上方的彙總數據:

https://i.snssdk.com/forum/home/v1/info/?activeWidget=1&forum_id=1656784762444839。

4、這樣Java爬蟲需要的基本要素我們都有了,接下來可以簡單的使用Jsoup和FastJSON簡易的完成爬蟲代碼:

public class CollectData1 {
	//定義幾個常量防止反爬蟲
	public static String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:49.0) Gecko/20100101 Firefox/49.0";
	public static String HOST = "i.snssdk.com";
	public static String REFERER = "https://i.snssdk.com/feoffline/hot_list/template/hot_list/forum_tab.html?activeWidget=1";
	
	public static void main(String[] args) throws IOException {
		//根URL
		String url = "https://i.snssdk.com/forum/home/v1/info/?activeWidget=1&forum_id=1656784762444839";
		String resultBody = Jsoup.connect(url).
				userAgent(USER_AGENT).header("Host", HOST).header("Referer", REFERER).execute().body();
		JSONObject jsonObject = JSON.parseObject(resultBody);
		String ncovStringList = jsonObject.getJSONObject("forum").getJSONObject("extra").getString("ncov_string_list");
		JSONObject ncovListObj = JSON.parseObject(ncovStringList);
		//取出每日的相關彙總數據,可以把這個數據存儲到數據庫中
		//[{confirmedNum: 9809, curesNum: 183, date: "2020-01-31", deathsNum: 213, suspectedNum: 15238}]
		JSONArray nationwides = ncovListObj.getJSONArray("nationwide");
		//取出當日數據
		JSONObject currentDateData = nationwides.getJSONObject(0);
		//取出昨日數據
		JSONObject lastDayData = nationwides.getJSONObject(1);
		System.out.println(currentDateData);
		System.out.println(lastDayData);
		Map<String,Object> rData = new HashMap<String,Object>();
		//獲取確診人數
		rData.put("confirmedCount",currentDateData.getIntValue("confirmedNum"));
		//獲取疑似病例
		rData.put("suspectedNum",currentDateData.getIntValue("suspectedNum"));
		//獲取治癒人數
		rData.put("curesNum",currentDateData.getIntValue("curesNum"));
		//獲取死亡人數
		rData.put("deathsNum",currentDateData.getIntValue("deathsNum"));
		//獲取確診人數比上日
		rData.put("confirmedDiff",currentDateData.getIntValue("confirmedNum") - lastDayData.getIntValue("confirmedNum"));
		//獲取疑似病例比上日
		rData.put("suspectedDiff",currentDateData.getIntValue("suspectedNum") - lastDayData.getIntValue("suspectedNum"));
		//獲取治癒人數比上日
		rData.put("curedDiff",currentDateData.getIntValue("curesNum") - lastDayData.getIntValue("curesNum") );
		//獲取死亡人數比上日
		rData.put("deadDiff",currentDateData.getIntValue("deathsNum") - lastDayData.getIntValue("deathsNum"));
		System.out.println(JSON.toJSONString(rData));
	}
	

}

以上只是一個簡單的代碼,使用Java獲取到了相關的數據信息,從這個接口中,可以獲取到頭條頁面上方的8項基本數據,和各個省市的明細數據;通過另外的一個接口,可以獲取到每個省市的地圖數據。

數據獲取到了以後,每個人都可以根據自己的想法做一些基本的數據分析和展示效果等,也可以把每日的數據存儲到數據庫中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章