• 156

(不定期更新)使用VBA解決 excel web 查詢無法匯入、匯入太慢的股市資料

(*因網站改版,此範例有部份報表無法正常匯入的問題,請改參考149樓*)

範例網站:台灣股市資訊網http://goodinfo.tw/StockInfo/index.asp
股票代號:中華電信2412 ,取得合併資產負債表內各季的資料




使用xmlhttp get,可以方便取得這個網站第一個網頁的資料
像上面這幾張圖,第一頁是沒問題
如果要其它資料時,就需要先選好種類後網頁才產生新內容
可是這類型的網頁在選擇資料類型更新內容後,有個特色
就是網址不會變
所以也不能用換網址的方式來抓資料,xmlhttp get就失效了

雖然可用簡單的ieobject來模擬點擊網頁抓資料,但缺點就是超慢,一筆2秒~1分鐘
想快就需改用 WinHttp post,一筆可縮短到0.3秒



'===程式碼不多做說明,雖然程式碼同類型網站都通用=========
'===可惜這些代碼、網址,只適用這個範例網站===============
'===只要換網站就要重新看網頁原始碼、追蹤網址、找出正確代碼==

'範例:中華電信2412合併季表2017Q1
'程式碼,請放在“模組”裡面
Sub getpost()

Cells.ClearContents

Dim HTMLsourcecode, Url, Url_a, TempArray()
Set HTMLsourcecode = CreateObject("htmlfile")

Url = "http://goodinfo.tw/StockInfo/StockFinDetail.asp?STEP=DATA&STOCK_ID=2412&RPT_CAT=BS_M_QUAR&QRY_TIME=20171"
Url_a = "http://goodinfo.tw/StockInfo/StockFinDetail.asp?RPT_CAT=BS_M_QUAR&STOCK_ID=2412"

With CreateObject("WinHttp.WinHttpRequest.5.1")
.Open "POST", Url, False
.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
.setRequestHeader "Referer", Url_a
.setRequestHeader "Cache-Control", "no-cache"
.setRequestHeader "Pragma", "no-cache"
.setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
.Send

HTMLsourcecode.body.innerhtml = convertraw(.ResponseBody)

Set Table = HTMLsourcecode.all.tags("table")(1).Rows
ReDim TempArray(Table.Length-1, Table(2).Cells.Length-1)

For i = 0 To Table.Length - 1
For j = 0 To Table(i).Cells.Length - 1
TempArray(i, j) = Table(i).Cells(j).innertext
Next j
Next i

Sheets("sheet1").Range(Cells(1, 1), Cells(Table.Length, Table(2).Cells.Length)) = TempArray()
End With

Set HTMLsourcecode = Nothing
Erase TempArray()

End Sub


Function convertraw(rawdata)

Dim rawstr
Set rawstr = CreateObject("adodb.stream")

With rawstr
.Type = 1
.Mode = 3
.Open
.Write rawdata
.Position = 0
.Type = 2
.Charset = "utf-8"
convertraw = .ReadText
.Close
End With

Set rawstr = Nothing

End Function
'=========================================================
取得別的股票代號修改方式(第一頁請維持get方式抓取,不知道第一頁是什麼,請回頭看61樓)
範例:中鋼2002 合併季表2017Q1
url、url_a,需同時修改
Url = "http://goodinfo.tw/StockInfo/StockFinDetail.asp?STEP=DATA&STOCK_ID=2002&RPT_CAT=BS_M_QUAR&QRY_TIME=20171"

Url_a = "http://goodinfo.tw/StockInfo/StockFinDetail.asp?RPT_CAT=BS_M_QUAR&STOCK_ID=2002"

取得資產負債表內其它年表、季表
url_a :只需要改最後4個字(股票代號)
url :要改2個地方
一、STOCK_ID=(股票代號)
二、年表、季表、年份,請照下面代碼修改,注意大小寫

資產負債表(BS_M_QUAR)

合併季表 2016Q4、Q3
BS_M_QUAR&QRY_TIME=20164
BS_M_QUAR&QRY_TIME=20163

