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

JackLoh wrote:

但我還是很想了解為什麼
它可以讓 WinHttp 或者 IDHttp 抓不到資料
還是你有解決之道
...(恕刪)


因為 "CSRF" ,詳細請 google "csrf token"

需要在xmlhttp物件裡,send 2次

第一次要在 meta name 裡面,拆出 csrf-token 的值
第二次再把 csrf-token,給要查詢的網址

傳回值是 json 格式,中文是 unicode







'=============================
'因為用來篩選的變數實在太多,範例中網址只用到3個變數=>新北市、鄉鎮不限、1000萬以下
'有興趣請自行測試增加



Sub get591()


Dim Jsondata As Object, DecodeJson, url As String, url_a As String, getxml As Object, temp, temp1, Token As String
Set getxml = CreateObject("WinHttp.WinHttpRequest.5.1")
Set Jsondata = CreateObject("HtmlFile")

Jsondata.write ”<script>document.JsonParse=function (s) {return eval(’(’ + s + ’)’);}</script>”
'Jsondata.write 這行程式碼,符號是全形字,複製後,請改成半形,或直接看附加檔案

Cells.Clear
'On Error Resume Next

url = "https://sale.591.com.tw/?shType=list&price=0_1000Rionid=3"

With getxml
.Open "GET", url, False
.setrequestheader "Cache-Control", "no-cache"
.setrequestheader "Pragma", "no-cache"
.setrequestheader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
.setrequestheader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
.send

