C#.NET股票历史数据采集,【附18年历史数据和源代码】
如果用知乎,可以关注专栏:.NET开源项目和PowerBI社区
重点重点:我没有买股票,没有买股票,股市是个坑,小心割韭菜哦。
本文的初衷是数据分析(分析结果就不说了,就是想看看筛选点数据),只不过搞下来发现比我想象的要简单多了。本文采集的数据是:2000年到2018年2月份,上证和深证交易所所有的上市股票交易数据,按天采集,不是小时哦,有兴趣的朋友,可以稍微改造,做到实时(这和我就无关了)。
.NET开源文章目录:本博客.NET开源项目文章目录
本文原文地址:【开源】C#.NET股票历史数据采集,【附18年历史数据和源代码】
1.数据采集需求
原始需求:想分析某些股票的历史天交易数据里面满足某些条件的股票。
初步分析:需要股票的基础数据,如名称,编码,交易所等信息,然后就是每天的开票收盘的价格,涨幅等信息。
以为很简单,起始搞起来越滚越大,刚开始以为2个表就够了,没想到搞来搞去,有6个表了。
还好,我们有强大的XCode组件,数据库设计,开发都极其简单,总共零零散散也就10个小时不到就完工了。主要的时间不是写代码,其实60%的时间都是在找资料,分析接口和想怎么设计上面,以及跑数据,还好源数据都保存下来了,重写跑起来很快。
2.股市数据接口
很早以前,有朋友也想让我给给他采集股票实时数据,而且用的是商业接口,由于时间匆忙,而且对股票一无所知,所以就拒绝了。
这次开工之前,心里也很忐忑,会不会很复杂,反正是自己想玩和想看,所以抱着试一试的心态,没想到比我想的要简单很多。
首先,我们得找到数据来源,否则一切无从谈起,而且我需要的是历史数据,对数据实时性要求不高:
此处。。。。。。。。。。。。省略1万字,因为搜索和找了很多资源,最终用的是下面的接口,简单,实测速度快,18年的数据不到20分钟刷刷刷搞下来了。
2.1 股票基础数据
股票基础数据我用的是这个网址:http://quote.eastmoney.com/stocklist.html
里面包括了上证和深圳交易所的所有股票代码信息,只需要直接采集即可,速度很快。
如果要做实时,每天更新一次即可,注意:我开始也没注意,股票代码有很多含义,除了交易所之外,还有啥创业板,基金之类的,我没仔细研究,我只把我需要的类型进行了标记。可以在这里看看代码的一些类型:https://baike.so.com/doc/4974613-5197406.html
股票基础数据结构比较简单:
编码(唯一),名称,交易所,类型1是要分析 ,0是暂时忽略的,上市日期
2.2 股票历史天数据
股票历史天数据刚开始想应该很大,找了一些结构才发现,基本每天的主要指标也就10个字段左右,计算一下,每只股票就算20年,也就6000条而已。
即使10000支股票,最多也就6000万而已,所以刚开始的时候直接全部撸到一个表里面了,实际上后面在分析的时候,极其不合理。分析的比较很复杂,搜索非常慢,所以后来我把历史数据进行了拆分,然后分析的时候多线程,速度瞬间提升10倍。由于XCode组件天生对分表分库和数据库反向工程的支持,所以开发起来非常快。
股票历史数据找了很多,最早用的是搜狐的一个接口:http://www.cnblogs.com/ldlchina/p/5392670.html
它的格式很简单,拼接股票代码和起始结束日期即可,后来还发现它还能查询指数信息:
http://q.stock.sohu.com/hisHq?code={code}&start={start}&end={end},{code}替换为股票代码,大陆股票代码加前缀cn_。
返回的json格式很标准,使用了Newlife组件的JsonParser类,轻松搞定。根据返回的数据信息,找了几支股票核对一下,就知道其意义了。在后面的数据库设计中会详细描述。说明:采集的时候我是先用临时表统一把返回的结果保存,防止程序有bug,下次又去请求,浪费人家的流量。也是保存在sqliet数据库。
2.3 其他附加
在后面分析的时候,我还用到了板块信息,相当于给每支股票加一个类型,属于什么板块,这样分析的时候有针对性。板块有类型,然后每个类型下面又有一些股票代码,有2个表,数据来源也是搜狐的:
http://q.stock.sohu.com/cn/bk.shtml
当然如果还要做复杂和完善一点,还有很多数据要采集,比如公司的一些基本信息,我暂时没有用到,后面我会把代码开源,大家随意折腾。
3.数据库设计
数据库设计我们采用XCode开发的设计规范,都用xml文件,可以自动生成实体类,后面有时间我会针对XCode写一篇开发实践的文章,再一次带大家温习了XCode,在这里感谢@大石头,10多年码农,X组件博大精深,极大的提高了开发效率,简单,简单,简单到你有时候怀疑人生。
1.股票基础信息
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<Table Name="StockBaseInfo" Description="股票基础信息" ConnName="stock_base"> <Columns> <Column Name="Code" DataType="String" PrimaryKey="True" Description="股票编码" /> <Column Name="Name" DataType="String" Master="True" Description="名称" /> <Column Name="Exchange" DataType="String" Description="交易所" /> <Column Name="Kind" DataType="Int32" Description="类型。1是要分析股票,0是暂时不分析" /> <Column Name="StartDate" DataType="DateTime" Description="上市日期" /> <Column Name="CreateDate" DataType="DateTime" Description="创建时间" /> </Columns> <Indexes> <Index Columns="Name" /> <Index Columns="StartDate" /> </Indexes></Table> |
2.股票历史日数据
其实在项目代码的xml文件表结构中,还有一个历史信息,就是一次性获取所有历史Josn文本存储,避免重复抓取Josn数据。结构很简单,就不贴了。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<Table Name="StockDayData" Description="股票日数据" ConnName="stock_day"> <Columns> <Column Name="ID" DataType="String" PrimaryKey="True" Description="编号。code+日期" /> <Column Name="Code" DataType="String" Description="股票编码" /> <Column Name="StatDate" DataType="DateTime" Description="数据日期" /> <Column Name="StartPrice" DataType="Double" Description="开盘价格" /> <Column Name="EndPrice" DataType="Double" Description="收盘价格" /> <Column Name="ChangePrice" DataType="Double" Description="涨跌金额" /> <Column Name="ChangeRatio" DataType="Double" Description="涨跌幅度" /> <Column Name="LowPrice" DataType="Double" Description="最低价格" /> <Column Name="HighPrice" DataType="Double" Description="最高价格" /> <Column Name="TotalHand" DataType="Int32" Description="总手" /> <Column Name="TotalAmount" DataType="Double" Description="总金额(万)" /> <Column Name="HandRate" DataType="Double" Description="换手率" /> <Column Name="UpdateDate" DataType="DateTime" Description="更新日期" /> </Columns></Table> |
3.板块分类和股票板块信息
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<Table Name="GroupKind" Description="板块分类" ConnName="stock_base"> <Columns> <Column Name="ID" DataType="String" PrimaryKey="True" Description="编码。url相关" /> <Column Name="Name" DataType="String" Master="True" Description="板块名称" /> <Column Name="Kind" DataType="String" Description="分类。1.行业,2地域,3.概念" /> <Column Name="Total" DataType="Int32" Description="总数" /> <Column Name="CreateDate" DataType="DateTime" Description="创建时间" /> </Columns> <Indexes> <Index Columns="Kind" /> <Index Columns="Name" /> </Indexes> </Table> <Table Name="StockGroup" Description="股票板块信息" ConnName="stock_base"> <Columns> <Column Name="ID" DataType="String" PrimaryKey="True" Description="编号。groupid+stockid" /> <Column Name="GroupID" DataType="String" Description="板块ID" /> <Column Name="Kind" DataType="String" Description="分类。1.行业,2地域,3.概念" /> <Column Name="Code" DataType="String" Description="股票代码" /> <Column Name="StockName" DataType="String" Description="股票名称" /> <Column Name="CreateDate" DataType="DateTime" Description="创建时间" /> </Columns> <Indexes> <Index Columns="GroupID" /> <Index Columns="Code" /> </Indexes></Table> |
4.关键信息采集
下面我们把数据采集过程简单分析一下,然后把源代码和数据库共享给大家。套路很简单,熟悉起来很快就可以搞定。
我的博客中,前几年,写过好几篇关于C#数据采集的方法,套路都比较通用,主要是:HtmlAgilityPack + XCode
C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子)
【开源】分享2011-2015年全国城市历史天气数据库【Sqlite+C#访问程序】
HtmlAgilityPack是.NET 下的一个强大的HTML 解析类库,支持用 XPath 。配合上自带的HAPExplorer,很快就可以解决问题。
具体使用可以参考上面2篇文章,下面我们也会上实际代码。
4.1 基础数据采集
先打开http://quote.eastmoney.com/stocklist.html右键,源代码,获取完整的HTML代码,用HAPExplorer工具找到上海和深圳列表的位置,如下图:

我们发现上海和深圳交易所的列表分别在下面位置:
|
1
2
|
/html[1]/body[1]/div[9]/div[2]/div[1]/ul[1]/html[1]/body[1]/div[9]/div[2]/div[1]/ul[2] |
还往下一个层级就是li标签列表,在HtmlAgilityPack中有现成的方法获取整个列表,并进行解析,如下面代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
/// 获取所有股票代码和名称基础信息public static void ReadAllStockBaseInfo(){ //上海:/html[1]/body[1]/div[9]/div[2]/div[1]/ul[1] //下级是li列表 ,Text值就是股票名称和代码 XXX() //深圳:上海:/html[1]/body[1]/div[9]/div[2]/div[1]/ul[2] string url = @"http://quote.eastmoney.com/stocklist.html"; HtmlWeb htmlweb = new HtmlWeb(); htmlweb.OverrideEncoding = Encoding.GetEncoding(936); HtmlDocument doc = htmlweb.Load(url); Dictionary<string, string> dic = new Dictionary<string, string>() { {"上海",@"/html[1]/body[1]/div[9]/div[2]/div[1]/ul[1]" }, {"深圳",@"/html[1]/body[1]/div[9]/div[2]/div[1]/ul[2]" } }; #region 获取 Dictionary<String, StockBaseInfo> list = new Dictionary<string, StockBaseInfo>(); foreach (var item in dic) { //获取所有子节点 var res = doc.DocumentNode.SelectSingleNode(item.Value).SelectNodes(@"li"); if (res.Count > 0) { foreach (var node in res) { //获取名称和代码 var name = node.InnerText.Trim(); if (name.IsNullOrEmpty()) continue; var str = name.Split('(', ')'); if (str.Length < 2) continue; StockBaseInfo et = new StockBaseInfo() { Code = str[1], Name = str[0], Exchange = item.Key, StartDate = new DateTime(2000, 1, 1), CreateDate = DateTime.Now }; if(!list.ContainsKey(et.Code)) { list.Add(et.Code,et); } } } } list.ToValueArray().Insert(true); #endregion} |
获取到子节点后,解析名称,然后用批量Insert到数据库。
用XCode默认都是使用Sqlite数据库,轻量级,非常方便,数据库表结构都是自动新建。
4.2 股票历史数据
我们使用2.2节中提到的接口,如下,配合前面采集到的所有股票基础数据,就可以便利进行历史数据抓取了,说一下,这个接口很给力,速度相当快。我是从2000年1月1日开始采集的,截止时间是2018年2月10日(放假闲的你懂的)。历史json数据按股票ID单独保存,后面写了一个转换程序单独从历史数据库转换即可。
http://q.stock.sohu.com/hisHq?code={code}&start={start}&end={end},{code}替换为股票代码,大陆股票代码加前缀cn_。
这里对老司机来说,其实没多少难度,就是拼接URL,请求获取json数据,然后解析json格式,我解析用了Newlife的JsonParser,用起来很简单有空我单独讲一下,就是把Json用字典和List<Object>保存下来,知道结构后,直接强制转换和取值即可。上代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public static void GetHistoryFromWeb(string stockCode, DateTime start, DateTime end,string type="cn"){ string url = @"http://q.stock.sohu.com/hisHq?code={3}_{0}&start={1}&end={2}".F(stockCode, start.ToString("yyyyMMdd"), end.ToString("yyyyMMdd"),type); WebClientX client = new WebClientX(); client.Timeout = 1000 * 120; var text = client.GetHtml(url); var doc = new HtmlDocument(); doc.LoadHtml(text); var value = doc.DocumentNode.InnerText; var et = new StockHisText() { Code = stockCode, Start = start, End = end, HisText = value }; try { if (type == "zs") et.Code = "{0}_{1}".F("Index",et.Code);//加前缀区分 et.Insert(); } catch(Exception err) { XTrace.WriteException(err); }} |
上面是获取单个股票其指定日期范围内的历史数据,直接到历史表,下面是解析部分,外面套的循环就不贴代码了,可以下载源代码看。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
public static void PraseHistoryData(){ var all = FindAll(); int index = 1; Parallel.For(0, all.Count, new ParallelOptions() { MaxDegreeOfParallelism = 1 }, i => { XTrace.WriteLine("进度:{0}/{1}",index ++,all.Count); #region 单个文本解析 JsonParser jp = new JsonParser(all[i].HisText); var decode = (List<object>)jp.Decode(); if (decode.Count < 1) return; var main = (Dictionary<string, object>)decode[0];//字典 if (main.ContainsKey("hq")) { var obj = (List<object>)main["hq"]; if (obj.Count > 0) { List<StockDayData> res = new List<StockDayData>(); foreach (var item in obj) { #region 单条记录解析 //item是一个10个元素的数组 //日期,今开价格,今天收盘价格,涨跌金额,涨跌幅度,最低价格,最高价格,总手,总金额(万),换手率 //"2018-02-09", "31.46", "31.46", "2.86", "10.00%", "31.46", "31.46", "303", "95.32", "0.15%" var list = (List<object>)item; StockDayData sd = new StockDayData() { Code = all[i].Code, StatDate = list[0].ToDateTime(), StartPrice = list[1].ToDouble(), EndPrice = list[2].ToDouble(), ChangePrice = list[3].ToDouble(), ChangeRatio = ((string)list[4]).Replace("%", "").ToDouble(), LowPrice = list[5].ToDouble(), HighPrice = list[6].ToDouble(), TotalHand = list[7].ToInt(), TotalAmount = list[8].ToDouble(), HandRate = ((string)list[9]).Replace("%", "").ToDouble(), UpdateDate = DateTime.Now }; sd.ID = "{0}_{1}".F(sd.Code, sd.StatDate.ToString("yyyyMMdd")); #endregion res.Add(sd); } res.Save(true); } } #endregion });} |
我的代码里面有直接把历史数据解析分库存储的,方法在 股票历史文本数据.Biz.cs 文件的,PraseHistoryDataV2方法中。分库方法非常简单,保存之前修改一下链接即可。
5.源代码和数据库
代码很简单丑陋,不要吐槽,代码如下:https://github.com/asxinyu/Stock (兄台,搞点数据不容易,点个赞或者给个Star一下吧)
基础数据Sqlite数据库:https://pan.baidu.com/s/1qZJIy8s,密码:61e3
2000年到2018年历史Json源数据:https://pan.baidu.com/s/1jIY70bG,密码:cmpw
2000年到2018年日历史数据Sqlite文件:https://pan.baidu.com/s/1eTxcjdC 密码:ujbn
Sqlite很好玩,强烈推荐工具,navicat,可以去CSDN找一个破解版下载玩玩。
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
出处:http://www.cnblogs.com/asxinyu/p/dotnet_stock_data_design.html
C#.NET股票历史数据采集,【附18年历史数据和源代码】的更多相关文章
- 【开源】C#.NET股票历史数据采集,【附18年历史数据和源代码】
如果用知乎,可以关注专栏:.NET开源项目和PowerBI社区 重点重点:我没有买股票,没有买股票,股市是个坑,小心割韭菜哦. 本文的初衷是数据分析(分析结果就不说了,就是想看看筛选点数据),只不过搞 ...
- Python获取股票历史、实时数据与更新到数据库
要做量化投资,数据是基础,正所谓"巧妇难为无米之炊" 在免费数据方面,各大网站的财经板块其实已提供相应的api,如新浪.雅虎.搜狐...可以通过urlopen相应格式的网址获取数据 ...
- SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码)
SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码) 摘自: https://blog.csdn.net/sjin_1314/article/det ...
- Ajax (Asynchronous javascript xml) 搜索框核心代码(JQuery) Ajax判断用户名存在核心代码 附:原生js的Ajax代码 其中有json的一句话解释
前端 <script type="text/javascript"> $(function(){ $("#tid").keyup(function( ...
- 利用Python+pyecharts+tushare图形化展示股票历史财务信息
在微信或其他平台上,经常能看到别人推荐股票,分析的头头是道,让自己懊恼于没有早点关注到这只股票,好像错失了几个亿.但是投资股票又忌讳听消息跟风,总不能看到别人推荐自己就无脑买入. 看到了一只股票,自己 ...
- 使用python和tushare查询股票历史名称变更记录
接口:namechange 描述:历史名称变更记录 注:tushare库下载和初始化教程,请查阅我之前的文章 输入参数 名称 | 类型 | 必选 ...
- 盘它!!一步到位,Tensorflow 2的实战 !!LSTM下的股票预测(附详尽代码及数据集)
关键词:tensorflow2.LSTM.时间序列.股票预测 Tensorflow 2.0发布已经有一段时间了,各种新API的确简单易用,除了官方文档以外能够找到的学习资料也很多,但是大都没有给出实战 ...
- java抓取东方财富股票数据(附源码)
背景 前段时间给朋友写了一个自动抓取同花顺股票数据的程序,不少人觉得不错. 这几天后台有粉丝给我留言让我也抓一下东方财富的数据,说东方财富的数据特别难抓,我还真不一定能搞得定. 本来我是一个德艺双磬且 ...
- Java爬取同花顺股票数据(附源码)
最近有小伙伴问我能不能抓取同花顺的数据,最近股票行情还不错,想把数据抓下来自己分析分析.我大A股,大家都知道的,一个概念火了,相应的股票就都大涨. 如果能及时获取股票涨跌信息,那就能在刚开始火起来的时 ...
随机推荐
- Flask权限管理
权限管理功能的实现可以分为以下几个小块: 1,新建数据库表Role,里面包括id(Integer,主键)name(String),permission(Integer),default(boolean ...
- 探索C++虚函数在g++中的实现
本文是我在追查一个诡异core问题的过程中收获的一点心得,把公司项目相关的背景和特定条件去掉后,仅取其中通用的C++虚函数实现部分知识记录于此. 在开始之前,原谅我先借用一张图黑一下C++: “无敌” ...
- Ecplise项目转移到Android Studio,以及Genymotion模拟器介绍
一.移植android项目 今天简单分享一个从ecplise开发项目转移到Android Studio的方法,之前一直在ecplise上开发android项目,但是因为google现在主打Androi ...
- 【eclipse】Server Tomcat v9.0 Server at localhost failed to start.
Server Tomcat v9.0 Server at localhost failed to start. 的一个原因就是启动超时了.
- Python学习札记(三十二) 面向对象编程 Object Oriented Program 3
参考:访问限制 NOTE 1.eg. #!/usr/bin/env python3 class Student(object): """docstring for Stu ...
- 【转载】解决telnet无法连接 Connection refused
原文:解决telnet无法连接 Connection refused telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式.它为用户提供了在本地计算机上完成 ...
- 【Python】实现对大文件的增量读取
背景 前段时间在做一个算法测试,需要对源于日志的数据进行分析才能获取到结果:日志文件较大,所以想要获取数据的变化曲线,增量读取是最好的方式. 网上有很多人的技术博客都是写的用for循环readline ...
- [java]java String.split()函数的用法分析
转自:http://swiftlet.net/archives/709 一.在java.lang包中有String.split()方法的原型是: public String[] split(Strin ...
- [转载]CentOS 6.3安装Subversion服务器
转载自http://www.cnblogs.com/zhoulf/archive/2013/02/02/2889949.html 安装说明 系统环境:CentOS-6.3 安装方式:yum insta ...
- 050——VUE中使用js库控制vue过渡动作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...