合併年表 2016、2015
BS_M_YEAR&QRY_TIME=2016
BS_M_YEAR&QRY_TIME=2015

非合併季表2017Q1、2016Q4
BS_QUAR&QRY_TIME=20171
BS_QUAR&QRY_TIME=20164

非合併年表2016、2015
BS_YEAR&QRY_TIME=2016
BS_YEAR&QRY_TIME=2015

合併簡式季表2017Q1、2016Q4
BS_EZ_M_QUAR&QRY_TIME=20171
BS_EZ_M_QUAR&QRY_TIME=20164

合併簡式年表2016、2015
BS_EZ_QUAR&QRY_TIME=20171
BS_EZ_QUAR&QRY_TIME=20164

非合併簡式季表2017Q1、2016Q4
BS_EZ_QUAR&QRY_TIME=20171
BS_EZ_QUAR&QRY_TIME=20164

非合併簡式年表2016、2015
BS_EZ_YEAR&QRY_TIME=2016
BS_EZ_YEAR&QRY_TIME=2015

'===============================
其它季表、年表、代碼、網址(注意大小寫)
範例:中華電信2412

損益表(IS_M_QUAR_ACC)
url_a="http://goodinfo.tw/StockInfo/StockFinDetail.asp?RPT_CAT=IS_M_QUAR_ACC&STOCK_ID=2412"
損益表(合併單季季表2016Q4)
url="http://goodinfo.tw/StockInfo/StockFinDetail.asp?STEP=DATA&STOCK_ID=2412&RPT_CAT=IS_M_QUAR&QRY_TIME=20164"

IS_M_QUAR 合併單季季表
IS_M_QUAR_ACC 合併累計季表
IS_M_YEAR 合併年表
IS_QUAR 非合併單季季表
IS_QUAR_ACC 非合併累計季表
IS_YEAR 非合併年表
IS_EZ_M_QUAR 合併簡式單季季表
IS_EZ_M_QUAR_ACC 合併簡式累計季表
IS_EZ_M_YEAR 合併簡式年表
IS_EZ_QUAR 非合併簡式單季季表
IS_EZ_QUAR_ACC 非合併簡式累計季表
IS_EZ_YEAR 非合併簡式年表

'================================
現金流量表(CF_M_QUAR_ACC)
url_a="http://goodinfo.tw/StockInfo/StockFinDetail.asp?RPT_CAT=CF_M_QUAR_ACC&STOCK_ID=2412"
現金流量表(合併單季季表2016Q4)
url="http://goodinfo.tw/StockInfo/StockFinDetail.asp?STEP=DATA&STOCK_ID=2412&RPT_CAT=CF_M_QUAR&QRY_TIME=20164"

CF_M_QUAR 合併單季季表
CF_M_QUAR_ACC 合併累計季表
CF_M_YEAR 合併年表
CF_QUAR 非合併單季季表
CF_QUAR_ACC 非合併累計季表
CF_YEAR 非合併年表

'================================
財務比率表(XX_M_QUAR_ACC)
url_a="http://goodinfo.tw/StockInfo/StockFinDetail.asp?RPT_CAT=XX_M_QUAR_ACC&STOCK_ID=2412"
財務比率表(合併單季季表2017Q1)
url="http://goodinfo.tw/StockInfo/StockFinDetail.asp?STEP=DATA&STOCK_ID=2412&RPT_CAT=XX_M_QUAR&QRY_TIME=20171"

XX_M_QUAR 合併單季季表
XX_M_QUAR_ACC 合併累計季表
XX_M_YEAR 合併年表
XX_QUAR 非合併單季季表
XX_QUAR_ACC 非合併累計季表
XX_YEAR 非合併年表
'================================
基本資料
股利政策
財務評分表

請使用get方式
'================================

其它季別、年份,正確的網址,為節省文章空間,請自行看範例腦補。

程式自動化、美觀,請自行另加程式碼處理

5/27 補充
增加一個變數,可以取得股票名稱、報表種類
至於要加在那裡、怎麼用,自己想辦法了