Token = Split(Split(.responsetext, "<meta name="" csrf-token""="" content="" ")(1),="" """="">")(0)


url = "https://sale.591.com.tw/home/search/list?type=2&&shType=list®ionid=3&price=0_1000%C2%AEionid,0_1000×tamp="
url_a = "https://sale.591.com.tw/?shType=list&price=0_1000Rionid=3"

.Open "GET", url & UNIXTime, False
.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"
.setrequestheader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
.setrequestheader "X-CSRF-TOKEN", Token
.send

Set DecodeJson = Jsondata.JsonParse(Unicode_to_Cht(.responsetext))
Set temp = CallByName(CallByName(DecodeJson, "data", VbGet), "house_list", VbGet)

Debug.Print "total=" & CallByName(CallByName(DecodeJson, "data", VbGet), "total", VbGet) + 1
Debug.Print "first page total=" & CallByName(temp, "length", VbGet)

For i = 0 To CallByName(temp, "length", VbGet) - 1
Set temp1 = CallByName(temp, i, VbGet)
'同上,因資料太多,只簡單列出=>標題、地區,其它請自行增加
Cells(i + 1, 1) = CallByName(temp1, "title", VbGet)
Cells(i + 1, 2) = temp1.region_name & temp1.section_name
Next i


End With

Set DecodeJson = Nothing
Set Jsondata = Nothing
Set getxml = Nothing
Set temp = Nothing

End Sub


Function UNIXTime()

UNIXTime = Round(((Date - #1/1/1970#) * 86400 + Timer) * 1000, 0)

End Function

Function Unicode_to_Cht(unicode As String)

With CreateObject("htmlfile")
.write "<script></script>"
'.write 這行程式碼,符號是全形字,複製後,請改成半形,或直接看附加檔案
Unicode_to_Cht = .parentwindow.unescape(Replace(unicode, "\u", "%u"))
End With

End Function




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




附加壓縮檔: 201904/mobile01-15c87802fa168793935dd0762388a7ef.zip
感謝Acer_kewei 提供想法, Snare 大大的程式資料及教學

620 的 0050 明細少抓一個table, 修正如下 (紅字)

Sub get_0050持股明細()

Dim url As String, HTMLsourcecode As Object, Getxml As Object, i As Integer, j As Integer, Update_Day As String
Set HTMLsourcecode = CreateObject("htmlfile")
Set Getxml = CreateObject("msxml2.xmlhttp")
url = "https://www.moneydj.com/ETF/X/Basic/Basic0007A.xdjhtm?etfid=0050.TW"
Application.ScreenUpdating = False

With Getxml

.Open "GET", url, False
.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 = .responsetext


With Sheets("台灣五十成份股比例")
.Cells.Clear

Update_Day = HTMLsourcecode.getelementbyid("ctl00_ctl00_MainContent_MainContent_sdate2").innertext
.Cells(1, 1) = "持股分佈 (依產業)" & "(" & Update_Day & ")"

Set Table = HTMLsourcecode.all.tags("table")(4).Rows
For i = 0 To Table.Length - 1
For j = 1 To Table(i).Cells.Length - 1
.Cells(i + 2, j) = Table(i).Cells(j).innertext
Next j
Next i

Update_Day = HTMLsourcecode.getelementbyid("ctl00_ctl00_MainContent_MainContent_sdate3").innertext
.Cells(1, 5) = "元大台灣卓越50基金-持股明細" & "(" & Update_Day & ")"
Set Table = HTMLsourcecode.all.tags("table")(5).Rows
For i = 0 To Table.Length - 1
For j = 0 To Table(i).Cells.Length - 1
.Cells(i + 2, j + 5) = Table(i).Cells(j).innertext
Next j
Next i
Set Table = HTMLsourcecode.all.tags("table")(6).Rows
For i = 0 To Table.Length - 1
For j = 0 To Table(i).Cells.Length - 1
.Cells(i + 2, j + 9) = Table(i).Cells(j).innertext
Next j
Next i


End With


End With

Call SetFormatCondition("台灣五十成份股比例", "H3")
Call SetFormatCondition("台灣五十成份股比例", "L3")
Set HTMLsourcecode = Nothing
Set Getxml = Nothing

Application.ScreenUpdating = True


End Sub
樓主您好
我是VBA初學者,在您的文章看到可從21樓的程式開始入門
但在執行過程中發生兩個問題
1. 無法執行避免抓取暫存資料程式
2. 當執行到Set Table = HTMLsourcecode.all.tags("table")(6).Rows時也是發生錯誤
不知道可否請教如何進行debug


yuhuahsiao wrote:
620 的 0050 明細少抓一個table, 修正如下 (紅字)...(恕刪)


感謝提醒,幫忙修改
重新檢查網頁後,發現確實漏了一個表格
water-仔 wrote:
但在執行過程中發生兩個問題
1. 無法執行避免抓取暫存資料程式
2. 當執行到Set Table = HTMLsourcecode.all.tags("table")(6).Rows時也是發生錯誤...(恕刪)


一、 => ' <=上逗號沒刪掉,綠色代表不執行
二、表格換位置了,21樓有說明如何找

snare wrote:
需先用文字處理函數replace)處理 .responsetext...(恕刪)

這個方式,小弟不知從何開始下手,可否請樓主簡單說明一下

snare wrote:
可改用21樓逐格寫入的方式,直接跳過第一欄、第一列...(恕刪)

此方式似乎若以.tags(“table”)(0)填入,會產生全部資料在同一儲存格;若以.tags(“table”)(2)處理,好像可以填入,但仍會產生部分資料在同一儲存格中(如圖紅框),同時會少了”B”欄(與clipboard方式比較),請教樓主,是取錯table,還是程式碼有誤?



snare wrote:
或是只取出表格放入clipboard...(恕刪)

此方式即可正確下載資料,請教樓主,以此網站為例,是否僅適用此方式下載資料?另附檔中有querytable方式,其webtables卻是”3”,為何與xmlhttp方式的table=0不同?




附加壓縮檔: 201904/mobile01-4b1185e36c1ba2b581154e548ee5cc87.zip
activer wrote:
這個方式,小弟不知從何開始下手,可否請樓主簡單說明一下sorry
...(恕刪)


假設有一個網頁表格長這樣(圖片左邊是網頁、右邊是網頁原始碼)
抓下來的.responsetext會是右邊

如果把其中3行用空白取代,replace(.responsetext,"要取代html語法","")
就會變成第2張圖的樣子





但網頁表格,有的是用置中排列、有的是把每列第一格用空白、有的是設定大小、有的是設定邊界…等等很多不同的方法
所以用這樣方法,必需要先了解 html 語法,才知道要修改什麼


詳細請 google

google "html 語法"
google "html 表格 語法"


activer wrote:
此方式似乎若以.tags(“table”)(0)填入,會產生全部資料在同一儲存格;若以.tags(“table”)(2)處理,好像可以填入,但仍會產生部分資料在同一儲存格中(如圖紅框),同時會少了”B”欄(與clipboard方式比較),請教樓主,是取錯table,還是程式碼有誤?
...(恕刪)


程式碼正確

逐格寫入的方式,無法判斷html中的排列方式,例如:網頁中的合併儲存格,excel中會變成一格
所以有些下載後,要排版整理

同一儲存格的資料是未整理前
.tags(“table”)(3)
才是整理好的資料

雖然逐格寫入有排版的缺點,但是優點是可放入陣列,可只取一格
也可在記憶體中,只取需要的資料做複雜計算,再放入儲存格
(例如:95樓、98樓範例,95f請自行在表格中放入測試資料)


activer wrote:
以此網站為例,是否僅適用此方式下載資料?另附檔中有querytable方式,其webtables卻是”3”,為何與xmlhttp方式的table=0不同?
...(恕刪)


此樓的很多種方式都可以混用,只是看您要用那一種

webtables 從 1 開始
.tags(“table”) 從 0 開始
webtables="1" = .tags(“table”)(0)


snare wrote:
假設有一個網頁表格長這樣(圖片左邊是網頁、右邊是網頁原始碼)...(恕刪)

簡單易懂,謝謝
snare wrote:
這是用vba,把yahoo...(恕刪)
(308樓)

請問 Snare 大大

在技術圖左上角的日線/周線..及成交量/KD... 這兩個變數是否能在VBA寫固定嗎?
即開啟的股票每一檔都是已選好的周期及指標
yuhuahsiao wrote:
在技術圖左上角的日線/周線..及成交量/KD... 這兩個變數是否能在VBA寫固定嗎?
即開啟的股票每一檔都是已選好的周期及指標...(恕刪)




308樓,1年多前的範例

那個範例只是把網頁整合,資料並沒有下載,所以一開始沒辦法固定預設值
而且308樓的範例iframe名稱,當初偷懶全部用空白的
所以要控制main.html中的10個網頁,308樓的範例要重寫
有空再改(補充:新範例請參考645樓)



不過只改一個圖表很簡單,可以在網頁打開後用 ie object 方式修改

(程式碼放在同一個檔案中執行,ie 需允許 activex ,不然圖表出不來)
方法1
'==========================
Sub test()

Dim ie As Object


Set ie = CreateObject("InternetExplorer.Application")

With ie
.Visible = True
.navigate ThisWorkbook.Path & "\2002_TaChart.html"

Do Until .readyState = 4: DoEvents: Loop
Do While .Busy: DoEvents: Loop


.document.getElementById("TAChartPeriod").selectedindex = 2
.document.getElementById("TAChartIndex").selectedindex = 1
'.document.all("TAChartPeriod").Value = "30m"
'5m、10m、30m、d、w、m
'.document.all("TAChartIndex").Value = "KD"
'VOL、KD、MACD、RSI、BIAS、WR、BS、CDP、DMI
.Refresh

End With


'ie.Quit
Set ie = Nothing

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


方法2
'==========================
Sub test1()

Dim ie As Object, DOM_event As Object


Set ie = CreateObject("InternetExplorer.Application")

With ie
.Visible = True
.navigate ThisWorkbook.Path & "\2002_TaChart.html"

Do Until .readyState = 4: DoEvents: Loop
Do While .Busy: DoEvents: Loop

Set DOM_event = .document.createEvent("HTMLEvents")
DOM_event.initEvent "change", True, False


'.document.getElementById("TAChartPeriod").Focus
.document.getElementById("TAChartPeriod").selectedindex = 2
.document.getElementById("TAChartPeriod").dispatchEvent DOM_event


'.document.getElementById("TAChartIndex").Focus
.document.getElementById("TAChartIndex").selectedindex = 1
.document.getElementById("TAChartIndex").dispatchEvent DOM_event



End With


'ie.Quit
Set ie = Nothing
Set DOM_event = Nothing

End Sub

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

方法3
使用第三方工具,例如:AutoHotkey,幫忙按選單

方法4
使用vba sendkey 來代替(可參考566樓範例)

方法5
使用vba 控制滑鼠點擊
關閉廣告
文章分享
評分
評分
複製連結
請輸入您要前往的頁數(1 ~ 144)

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