若是用知乎,能够关注专栏:.NET开源项目和PowerBI社区node
重点重点:我没有买股票,没有买股票,股市是个坑,当心割韭菜哦。git
本文的初衷是数据分析(分析结果就不说了,就是想看看筛选点数据),只不过搞下来发现比我想象的要简单多了。本文采集的数据是:2000年到2018年2月份,上证和深证交易所全部的上市股票交易数据,按天采集,不是小时哦,有兴趣的朋友,能够稍微改造,作到实时(这和我就无关了)。github
.NET开源文章目录:本博客.NET开源项目文章目录web
本文原文地址:【开源】C#.NET股票历史数据采集,【附18年历史数据和源代码】sql
1.数据采集需求
原始需求:想分析某些股票的历史天交易数据里面知足某些条件的股票。数据库
初步分析:须要股票的基础数据,如名称,编码,交易所等信息,而后就是天天的开票收盘的价格,涨幅等信息。json
觉得很简单,起始搞起来越滚越大,刚开始觉得2个表就够了,没想到搞来搞去,有6个表了。c#
还好,咱们有强大的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找一个破解版下载玩玩。
若是您以为阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写做动力!欢迎各位转载,可是未经做者本人赞成,转载文章以后必须在文章页面明显位置给出做者和原文链接,不然保留追究法律责任的权利。