stockname = Split(HTMLsourcecode.all.tags("table")(0).innertext, " ")(1) & _
Split(HTMLsourcecode.all.tags("table")(0).innertext, " ")(2) & _
Split(HTMLsourcecode.all.tags("table")(0).innertext, " ")(4)



附加壓縮檔: 201705/mobile01-641ffcda49436c367c20846191239c66.zip
謝謝師傅指點迷津,小徒會依您的指點,小心參讀,小徒不知如何感謝,
藉此圖了表心意.(不是睡著了,是五體投體)

報告師傅,剛才依照您的指點,成功下載到"合併年表",
再來就是依照您的指點,逐步的擴大到其他的報表,並
將股票代號以"代數"來代替...
大感謝!
1515151515115151
新東西 來學習
20200410 更新,因網站改版,分新(json)、舊版,新版請參考文末,寫法同274樓

看了麻辣那邊一大堆的股票歷史行情下載範例
全部都是用 web query 跟 CreateObject("InternetExplorer.application")二種方式
有個小缺點,超級慢,10年的資料量,ie反應正常的情況下,大約需要70秒~150秒
常常還會下載不完全,可能選了1年的資料,卻只下載到幾個月,因為ie來不及回應

搜尋一下,發現都沒有xmlhttp方法的下載範例
這就是伸手牌太多的造成的現象,抄來抄去都是那幾種很慢的方法

用xmlhttp方法,同樣下載10年的資料,最少快10倍,可縮短到只需4~15秒
在股市收盤後、晚上離鋒時間,速度更快到0.5~4秒,這可是用10年資料量測試的
假如只要幾個月的資料,基本上是瞬間完成

即然在71樓,寫了一個讓大家抄、copy的xmlhttp範例
再多一篇給大家抄吧

一樣不解釋,因為xmlhttp方式,在不同的網站,程式碼也會不同
加上mobile01 ,很不適合程式教學
一堆文章,都是新註冊來發問的,拿到答案,人就不見了,基本的禮貌都沒有


範例:鉅亨網個股歷史行情下載
(可換至finance.yahoo.com下載,速度比鉅亨快很多,請參考271、272、274樓範例)

下載後,想做“偽”技術分析圖,也很容易了(不過畫圖部份,請自己想辦法)











'====程式碼,請放在“模組”裡============
Sub getpost()

Cells.Clear

Dim HTMLsourcecode As Object, Url As String, Url_a As String, TempArray(), Table, Title, Getxml As Object, ttt As Double
Set HTMLsourcecode = CreateObject("htmlfile")
Set Getxml = CreateObject("msxml2.xmlhttp")

stock = InputBox("股票代號", , 2412)
startday = Format(InputBox("開始日期(8碼數字)", , "20170101"), "####/##/##")
endday = Format(InputBox("結束日期(8碼數字)", , Format(Date, "yyyymmdd")), "####/##/##")

Url = "https://www.cnyes.com/archive/twstock/ps_historyprice/" & stock & ".htm"

Url_a = "pageTypeHidden=1&code=" & stock & _
"&ctl00$ContentPlaceHolder1$startText=" & startday & _
"&ctl00$ContentPlaceHolder1$endText=" & endday & _
"&ctl00$ContentPlaceHolder1$submitBut=查詢"

ttt = Timer
Application.ScreenUpdating = False

With Getxml
.Open "POST", Url, False
.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
.setRequestHeader "Referer", Url
.setRequestHeader "Cache-Control", "no-cache"
.setRequestHeader "Pragma", "no-cache"
.setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
.send (Url_a)

HTMLsourcecode.body.innerhtml = .Responsetext
Title = stock & HTMLsourcecode.getelementbyid("ctl00_ContentPlaceHolder1_titleLab").innertext
Set Table = HTMLsourcecode.all.tags("table")(0).Rows
ReDim TempArray(Table.Length - 1, Table(2).Cells.Length - 1)


For i = 0 To Table.Length - 1
For j = 0 To Table(i).Cells.Length - 1
TempArray(i, j) = Table(i).Cells(j).innertext
If (i > 0 And j = 5) Then
With Sheets("sheet1")
If TempArray(i, j) > 0 Then .Range(.Cells(i + 1, 5), .Cells(i + 1, 7)).Font.Color = -16776961
If TempArray(i, j) < 0 Then .Range(.Cells(i + 1, 5), .Cells(i + 1, 7)).Font.Color = -11489280
End With
End If
Next j
Next i

