C#.NET股票歷史數據採集,【附18年曆史數據和源代碼】

若是用知乎,能夠關注專欄:.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找一個破解版下載玩玩。



若是您以爲閱讀本文對您有幫助,請點一下「推薦」按鈕,您的「推薦」將是我最大的寫做動力!歡迎各位轉載,可是未經做者本人贊成,轉載文章以後必須在文章頁面明顯位置給出做者和原文鏈接,不然保留追究法律責任的權利。

 

出處:http://www.cnblogs.com/asxinyu/p/dotnet_stock_data_design.html

相關文章
相關標籤/搜索