量化投资从0开始系列 ---- 12. 上期所日统计数据

国内目前5家期货交易所都在官网公开了各自期货品种的历史数据。从技术上,它们各自的实现方式都不相同。在抓取数据的时候,会用到解析json,解析xml,解析html和解析tsv这几种不同的方式。爬取这几家的数据对于学习网络爬虫特别是数据解析方法是一个很好的练习题目。

接下来把5家交易所的日统计数据都抓取一遍,这里先从上海期货交易所日统计数据开始。

在浏览器按F12能看到,上海期货交易所的数据获取接口是:

GET	http://www.shfe.com.cn/data/dailydata/kx/kx20210826.dat

返回的数据格式是json。

具体的实现还是沿用这个系列一直使用的框架,按全量获取和增量获取分别实现,用线程池提高访问速度。本系列之前的文章都有说明,这里就不重复了。

json能取代其它数据格式,当前这么流行是有原因的,它不仅一定程度上满足了可读性,且对程序解析非常友好,基本就是一句话就转成了map数据结构。当然python中是dict。

json_value = json.loads(r.content.decode())

剩下唯一值得一提的问题是,具体测试的时候,发现新旧数据不完全兼容,比较新的数据增加了一个turnover字段(表示成交额),而在老数据中没有这个字段。

为了程序不出错,且能保存到同一张数据库表中,这里用最简单的方式处理一下,如果发现服务器没有返回turnover字段,就补一个None把位置填充上。

                if 'TURNOVER' in data:
                    turnover = data['TURNOVER']
                else:
                    turnover = None

完整的代码如下:

class ShfeDaily(AbstractDataRetriever):
    def __init__(self):
        super().__init__('futures_shfe_daily')

    def _full(self, **kwargs):
        self._get_data_list('20110101', today())

    def _delta(self, **kwargs):
        df_origin = self.query(fields='max(report_date)')
        if df_origin.empty or df_origin.iat[0, 0] is None:
            self._get_data_list('20110101', today())
        else:
            self._get_data_list(df_origin.iat[0, 0], today())

    def _get_data_list(self, start_date, end_date, max_worker=multiprocessing.cpu_count() * 2):
        df_cal_date = StockCalendar().query(
            fields='cal_date',
            where=f'`exchange`=\'shfe\' and is_open=\'1\' and cal_date >\'{start_date}\' and cal_date <= \'{end_date}\'',
            order_by='cal_date')

        with ThreadPoolExecutor(max_worker) as executor:
            future_to_date = \
                {executor.submit(self._get_daily_data, trade_date=row['cal_date']): row
                 for index, row in df_cal_date.iterrows()}
            for future in as_completed(future_to_date):
                row = future_to_date[future]
                try:
                    data = future.result()
                except Exception as ex:
                    self.logger.error(f"failed to retrieve {row['cal_date']}")
                    self.logger.exception(ex)

    def _get_daily_data(self, trade_date):
        shfe_url = f'http://www.shfe.com.cn/data/dailydata/kx/kx{trade_date}.dat'
        headers = {
            'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0',
            'Accept': '*/*',
            'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2'
        }
        r = requests.get(shfe_url, headers=headers)
        json_value = json.loads(r.content.decode())

        df = pd.DataFrame(
            columns=['productid', 'name', 'deliverymonth', 'open', 'high', 'low', 'close', 'settlement', 'zd1_chg',
                     'zd2_chg', 'volume', 'turnover', 'openinterest', 'openinterestchg', 'report_date'])

        for data in json_value['o_curinstrument']:
            if data['OPENPRICE'] != '':
                if 'TURNOVER' in data:
                    turnover = data['TURNOVER']
                else:
                    turnover = None
                df = df.append(
                    {'productid':data['PRODUCTID'].strip(), 'name':data['PRODUCTNAME'].strip(),'deliverymonth':data['DELIVERYMONTH'],
                     'open':data['OPENPRICE'], 'high':data['HIGHESTPRICE'], 'low':data['LOWESTPRICE'],
                     'close':data['CLOSEPRICE'],'settlement':data['SETTLEMENTPRICE'],'zd1_chg':data['ZD1_CHG'],
                     'zd2_chg':data['ZD2_CHG'],'volume':data['VOLUME'],  'turnover':turnover,
                     'openinterest':data['OPENINTEREST'],'openinterestchg':data['OPENINTERESTCHG'],
                     'report_date':trade_date},
                    ignore_index=True)

        self._save(df)


if __name__ == '__main__':
    ShfeDaily().retrieve()

上海国际能源交易中心和上海期货交易所比较,除了网址域名不同,其它从数据格式到命名风格都如出一辙。这两家应该用的完全相同的后台系统。所以爬取数据的处理方式也完全相同,就不重复了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blkq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值