With Sheets("sheet1")
.Range(.Cells(1, 1), .Cells(Table.Length, Table(2).Cells.Length)) = TempArray()
End With
End With

Application.ScreenUpdating = True
MsgBox Title & vbNewLine & "開始日期" & startday & vbNewLine & "結束日期" & endday & vbNewLine & "資料筆數" & Table.Length - 1 & vbNewLine & "使用時間" & Round(Timer - ttt, 2) & "秒", vbOKOnly, "下載完成"

Set HTMLsourcecode = Nothing
Set Table = Nothing
Set Getxml = Nothing
Erase TempArray()

End Sub

'==================================

我希望看這一篇的人,不要只是抄、copy
抄完之後,有空的話,請從第一篇開始看,多少把說明看一下,了解一下程式碼
如果能理解的話,這篇幾乎所有的範例,都只需要變動少數幾行程式碼,就可以直接套用到別的網站

[點擊下載]





20200410 更新,cnyes新版網頁json格式,下載範例

20210217 更新,因cnyes網頁改版,修正範例




Sub Get_cnyes_Jsondata()

Dim Xmlhttp As Object, Jsondata As Object, Url As String, Url_a As String, stock As String, startday As String, endday As String, ttt As Double, DecodeJson, i As Integer
Set Jsondata = CreateObject("HtmlFile")
Set Xmlhttp = CreateObject("msxml2.xmlhttp")




Sheets("工作表1").Cells.Clear
Sheets("工作表1").Range("a1:h1") = Array("日期", "開盤", "最高", "最低", "收盤", "漲跌", "漲跌(%)", "成交張數")

stock = InputBox("股票代號", , "2330")

startday = Format(InputBox("開始日期(多減一天)(8碼數字)", , Format(Date - 91, "yyyymmdd")), "####/##/##")
endday = Format(InputBox("結束日期(多加一天)(8碼數字)", , Format(Date + 1, "yyyymmdd")), "####/##/##")


If startday > endday Or stock = "" Or startday = "" Or endday = "" Then
MsgBox "資料輸入錯誤", vbOKOnly, "請重新輸入"
Exit Sub
End If

ttt = Timer

Url = "https://ws.api.cnyes.com/ws/api/v1/charting/history?resolution=D&symbol=TWS:" & stock & ":STOCK&from=" & DataToUnixTime(endday) & "&to=" & DataToUnixTime(startday) & ""e=1"
Url_a = "https://invest.cnyes.com/twstock/TWS/" & stock & "/history#fixed"

With Xmlhttp
.Open "GET", Url, False
.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
.setRequestHeader "Referer", Url_a
.send
Set DecodeJson = CallByName(Jsondata.JsonParse(.responsetext), "data", VbGet)
End With

With Sheets("工作表1")
Application.ScreenUpdating = False

For i = 0 To CallByName(CallByName(DecodeJson, "t", VbGet), "length", VbGet) - 1

