采集的文章内容不能直接发布(2022全球C++及系统软件技术大会|API信息如下(图))
优采云 发布时间: 2022-03-02 23:15采集的文章内容不能直接发布(2022全球C++及系统软件技术大会|API信息如下(图))
2022全球C++与系统软件技术大会| 3月11-12日上海点击查看详情>>>
概述
国家统计局的公开数据具有高度真实性、宏观性,与我们的生活息息相关。
因此,采集这个数据作为数据分析实验的数据再好不过了。
采集进程
采集各种公开可用数据的第一步是分析网页。
上图是国家统计局年度数据界面。左边是数据分类的树形菜单,右边是每个菜单点击后显示的数据。您可以设置年份来过滤数据。
采集数据分类树
根据页面的情况,首先我们需要采集树形菜单的数据,然后根据菜单的分类,依次是采集右边的数据. 这避免了 采集 的遗漏。
爬虫采集数据一般有两种情况:
通过分析网页的加载过程,发现国际统计局的数据有一个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
代码说明:
tree_data_path = "./tree.data" :这是第一步序列化的树形菜单数据采集的数据,根据树形菜单中每个菜单的编号生成对应的csv树形菜单。只有叶子节点有数据,非叶子节点不需要调用数据函数采集到采集数据,默认是采集从1978年的数据开始的结果采集
这个 采集 的结果有 1917 种不同类型的数据。
下载链接: