粉丝服务

手把手教你学python进阶版第二课:Python网络数据抓取—以沪深交易所现券交易月报为例

手把手教你学python基础课系列快速回顾:

1、“零基础包学会” 手把手教你学python一:如何快速找到学习资源?

2、手把手教你学python第二课:如何迅速上手马上能run

3、手把手教你学python第三课:数据分析,学会使用pandas大杀器

4、手把手教你学python第四课:数据可视化

5、手把手教你学python第五课:各种渠道获取数据,快速学会windpy和网络爬虫

6、手把手教你学python第六课:技术分析入门-talib

手把手教你学python第六课:技术分析入门-talib-成交量及曲线形态

手把手教你学python第六课:技术分析入门-talib-安装及动量指标

7、手把手教你学python第七课:数据分析和机器学习


手把手教你学python进阶系列快速回顾:

1、手把手教你学python进阶版:整体框架

2、手把手教你学python进阶版第一课:Python常用文件目录操作—以清理PC微信缓存文件为例

2023

手把手教你学python系列进阶版

写在最初


在新的一年与尔岩说将继续之前的手把手教你学python专栏(停更的原因一部分是因为大家说之前的需要时间消化,所以停了一段时间,另一部分原因是想邀请比我更专业而且实用性更强的王者们来上进阶课程),主笔加入了我的两个非常好的两个朋友(笔名十月和木矛木心),下面是十月写在最初的话:


在塔勒布的《非对称风险》开篇章节中提到一句希腊谚语“Pathemata mathemata”,意即只有在痛苦的实践中才能学到东西。

从经济学专业背景逐步走向需要大量编程经验的量化方向,我对此谚语深有体会,虽然目前已转向固收投研,编程技能仍在日常工作中发挥着重要的作用。

作为非专业“码农”,收到与尔岩说的邀请,初时颇觉惶恐,后又逐渐释然。总结和分享这些年踩过许多坑得到的经验教训,既是抛砖引玉,也是学习的一种方式。

Python作为一种胶水语言,随着AI技术的火热被越来越多人知道,科学计算相关领域的工具包也非常全面。这里会先把我日常投研中经常用到的工具包简单总括介绍,主要涉及通用模块、科学计算、高性能编程、数据库交互、绘图、OFFICE协作、爬虫等七大类别,后续将结合具体案例再逐渐展开不同工具包的用法。


这里会定期更新一些实际案例和具体用法,如果你和我一样热爱用科技提高工作效率,并想要一起研究一些有意思的事情,欢迎一起来进阶!!


Python网络数据抓取


蓬蓬说:爬虫基础部分也讲解过,大家可以先做一下复习


在研究交易所债券的持仓和交易情况时,往往需要用到沪深交易所历史上公布的投资者持仓结构和现券交易月报数据。若采用手动复制粘贴的方式,需要花费大量时间的同时,也容易在数据整理的过程中出错,后续更新也并不方便。利用爬虫技术可以实现自动化数据抓取和清洗整理,Python有较完善的工具链来实现整个过程。


首先,我们在VSCode中新建一个jupyter notebook。并导入本案例中需要用到的模块。

#  导入数据抓取需要使用的Python模块import requestsimport reimport datetimeimport pandas as pdimport numpy as npimport os
# 用于显示循环进度条from tqdm import tqdm
# 用于批量抓取过程中控制抓取速度from time import sleep
# 用于解析字符串格式的json数据import json


除了Python工具包,其他还需要使用的工具主要为Chrome浏览器,通过开发者工具的Network界面跟踪获取数据的真实地址和请求参数,这里也可使用其他能够提供开发者工具的浏览器。


寻找现券交易数据的真实地址

  • 深交所现券交易月报关键参数

深交所现券交易月报网址:

http://bond.szse.cn/marketdata/statistics/report/trade/index.html

用chrome打开该网页,并打开开发者工具(快捷键Ctrl+Shift+I),切换至Network界面。在网页中选择任一月份并点击查询,在开发者工具的Network界面寻找包含所需数据的链接、请求参数和数据格式。主要查看Headers、Payload和Preview三个界面,其中包含了获取数据的链接、请求参数和返回数据;从示例图片中,可在Headers界面找到现券交易月报数据的真实地址,发起请求的方法为Get,Payload页面明确获取数据的参数共6个,Preview页面则显示数据格式为规整的json格式,即可以直接使用json模块实现数据解析。

#  将上述关键信息设为Python变量#  获取深交所现券交易月报数据的真实链接URL_SZSE = \"http://bond.szse.cn/api/report/ShowReport/data\"
# 获取深交所现券交易月报数据的HeadersHEADERS_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模块实现数据解析。