.Cells(i + 2, 1) = Format(CallByName(CallByName(DecodeJson, "t", VbGet), i, VbGet) / 86400 + #1/1/1970 8:00:00 AM#, "yyyy/mm/dd")
.Cells(i + 2, 2) = CallByName(CallByName(DecodeJson, "o", VbGet), i, VbGet)
.Cells(i + 2, 3) = CallByName(CallByName(DecodeJson, "h", VbGet), i, VbGet)
.Cells(i + 2, 4) = CallByName(CallByName(DecodeJson, "l", VbGet), i, VbGet)
.Cells(i + 2, 5) = CallByName(CallByName(DecodeJson, "c", VbGet), i, VbGet)
If i > 0 Then
.Cells(i + 2 - 1, 6) = .Cells(i + 2 - 1, 5) - .Cells(i + 2, 5)
.Cells(i + 2 - 1, 7) = Round((.Cells(i + 2 - 1, 6) / .Cells(i + 2, 5)) * 100, 2) & "%"

If .Cells(i + 2 - 1, 6) > 0 Then
.Cells(i + 2 - 1, 5).Font.Color = vbRed
.Cells(i + 2 - 1, 6).Font.Color = vbRed
.Cells(i + 2 - 1, 7).Font.Color = vbRed
End If
If .Cells(i + 2 - 1, 6) < 0 Then
.Cells(i + 2 - 1, 5).Font.Color = -11489280
.Cells(i + 2 - 1, 6).Font.Color = -11489280
.Cells(i + 2 - 1, 7).Font.Color = -11489280
End If
End If
.Cells(i + 2, 8) = CallByName(CallByName(DecodeJson, "v", VbGet), i, VbGet)

Next i

'debug
.Cells(1, 10) = stock
.Cells(1, 11) = Format(.Cells(i, 1), "yyyy/mm/dd")
.Cells(1, 12) = Format(.Cells(2, 1), "yyyy/mm/dd")

.Rows(i + 1).Clear
.Cells.EntireColumn.AutoFit
.Cells(1, 1).Select

Application.ScreenUpdating = True

End With

Debug.Print Timer - ttt

Set Xmlhttp = Nothing
Set DecodeJson = Nothing
Set Jsondata = Nothing

End Sub

Function DataToUnixTime(dstring) As Long

DataToUnixTime = (DateValue(dstring) - #1/1/1970 8:00:00 AM#) * 86400

End Function

't 日期
'o 高盤
'h 最高
'l 最低
'c 收盤
'v 成交張數




(20220217 因網頁改版,修正部份程式碼)
[點擊下載]
snare wrote:
看了麻辣那邊一大堆的...(恕刪)




這網站不錯,可以抓歷史行情
不像證交所,還一個月一個月再點的
實在是笑死人

師傅終於把私藏的WINHTTP拿出來了
兄弟們,趕緊先抄再說

師傅這次很花俏唷,還弄了一個MSGBOX..
先謝謝 snare 大大的心得分享^^
在下先收下來了,待後面參考學習
如有問題再來向您請教。
各位大大好,很幸運找到這篇文章
感謝snare大的分享跟教學

一開始也是從麻辣家族那邊開始學 web query 跟 CreateObject 來抓值
對我這種初學者也比較簡單易懂,只是當初抓的 table 遇到了點小問題,還在做後續的處理
例如抓營收的時候,表格很長所以被分段了。像這樣



後來就看到snare大大的教學討論串
正努力學習中,先跑跑看程式碼

但不管是71樓或75樓的程式碼,跑到 Set Table 的時候,就會出現錯誤
請問是為什麼呢?



謝謝大家!!

keeptry wrote:
各位大大好,很幸運...(恕刪)


您好!^^
在下有先試過不管是 71F 或 75F 的範例執行後都沒有錯誤,
而在下是用 Office 2010 版本的.

所以,想先請問您所使用的 Office 版本是 2003 or 2010以上?
keeptry wrote:
但不管是71樓或75樓的程式碼,跑到 Set Table 的時候,就會出現錯誤...(恕刪)

直接用範例中的網頁、網頁內的報表是不會有任何問題的
我都測試過才po出來的
不知道您抓那個網站,如果您是用post方法,除了表格位置要注意之外
snare wrote:
'===可惜這些代碼、網址,只適用這個範例網站===============
'===只要換網站就要重新看網頁原始碼、追蹤網址、找出正確代碼==
...(恕刪)

snare wrote:
因為post 方式,在不同的網站,程式碼也會不同...(恕刪)

如果您是用get方法,那就是那個編號的表格不存在





bioleon69 wrote:
還弄了一個MSGBOX...(恕刪)


為了方便“抄”的人
可以簡單的把,股票名稱、資料筆數、開始日期、結束日期…等資料
改到表格內的其它位置
所以把變數用msgbox 秀出來
  • 156
內文搜尋
X
評分
評分
複製連結
請輸入您要前往的頁數(1 ~ 156)
Mobile01提醒您
您目前瀏覽的是行動版網頁
是否切換到電腦版網頁呢?