URL_SZSE = "http://bond.szse.cn/api/report/ShowReport/data"
HEADERS_SZSE = {
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Content-Type": "application/json",
"DNT": "1",
"Host": "bond.szse.cn",
"Referer": "http://bond.szse.cn/marketdata/statistics/report/struc/index.html",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
"X-Request-Type": "ajax",
"X-Requested-With": "XMLHttpRequest",
}
PARAMS_SZSE = {
"SHOWTYPE": "JSON",
"CATALOGID": "zqxqjycyyb",
"TABKEY": "tab1",
"jyrqStart": "2023-01",
"jyrqEnd": "2023-01",
"random": 0.9926478152057119,
}
MAPPING_SZSE = {
"tzzlb": "投资者类别",
"hjje": "合计金额",
"hjzb": "合计占比",
"gx": "国债",
"dz": "地方政府债",
"zj": "政策性金融债",
"bj": "政府支持债券",
"qx": "企业债券",
"sz": "公司债券",
"kz": "可转换债券",
"hz": "可交换公司债券",
"sm": "非公开发行公司债券",
"js": "非公开发行可交换公司债券",
"bc": "创新创业可转换债券",
"zc": "证券公司次级债券",
"zd": "证券公司短期债券",
"zr": "企业资产支持证券",
"rt": "不动产投资信托"
}
上交所现券交易月报网址:
http://bond.sse.com.cn/data/statistics/monthly/bond/
同样用chrome打开网页,并打开开发者工具(快捷键Ctrl+Shift+I),切换至Network界面。在网页中选择任一月份并点击查询,在开发者工具的Network界面寻找包含所需数据的链接、请求参数和数据格式。主要查看Headers、Payload和Preview三个界面,其中包含了获取数据的链接、请求参数和返回数据;从示例图片中,可在Headers界面找到现券交易月报数据的真实地址,发起请求的方法为Get,Payload页面明确获取数据的参数共5个,Preview页面显示数据格式为json,但多了一些字符前缀并不规整,需要先使用re模块进行处理,再使用json模块实现数据解析。
URL_SSE = "http://query.sse.com.cn/commonQuery.do"
HEADERS_SZSE = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"DNT": "1",
"Host": "query.sse.com.cn",
"Referer": "http://bond.sse.com.cn",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
}
PARAMS_SSE = {
"jsonCallBack": "jsonpCallback91796",
"isPagination": "false",
"sqlId": "COMMON_BOND_SCSJ_SCTJ_TJYB_XQJYYB_L",
"TRADEDATE": "2023-1",
"_": 1676636559107,
}
MAPPING_SSE = {
"TYPE": "投资者类别",
"TRADE_DATE": "报告期",
"AMT": "合计金额",
"AMT_PERCENT": "合计占比",
"JZGZ_AMT": "记账式国债",
"DFZ_AMT": "地方政府债券",
"JRZ_AMT": "普通金融债",
"QYZ_AMT": "企业债券",
"SMZ_AMT": "中小企业私募债券",
"GKGSZ_AMT": "公司债券",
"FGKGSZ_AMT": "非公开发行公司债券",
"KZZ_AMT": "可转换公司债券",
"FLZ_AMT": "分离交易的可转换公司债券",
"QYZC_AMT": "企业资产支持证券",
"XDZC_AMT": "信贷资产支持证券",
"BXCJZ_AMT": "可交换公司债券",
"OTHER_AMT": "其他债券",
}
最新一期现券交易月报数据抓取
结合上述关键参数,利用requests模块即可实现现券交易月报数据的抓取,我们将抓取到的数据保存到指定的文件夹中,以便于后续案例中使用。
PATH_BASE = os.path.abspath("./")
PATH_DATA = os.path.join(PATH_BASE, "Data")
if not os.path.exists(PATH_DATA):
os.mkdir(PATH_DATA)
report_date = datetime.date(2023, 1, 31)
深交所现券交易月报数据抓取
抓取深交所现券交易月报返回的json数据较为规整,主要的处理难点在于数据中包含有千分位符和空字符串。此外,Python默认字符编码为UTF-8格式,利用pandas将数据保存为csv格式时,若未修改字符编码为合适格式,使用Excel查看时可能出现中文乱码;可以通过指定字符编码为GB18030等格式避免这一问题。
PARAMS_SZSE.update({
"jyrqStart": report_date.strftime("%Y-%m"),
"jyrqEnd": report_date.strftime("%Y-%m"),
"random": np.random.random(),
})
response_szse = requests.get(
url=URL_SZSE,
headers=HEADERS_SZSE,
params=PARAMS_SZSE,
)
if response_szse.status_code == 200:
data_json_szse = json.loads(response_szse.text)
data_szse = pd.DataFrame.from_dict(data_json_szse[0]["data"])
data_szse = data_szse.reindex(columns=MAPPING_SZSE.keys())
data_szse.rename(columns=MAPPING_SZSE, inplace=True)
data_szse["报告期"] = report_date + pd.offsets.MonthEnd(0)
data_szse.set_index(["投资者类别", "报告期"], inplace=True)
data_szse = data_szse.applymap(lambda x: x.replace(',', '')).replace("", np.nan).astype(float)
data_szse.to_csv(f"{PATH_DATA}/bond_trade_szse_{report_date.strftime('%Y%m%d')}.csv", encoding="GB18030")
上交所现券交易月报数据抓取
抓取上交所现券交易月报返回的json数据前后包含多余的字符串,也是主要的处理难点,可通过正则匹配去除多余字符串。与深交所现券交易月报数据的保存一样,保存为csv文件时,仍需要注意指定字符编码为GB18030等格式,以避免Excel查看时的中文乱码问题。
PARAMS_SSE.update({
"jsonCallBack": f"jsonpCallback{np.random.randint(10000, 99999)}",
"TRADEDATE": f"{report_date.year}-{report_date.month}",
"_": int(datetime.datetime.now().timestamp()*1000),
})
response_sse = requests.get(
url=URL_SSE,
headers=HEADERS_SSE,
params=PARAMS_SSE,
)
if response_sse.status_code == 200:
data_raw_sse = re.findall("^.+\((\{.+\})\)$", response_sse.text)[0]
data_json_sse = json.loads(data_raw_sse)
data_sse = pd.DataFrame.from_dict(data_json_sse["result"])
data_sse = data_sse.reindex(columns=MAPPING_SSE.keys())
data_sse.rename(columns=MAPPING_SSE, inplace=True)
data_sse["报告期"] = report_date + pd.offsets.MonthEnd(0)
data_sse.set_index(["投资者类别", "报告期"], inplace=True)
data_sse = data_sse.astype(float)
data_sse.to_csv(f"{PATH_DATA}/bond_trade_sse_{report_date.strftime('%Y%m%d')}.csv", encoding="GB18030")
历史月报数据抓取
上述代码已经实现了沪深交易所现券交易月报数据的单次抓取,若需要对历史上的月报数据进行抓取,可将上述代码分别封装为工具函数,将月报日期设定为函数参数,再对需要抓取的日期循环调用工具函数,即可完成历史月报数据的批量抓取。需要注意的重点,一方面避免重复抓取,另一方面注意通过sleep函数控制抓取速度。
report_dates = pd.date_range(datetime.date(2022, 1, 1), datetime.date(2023, 1, 31), freq="M")
pbar = tqdm(report_dates)
for loop_date in pbar:
pbar.set_description(loop_date.strftime("%Y-%m-%d"))
if not os.path.exists(f"{PATH_DATA}/bond_trade_szse_{loop_date.strftime('%Y%m%d')}.csv"):
pass
if not os.path.exists(f"{PATH_DATA}/bond_trade_sse_{loop_date.strftime('%Y%m%d')}.csv"):
pass
sleep(1)
本次案例以抓取沪深交易所现券交易月报为例,主要展示了用Python实现网络数据抓取的简要流程,以及requests、re、datetime、pandas、numpy、tqdm的初步使用。若有错漏之处,还请各位批评指正。