用Python抓取新浪的股票数据

最近做数据分析,先是找到了Tushare这个免费开源的第三方财经包,但后来用了几天之后发现,它的日交易历史数据有时候有不准确的情况,查看源代码发现,这个包的数据源是凤凰财经,而对比凤凰网站其站点的数据本身就是有出入的,所以到也不是Tushare的问题。于是百度了一圈,发现很多网友都是获取新浪的股票数据,包括其历史数据和实时数据。于是乎试了一下,发现速度还挺快,没有具体去测时间但从感官上要比Tushare获取的凤凰数据要快得多。并且数据也很丰富,囊括了每只票自上市以来的所有数据,对此Tushare貌似只有三年数据。当然,新浪数据也有不足的地方,细节上没凤凰数据那么丰富,没有价MA5、MA10以及量MA5、MA10等等,最重要的还是缺少每天的交易额。所幸我目前计算所需的数据里还不包括每天交易额。

新浪财经的数据接口地址是:http://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/var=/CN_MarketData.getKLineData?symbol=sz000001&scale=240&ma=no&datalen=60。在地址里symbol指的是股票代码,这里需要注意的是不能只填数字代码,还需要把交易市场的前缀加上去,比如sz000001指的是平安银行,而sh000001则是上证指数;scale表示的是时间长度,以分钟为基本单位,输入240就表示下载日K线数据,60就是小时K线数据,貌似最短时间是5分钟,并没有提供分钟数据;datalen则是获取数据的条数,在日K线的时间长度了,datalen就是获取60天日K数据,当然也可以获取60小时K数据。

人生苦短,我用Python,所以代码就用它了,其实以前一直是用世界上最好的语言PHP 的,这是为了做数据分析才开始学着用Python,代码粗糙了些,返回的是个列表,每笔数据则是字典,将就着看吧。

import urllib.request
links = 'http://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/var=/CN_MarketData.getKLineData?symbol=' + code + '&scale=' + str(scale) + '&ma=no&datalen='+str(datalen)
histData = urllib.request.urlopen(links).read()
histData = str(histData).split('[')[1]
histData = histData[1:len(histData) - 4].split('},{')
datas = []
for i in range(0, len(histData)):
    column = {}
    dayData = histData[i].split(',')
    for j in range(0, len(dayData)):
       field = dayData[j].split(':"')
       if field[0] == 'day':
          column['date'] = field[1].replace('"', '')
       else:
          column[field[0]] = field[1].replace('"', '')
    datas.append(column)
return datas

PS:这里要说明的是新浪默认字段里,日期字段名称是day,个人觉得很是膈应,如果是小时数据或者5分钟数据还叫day岂不难受?所以改成了 date,其实没什么大的意义,个人喜好而已。

实时数据获取方式和历史数据差别不大,需要的也是完整代码,地址是:http://hq.sinajs.cn/list=sz000001,不同的是实时数据可以多支同时获取的,代码之间用逗号隔开就可以了,经过实验,貌似最多一次可以获取100只票的实时数据。

links = 'http://hq.sinajs.cn/list=' + codes
realTimeData = urllib.request.urlopen(links).read()
realTimeData = realTimeData.decode('gbk').replace('"','').split('\n')
data = {}
for i in range(len(realTimeData)-1):
    if len(realTimeData[i]) > 0:
       data[realTimeData[i].split('=')[0].split('_')[2][2:]] = realTimeData[i].split('=')[1].split(',')[:-1]
return data

PS:大家获取可别太多线程,我试过,会被封。

用Python抓取大智慧除权数据

继续做的数据分析,由于新浪获取的是未复权数据,所以在分析的时候出了些小问题,结果变得扑所迷离。于是又用了几天Tushare的获取复权数据功能,本来是写了个循环,每天自动获取,可是几乎每次下载都卡死了,这真是坑爹的网络。翻了翻Tushare的这段代码,Tushare的这个数据倒也是新浪的,但是是从网页上扒下来的,过程好像获取了两个地址的数据,貌似一个复权因子一个后复权数据,没细看了。反正我也不认为这是合理的获取方式,理想的当然是JSON或者XML之类的格式最好了。百度了一圈,没有发现哪里有免费的前复权数据,更找不到除权数据,心想找到除权数据也好啊,自己来复权。这过程中,得到了几个新浪地址,好像和复权有关,但是也没琢磨出怎么用。在这里先把地址贴下来,有时间再研究。

http://vip.stock.finance.sina.com.cn/api/json.php/BasicStockSrv.getStockFuQuanData?symbol=sz000001&type=hfq,和惯例一样,symbol指得是股票代码,而type按拼音来说应该是后复权,但我输入qfq之后得到并非前复权。

http://biz.finance.sina.com.cn/stock/flash_hq/kline_data.php?symbol=sz000001&begin_date=20100101&end_date=20161206,同上,只是有开始和结束时间。

新浪没办法了,看到很多网友都是从大智慧获取,毕竟资料还多,于是下载安装了个大智慧365。装好后在安装目录下有个Download文件夹,而除权数据就在PWR文件夹里,FIN文件夹里放的是财务数据,而ABK里则是大智慧的板块数据。

这里之所以写个日志是因为,365里的数据结构并非网络上写的四字节方式,四字节转赠股,四字节红利,四字节的配股,四字节配股价。我所看到的是120字节分段的形式,任一只股票先120字节的4-12字节为代码,接来下的120字节里前四个字节为除权日期,接着4-20字节为空,剩下的就是除权数据了,而这个数据并非分段的数值形式,而是一句话,比如10股送5股之类的,就是说得到这句话后还要进行分割。一只票除权了多少次就有多少个这样的120字节了,整理之后把代码贴上:

path = '/home/jeff/Share/DZH/Download/PWR/' 
name = ['full_sh.PWR','full_sz.PWR']
data = []
for i in name:
    exFile = open(path+i,'rb')
    exFile.seek(8)
    while True:
        exContent = exFile.read(120)
        if not exContent:
            break
        if exContent[:4] == b'\xff\xff\xff\xff':
            code = exContent[4:12].decode('gbk')
        elif len(exContent) > 0:
            date= struct.unpack("I", exContent[:4])[0]
            date= time.localtime(date)
            exlist = exContent[20:].decode('gbk').split('\x00')[0][2:].split()
            present = 0
            bonus = 0
            rationed = 0
            price = 0
            for i in exlist:
                if '送' in i:
                    present += float(re.findall(r"\d+\.?\d*",i)[0])
                if '增' in i:
                    present += float(re.findall(r"\d+\.?\d*",i)[0])
                if '派' in i:
                    bonus += float(re.findall(r"\d+\.?\d*",i)[0])
                if '价' in i:
                    price += float(re.findall(r"\d+\.?\d*",i)[0])
                if '配' in i:
                    rationed += float(re.findall(r"\d+\.?\d*",i)[0]) - price
            data.append({'code':code[2:],'date':date,'present':present,'bonus':bonus,'price':price,'rationed':rationed})
return data