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

peter624 wrote:
其中json網址部分(圖片顯示'月'、'季'、'年')是如何取得的


一、chrome
二、https://tw.stock.yahoo.com/quote/2412/revenue
三、F12
四、月、季、年,各點一次
五、xhr



snare您好:
敝人又來打擾您了,研究您上次的程式後,試圖將下圖圈起來的部份匯入

程式可以把0到4的資料匯入,但下方的currencyCode及fiscalYear不行匯入,以下是修改您的程式而來。
Sub Get_barrons_Json()

Dim URL As String, GetXml As Object, DecodeJson
Set GetXml = CreateObject("msxml2.xmlhttp")
Set Jsondata = CreateObject("HtmlFile")

URL = "https://www.barrons.com/market-data/api/proxy?https://quote-barrons.millstone.mktw.dowjones.io/api/quote/financials?chartingSymbol=stock///ko&urlFragment=cash-flow/annual"
With GetXml

.Open "GET", URL, False
.send

'第0層

'第1層
Set DecodeJson = CallByName(jsonDecode(.responsetext), "blocks", VbGet)

Dim json, json1, i As Integer, j As Integer
On Error Resume Next

'第2層(0~6跳過,請自行補上)
Set json = CallByName(CallByName(DecodeJson, "7", VbGet), "sections", VbGet)

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

'第3層
Set json1 = CallByName(CallByName(json, i, VbGet), "columns", VbGet)
For j = 0 To CallByName(json1, "length", VbGet) - 1
Debug.Print CallByName(json1, j, VbGet)
Next j
Next i

End With


Set GetXml = Nothing
Set DecodeJson = Nothing
Set json = Nothing
Set json1 = Nothing

End Sub
Function jsonDecode(JsonString As Variant)
Set sc = CreateObject("ScriptControl")
sc.Language = "JScript"
Set jsonDecode = sc.Eval("(" + JsonString + ")")
End Function

不知要如何才能把"columns"以下的0~4、currencyCode及fiscalYear的資料匯入呢?依您先前的程式碼,還要修改什麼部份才能匯入呢?
snare您是否可以再次指點迷津,感謝您撥空回應。
alfidpan
試圖加入此行Debug.Print CallByName(json1, "currencyCode", VbGet),但沒有效果。
alfidpan wrote:
不知要如何才能把"columns"以下的0~4、currencyCode及fiscalYear的資料匯入呢?依您先前的程式碼,還要修改什麼部份才能匯入呢?


alfidpan wrote:
試圖加入此行Debug.Print CallByName(json1, "currencyCode", VbGet),但沒有效果


(以您的程式碼做說明)
Set json = CallByName(CallByName(DecodeJson, "7", VbGet), "sections", VbGet)
假設 json 算是第一層
那裡面的0、1、2,就是第二層

0裡面的 columns、currencyCode、fiscalYear、items、sectionHeader
雖然分成2個物件、3個字串,但同樣都是第三層
(1、2方法同0)

columns裡面的0、1、2、3、4,共5個字串,就是第四層
第四層裡面沒有currencyCode、fiscalYear,只有01234

Set json1 = CallByName(CallByName(json, i, VbGet), "columns", VbGet)
2個callbyname(callbyname(... ... ,跳過第3層,直達第4層
所以Debug.Print CallByName(json1, "currencyCode", VbGet)當然找不到東西


資料是在第三層
(點我看大圖)

snare
進入visual Basic視窗=>檢視=>區域變數視窗,按f8逐行執行(或設中斷點)
alfidpan
snare您好 非常感謝 ,因為您的熱心相助,讓敝人三腳貓的功夫可以把該網站的資料匯入,太感謝您了。
snare您好:
想要再請教一個問題,一般我們都是匯入網頁中內的資料,那<head>內的資料是否也可以利用此篇文章所教的方法,匯入想要的資料呢?例如下圖

網址為:https://www.barrons.com/market-data/stocks/ko

如果想要匯入<meta name="robots" >中content的內容
是將HTMLsourcecode.body.innerhtml = .responsetext中body改成head就可以了嗎?

因為改過也不能匯入想要的資料,不管是用innerhtml或是outerhtml或是innertext或是用outertext都沒有成功過。利用.getElementsByTagName找meta也是一無所獲。
snare您是否可以再次指點迷津,感謝您撥空回應。
snare wrote:
中油公司車用汽、柴油...(恕刪)



請問一下snare大大, 這段程式碼貼在Google Apps Script會有錯誤, 這要怎麼解決呢?

snare
或是您給我其它資料來源網址,我試試
zxc_tw
snare 我剛剛測試了, 可以下載. 我的系統是Windows
alfidpan wrote:
想要再請教一個問題,一般我們都是匯入網頁中內的資料,那<head>內的資料是否也可以利用此篇文章所教的方法,匯入想要的資料呢?例如下圖

網址為:https://www.barrons.com/market-data/stocks/ko

如果想要匯入<meta name="robots" >中content的內容



那個是網頁始碼的一部份,就像我們寫vba
一開始就先打個 sub 一樣

沒那麼複雜,直接對.responsetext處理就行

snare
(0) split陣列裡第一個,(1)第2個,"/"、"robots" 分割字串用的關鍵字,更詳細請google split vba
alfidpan
snare您好 感謝指點,十分感謝。
alfidpan wrote:
snare您好:敝人...(恕刪)


謝謝snare哥
因為太無聊去google各大論譠的的json範例
發現大部份都還是用ScriptControl那一套在解折json
這很好用,缺點是不能在excel 64位元使用,沒看到簡單一些的方法
只有少數範例是用mshta or powershell …等等高階寫法來繞過64位元限制使用
但改寫程式碼上需注意不少地方、程式碼也較長,且幾乎都有一個相同的問題
那就是程式結束後,沒把mshta.exe... 等等釋放,會留在工作管理員中佔記憶體
建議考慮到未來是64位元的天下,早點放棄ScriptControl比較好



ScriptControl寫法主要有2個function,getkeys、getproperty

詳細程式碼請google,基本上都一模一樣
google ScriptEngine.Run getKeys
google ScriptEngine.Run getProperty


這樓範例是把ScriptControl的程式碼,改寫成CreateObject("HtmlFile")
如此一來程式碼就不需要另外弄個什麼CreateObjectx86("ScriptControl") + mshta.exe
還可以同時在excel x32 x64 使用,方法簡單,也不會出現找不到ScriptControl物件的問題
程式碼架構也幾乎不用變動,就算初學者也有能力修改別人寫的範例
非常接近無痛轉移了


(ScriptControl簡易改寫方式只在mobile01發文,轉貼請註明來源)
'這裡用1122樓+1123樓nickchu 提供的範例,示範如何改寫scriptcontrol => htmlfile

'1123樓有3個function => getKeys、GetObjectProperty、getProperty
'為了方便修改程式碼,在htmlfile裡面,使用的是相同函數名稱、參數

'(getKeys)(GetObjectProperty、getProperty合併成getProperty)
'scriptcontrol getKeys、GetObjectProperty、getProperty
'htmlfile Jsondata.GetKeys、Jsondata.GetProperty
'函數說明,請參考1123樓nickchu的範例

'scriptcontrol物件 轉用 htmlfile物件,函數改寫方式
'一、scriptcontrol GetObjectProperty、getProperty => Jsondata.GetProperty
'改寫方式如下:
'CallByName(json物件, "名稱", VbGet)
'GetProperty(json物件, "名稱")
'GetObjectProperty(json物件, "名稱")
'可改用
'Jsondata.GetProperty(json物件,"名稱"),或混用callbyname



'二、scriptcontrol getKeys => Jsondata.GetKeys
'改寫方式如下:(需多一個暫存用的變數)
'set 暫存變數=Jsondata.GetKeys(json物件)
'key值=Split(暫存變數, ",")

'(這個偷懶的方式不建議使用)
'如果100%確定該json物件裡面,型態都是variant/string,'不用暫存,少個set也行
'key值=split(Jsondata.GetKeys(json物件),",")


'三、Jsondata.GetProperty、Jsondata.GetKeys,也可以做成function獨立出來
'這樣主程式連函數名稱都不用改,100%無痛轉移
'(範例主要是說明改寫方式,所以沒有特別做成function,有能力的可以試著改寫function看看)


'===================================================================
'改寫的地方沒特別標示,請自行和1122樓、1123樓,原始程式碼比較差異

Sub ScriptControlfunction_32bit_convert_to_excel_64bit()

Dim Xmlhttp As Object, Jsondata As Object, Url As String, Url_a As String, token As String, DecodeJson, ttt As Double, Stock As String
Dim temp, acts, periods, reports, reportdate As String, i As Integer, j As Integer, lastrow As Double

Set Xmlhttp = CreateObject("msxml2.xmlhttp")


(因語法關係,改用圖片,請手動輸入,或到文末下載txt檔,點我看大圖 )



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


ttt = Timer

'Stock = "DIS" 'test
Stock = "AAPL" 'test


Url = "https://app.quotemedia.com/auth/g/authenticate/dataTool/v0/91386/d54dae6c8cf2bda0196be3a59647fcc4ee56671e9187d4388275abc155ea137c"
Url_a = "https://invest.cnyes.com/usstock/detail/" & Stock & "/financial/financials20yr"


With Xmlhttp

.Open "POST", Url, False
.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
.setRequestHeader "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
.setRequestHeader "origin", "https://invest.cnyes.com"

.send

token = Split(Split(.responsetext, """token"" : """)(1), """")(0)

Url = "https://app.quotemedia.com/datatool/getFinancialsEnhancedBySymbol.json?symbol=" & Stock & "&numberOfReports=20&latestfiscaldate=true?cy=true&reportType=A&token=" & token

.Open "GET", Url, False
.setRequestHeader "origin", "https://invest.cnyes.com"
.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
.setRequestHeader "Referer", Url_a
.send

If InStr(1, .responsetext, "Company") = 0 Then
Debug.Print "no Company"
Exit Sub
End If

'use callbyname
Set DecodeJson = CallByName(CallByName(CallByName(Jsondata.JsonParse(.responsetext), "results", VbGet), "Company", VbGet), "Report", VbGet)

'use GetProperty
'Set DecodeJson = Jsondata.GetProperty(Jsondata.GetProperty(Jsondata.GetProperty(Jsondata.JsonParse(.responsetext), "results"), "Company"), "Report")



Set temp = Jsondata.GetKeys(DecodeJson)
periods = Split(temp, ",")

'debug
'Debug.Print temp & vbNewLine & UBound(periods)


For i = 0 To UBound(periods)

Set reports = Jsondata.GetProperty(DecodeJson, periods(i))
reportdate = Jsondata.GetProperty(reports, "reportDate")

'debug
Sheets("工作表1").Cells(lastrow + 1, 1) = reportdate

If IsObject(reports.BalanceSheet) Then 'if TypeName (reports.BalanceSheet)="JScriptTypeInfo"
Set temp = Jsondata.GetKeys(reports.BalanceSheet)
acts = Split(temp, ",")

For j = 0 To UBound(acts)
value = Jsondata.GetProperty(reports.BalanceSheet, acts(j))

'debug
Sheets("工作表1").Cells(j + 2 + lastrow, 1) = acts(j)
'debug
Sheets("工作表1").Cells(j + 2 + lastrow, 2) = "'" & value '純文字

Next j
End If

If IsObject(reports.IncomeStatement) Then
Set temp = Jsondata.GetKeys(reports.IncomeStatement)
acts = Split(temp, ",")

For j = 0 To UBound(acts)
value = Jsondata.GetProperty(reports.IncomeStatement, acts(j))

'debug
Sheets("工作表1").Cells(j + 2 + lastrow, 3) = acts(j)
'debug
Sheets("工作表1").Cells(j + 2 + lastrow, 4) = "'" & value


Next j
End If

If IsObject(reports.CashFlow) Then
Set temp = Jsondata.GetKeys(reports.CashFlow)
acts = Split(temp, ",")

For j = 0 To UBound(acts)
value = Jsondata.GetProperty(reports.CashFlow, acts(j))

'debug
Sheets("工作表1").Cells(j + 2 + lastrow, 5) = acts(j)
'debug
Sheets("工作表1").Cells(j + 2 + lastrow, 6) = "'" & value

Next j
End If

'debug
lastrow = Sheets("工作表1").UsedRange.Rows.Count + 5

Next i

Sheets("工作表1").Columns.AutoFit

Application.ScreenUpdating = True

Debug.Print Timer - ttt

End With


Set GetXml = Nothing
Set Jsondata = Nothing
Set DecodeJson = Nothing
Set reports = Nothing
Set temp = Nothing


End Sub
'===================================================================



[點擊下載]
g80860
板主功力真的強又深厚.重要是很熱心無私.板主謝謝你.
zxc_tw wrote:
我剛剛測試了, 可以下載. 我的系統是Windows


所以手動可以,vba不行?

改用URLDownloadToFile 試試(200樓範例)
再不行我也沒辦法了,因為我試都正常

'原始資料,最後一列的排版就是那樣



#If VBA7 Then
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
Alias "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, _
ByVal szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
#Else
Private Declare Function URLDownloadToFile Lib "urlmon" _
Alias "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, _
ByVal szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
#End If



Sub GetOILCsv()

Dim Url, Target, OILcsv, Clipboard As Object, OIL As Object
Set Clipboard = CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
Set OIL = CreateObject("ADODB.Stream")

On Error GoTo checkid


Target = "d:\excel\" '暫存目錄
If Dir(Target, vbDirectory) = "" Then MkDir Target
'注意啟用下一行,暫存目錄下的檔案,會在無任何提示下刪除
'If Dir(Target & "*.*") <> "" Then Kill Target & "*.*"

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

'原始資料來源網址(https://twn.databasesets.com/id/6339?amp=)
Url = "http://www3.cpc.com.tw/opendata_d00/webservice/中油主要產品牌價.csv"

URLDownloadToFile 0, Url, Target & "中油主要產品牌價.csv", 0, 0

'如果單純只是下載檔案,那程式碼到這裡就可以結束了

With OIL
.Type = 2
.Charset = "UTF-8"
.Open
.LoadFromFile Target & "中油主要產品牌價.csv"
OILcsv = .ReadText
.Close
End With

With Clipboard
.SetText OILcsv
.PutInClipboard
End With


With Sheets("工作表1")
.Select
.Cells.Clear
.Cells(1, 1).Select
.PasteSpecial NoHTMLFormatting:=True
.Columns(1).TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, TextQualifier:=xlDoubleQuote, Comma:=True, TrailingMinusNumbers:=True
.Columns.AutoFit
.Cells(1, 1).Select
End With

Set Clipboard = Nothing
Set OIL = Nothing

checkid:

If Err.Number <> 0 Then
Debug.Print Err.Description
End If

End Sub





[點擊下載]
zxc_tw
對, 手動可以. VBA會在第11行的.send有錯誤
snare wrote:
如果不想在 finance...(恕刪)


snare您好:
敝人又來打擾您了,研究您274樓的程式後,試圖將下圖圈起來以下的部份匯入,
網址為:https://query1.finance.yahoo.com/v7/finance/chart/KO?period1=-28800&period2=1639644269&interval=1mo&events=div
如圖

在VBA中設定看來是有設定到對的地方,如下圖

但是利用您上次Barrons網站的方法,找出 "length"的方法,但找不出"length",不知這種資料要如何將dividends以下的資料匯入呢?
是否可以指點迷津,感謝您撥空回應。
文章分享
評分
評分
複製連結
請輸入您要前往的頁數(1 ~ 159)

今日熱門文章 網友點擊推薦!