zoukankan      html  css  js  c++  java
  • 国家统计局数据采集

    概述

    国家统计局的公开数据真实性强,宏观且与我们的生活息息相关。

    因此,采集此数据作为数据分析实验的数据再好不过。

    采集过程

    采集各种公开数据的第一步就是分析网页。

    数据展示界面

    上面的图是国家统计局年度数据的界面。 左边是数据分类的树形菜单,右边是每个菜单点击之后显示的数据,可以设置年份来过滤数据。

    采集数据分类树

    根据页面的情况,首先,我们需要采集树形菜单中的数据,然后再根据菜单的分类来依次采集右边的数据。 这样可以避免采集的遗漏。

    爬虫采集数据一般有 2 种情况:

    • 采集 html 页面,然后分析其中的结构,提取出数据
    • 查看是否存在获取数据的 API,直接从 API 中提取数据

    通过分析网页的加载过程,发现国际统计局的数据是有 API 的,这就节省了很多时间。 API 信息如下:

    host: "https://data.stats.gov.cn/easyquery.htm"
    method: POST
    params:  id=zb&dbcode=hgnd&wdcode=zb&m=getTree
    

    通过 python 的 requests 库模拟 POST 请求就可以获取到树形菜单中的数据了。

    def init_tree(tree_data_path):
        data = get_tree_data()
        with open(tree_data_path, "wb") as f:
            pickle.dump(data, f)
    
    
    def get_tree_data(id="zb"):
        r = requests.post(f"{host}?id={id}&dbcode=hgnd&wdcode=zb&m=getTree", verify=False)
        logging.debug("access url: %s", r.url)
        data = r.json()
    
        for node in data:
            if node["isParent"]:
                node["children"] = get_tree_data(node["id"])
            else:
                node["children"] = []
    
        return data
    

    直接调用上面的 init_tree 函数即可,树形菜单会以 json 格式序列化到 tree_data_path 中。
    序列化的目的是为了后面采集数据时可以反复使用,不用每次都去采集这个树形菜单。(毕竟菜单是基本不变的)

    根据分类采集数据

    有了分类的菜单,下一步就是采集具体的数据。 同样,通过分析网页,数据也是有 API 的,不用采集 html 页面再提取数据。

    host: "https://data.stats.gov.cn/easyquery.htm"
    method: GET
    params: 参数有变量,具体参见代码
    

    采集数据稍微复杂一些,不像采集树形菜单那样访问一次 API 即可,而是遍历树形菜单,根据菜单的信息访问 API。

    # -*- coding: utf-8 -*-
    
    import logging
    import os
    import pickle
    import time
    
    import pandas as pd
    import requests
    
    host = "https://data.stats.gov.cn/easyquery.htm"
    tree_data_path = "./tree.data"
    data_dir = "./data"
    
    
    def data(sj="1978-"):
        tree_data = []
        with open(tree_data_path, "rb") as f:
            tree_data = pickle.load(f)
    
        traverse_tree_data(tree_data, sj)
    
    
    def traverse_tree_data(nodes, sj):
        for node in nodes:
            # 叶子节点上获取数据
            if node["isParent"]:
                traverse_tree_data(node["children"], sj)
            else:
                write_csv(node["id"], sj)
    
    
    def write_csv(nodeId, sj):
        fp = os.path.join(data_dir, nodeId + ".csv")
        # 文件是否存在, 如果存在, 不爬取
        if os.path.exists(fp):
            logging.info("文件已存在: %s", fp)
            return
    
        statData = get_stat_data(sj, nodeId)
        if statData is None:
            logging.error("NOT FOUND data for %s", nodeId)
            return
    
        # csv 数据
        csvData = {"zb": [], "value": [], "sj": [], "zbCN": [], "sjCN": []}
        for node in statData["datanodes"]:
            csvData["value"].append(node["data"]["data"])
            for wd in node["wds"]:
                csvData[wd["wdcode"]].append(wd["valuecode"])
    
        # 指标编码含义
        zbDict = {}
        sjDict = {}
        for node in statData["wdnodes"]:
            if node["wdcode"] == "zb":
                for zbNode in node["nodes"]:
                    zbDict[zbNode["code"]] = {
                        "name": zbNode["name"],
                        "cname": zbNode["cname"],
                        "unit": zbNode["unit"],
                    }
    
            if node["wdcode"] == "sj":
                for sjNode in node["nodes"]:
                    sjDict[sjNode["code"]] = {
                        "name": sjNode["name"],
                        "cname": sjNode["cname"],
                        "unit": sjNode["unit"],
                    }
    
        # csv 数据中加入 zbCN 和 sjCN
        for zb in csvData["zb"]:
            zbCN = (
                zbDict[zb]["cname"]
                if zbDict[zb]["unit"] == ""
                else zbDict[zb]["cname"] + "(" + zbDict[zb]["unit"] + ")"
            )
            csvData["zbCN"].append(zbCN)
    
        for sj in csvData["sj"]:
            csvData["sjCN"].append(sjDict[sj]["cname"])
    
        # write csv file
        df = pd.DataFrame(
            csvData,
            columns=["sj", "sjCN", "zb", "zbCN", "value"],
        )
        df.to_csv(fp, index=False)
    
    
    def get_stat_data(sj, zb):
        payload = {
            "dbcode": "hgnd",
            "rowcode": "zb",
            "m": "QueryData",
            "colcode": "sj",
            "wds": "[]",
            "dfwds": '[{"wdcode":"zb","valuecode":"'
            + zb
            + '"},{"wdcode":"sj","valuecode":"'
            + sj
            + '"}]',
        }
    
        r = requests.get(host, params=payload, verify=False)
        logging.debug("access url: %s", r.url)
        time.sleep(2)
    
        logging.debug(r.text)
        resp = r.json()
        if resp["returncode"] == 200:
            return resp["returndata"]
        else:
            logging.error("error: %s", resp)
            return None
    

    代码说明:

    1. tree_data_path = "./tree.data" : 这个是第一步序列化出的树形菜单数据
    2. 采集的数据按照树形菜单中的每个菜单的编号生成相应的 csv
    3. 树形菜单的每个叶子节点才有数据,非叶子节点不用采集
    4. 调用 data 函数来采集数据,默认是从 1978 年的数据开始采集的

    采集结果

    本次采集的结果有 1917 个不同种类的数据。
    下载地址: https://databook.top/data/de9d8cc6-2bab-4ef1-b09f-8dcf83c32648/detail

  • 相关阅读:
    开源项目之小玩具---各种机器人开源硬件
    目标检测之显著区域检测---国外的一个图像显著区域检测代码及其效果图 saliency region detection
    目标检测之指尖检测---指尖检测的新方法几种
    模式匹配之sift--- sift图像特征提取与匹配算法代码
    vi 之行号操作---显示行号、跳到指定行
    模式匹配之图像融合---小波变换的融合
    目标检测之harr---角点检测harr 的opencv实现
    java写 excel
    矩阵连乘最小权值
    leetcode Word Break I II 算法分析
  • 原文地址:https://www.cnblogs.com/wang_yb/p/14636575.html
Copyright © 2011-2022 走看看