#  将上述关键信息设为Python变量#  获取上交所现券交易月报数据的真实链接URL_SSE = \"http://query.sse.com.cn/commonQuery.do\"
# 获取上交所现券交易月报数据的HeadersHEADERS_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\", # 5位尾数为随机数,抓取时需更新 \"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模块即可实现现券交易月报数据的抓取,我们将抓取到的数据保存到指定的文件夹中,以便于后续案例中使用。

#  利用os模块,在Notebook的相同目录下新建Data文件夹,用于存放数据PATH_BASE = os.path.abspath(\"./\")PATH_DATA = os.path.join(PATH_BASE, \"Data\")
# 判断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\"),                # 利用numpy.random模块生成随机数        \"random\": np.random.random(),    })
# 利用requests获取深交所现券交易数据response_szse = requests.get( url=URL_SZSE, headers=HEADERS_SZSE, params=PARAMS_SZSE,)
# 根据requests返回值的状态码,判断是否成功获取数据if response_szse.status_code == 200: # 利用json模块解析月报数据 data_json_szse = json.loads(response_szse.text) # 利用pandas整理json数据 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) # 利用pandas.offsets模块,将报告期设定为当月月末 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) # 将数据以CSV格式保存到Data文件夹中 # Python默认字符编码为UTF-8格式,若直接保存为CSV,EXCEL打开时中文可能乱码 # 保存时将字符编码修改为GB18030格式 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({                # 采用f-string的形式进行字符串拼接        # 利用numpy.random模块生成随机数        \"jsonCallBack\": f\"jsonpCallback{np.random.randint(10000, 99999)}\",                         # 采用f-string的形式进行字符串拼接        \"TRADEDATE\": f\"{report_date.year}-{report_date.month}\",                # 利用datetime模块获取当前时间戳,并乘以1000转换为JavaScript中的时间戳        \"_\": int(datetime.datetime.now().timestamp()*1000),    })
# 利用requests获取上交所现券交易数据response_sse = requests.get( url=URL_SSE, headers=HEADERS_SSE, params=PARAMS_SSE,)
# 根据requests返回值的状态码,判断是否成功获取数据if response_sse.status_code == 200: # 利用正则表达式去除json数据中多余的字符串 data_raw_sse = re.findall(\"^.+\\((\\{.+\\})\\)$\", response_sse.text)[0] data_json_sse = json.loads(data_raw_sse) # 利用pandas整理json数据 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) # 利用pandas.offsets模块,将报告期设定为当月月末 data_sse[\"报告期\"] = report_date + pd.offsets.MonthEnd(0) # 将投资者类别和报告期设定为索引 data_sse.set_index([\"投资者类别\", \"报告期\"], inplace=True) # 强制转换数据类型为浮点数 data_sse = data_sse.astype(float) # 将数据以CSV格式保存到Data文件夹中 # Python默认字符编码为UTF-8格式,若直接保存为CSV,EXCEL打开时中文可能乱码 # 保存时将字符编码修改为GB18030格式 data_sse.to_csv(f\"{PATH_DATA}/bond_trade_sse_{report_date.strftime(\'%Y%m%d\')}.csv\", encoding=\"GB18030\")


历史月报数据抓取

上述代码已经实现了沪深交易所现券交易月报数据的单次抓取,若需要对历史上的月报数据进行抓取,可将上述代码分别封装为工具函数,将月报日期设定为函数参数,再对需要抓取的日期循环调用工具函数,即可完成历史月报数据的批量抓取。需要注意的重点,一方面避免重复抓取,另一方面注意通过sleep函数控制抓取速度。

#  利用pandas生成待抓取日期对应的循环进度条report_dates = pd.date_range(datetime.date(2022, 1, 1), datetime.date(2023, 1, 31), freq=\"M\")
# 利用tqdm生成待抓取日期对应的循环进度条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修改为深交所现券交易数据抓取代码,注意循环中日期参数为loop_date pass # 判断该日期月报是否已抓取,避免重复运行爬虫 if not os.path.exists(f\"{PATH_DATA}/bond_trade_sse_{loop_date.strftime(\'%Y%m%d\')}.csv\"): # 此处将pass修改为上交所现券交易数据抓取代码,注意循环中日期参数为loop_date pass # 利用sleep函数控制抓取速度,单位为秒 sleep(1)


本次案例以抓取沪深交易所现券交易月报为例,主要展示了用Python实现网络数据抓取的简要流程,以及requests、re、datetime、pandas、numpy、tqdm的初步使用。若有错漏之处,还请各位批评指正。


——文 by 十月

本栏目精选

热门推荐

频道导航