標題:
用迴圈抓資料越跑越慢,該如何釋放記憶體?
[打印本頁]
作者:
sasho
時間:
2014-6-7 01:53
標題:
用迴圈抓資料越跑越慢,該如何釋放記憶體?
各位前輩大家好,
我有個程式使用迴圈及querytable.add的方式抓取外部資料(抓完會執行querytable.delete)
並在匯入資料後用worksheets.copy的方式另存,
此時程式會自動新增一個活頁簿,待存檔後再將新增的活頁簿關閉
但我發現如此一來,因為每次copy工作表時都會新增一個活頁簿
如果我的迴圈一共要抓好幾百次的資料
那就會增加好幾百個活頁簿
雖然之後會關閉,但不曉得記憶體有沒有釋放掉,因為活頁簿的編號會一直增加上去
而程式跑的速度也越來越慢....@@"
可以請各位前輩幫忙解答一下嗎? 謝謝
作者:
GBKEE
時間:
2014-6-7 06:43
本帖最後由 GBKEE 於 2014-6-21 05:18 編輯
回復
1#
sasho
Option Explicit
Sub Ex()
Dim i As Integer, rng As Range
With ActiveSheet
If .QueryTables.Count = 0 Then .QueryTables.Add "URL;", .[a1]
For i = 3 To 17 Step 2
'迴圈中ㄧ直的 querytable.add 檔案會胖起來,導致程式的速度越來越慢
With .QueryTables(1)
.Connection = "URL;http://forum.twbts.com/thread-635-1-1.html"
.WebSelectionType = xlSpecifiedTables
.WebFormatting = xlWebFormattingNone
.WebTables = i & ""
.WebPreFormattedTextToColumns = True
.WebConsecutiveDelimitersAsOne = True
.WebSingleBlockTextImport = False
.WebDisableDateRecognition = False
.WebDisableRedirections = False
.Refresh BackgroundQuery:=False
Wb_Save .ResultRange, i
End With
Next
End With
End Sub
Private Sub Wb_Save(Rng As Range, i As Integer) '副程式:新增活頁簿,存檔
With Workbooks.Add(1)
Rng.Copy .Sheets(1).[a1]
.Close True, "d:\test_" & i & ".xls"
'關閉存檔
End With
End Sub
複製代碼
作者:
c_c_lai
時間:
2014-6-7 07:01
本帖最後由 c_c_lai 於 2014-6-7 07:06 編輯
各位前輩大家好,
我有個程式使用迴圈及querytable.add的方式抓取外部資料(抓完會執行querytable.delete) ...
sasho 發表於 2014-6-7 01:53
依你描述作業過程,勢必會愈來愈慢,EXCEL 本身在執行時會另外產生一工作站暫存檔,
它會隨時動態地將你所有異動狀態紀錄於此檔案內,它承擔了所有驗對、變動、刪除、
新增以及記憶體的耗用,於檔案不斷增大時,其負荷亦跟隨之增大。
解決方法可將須不斷增加的檔案存於另一工作檔,不用時則隨時將它關閉,如此作業
再來評估成效。就如上 GBKEE 版大所展示的程式碼一樣,處理完畢即隨手將它關閉。
作者:
sasho
時間:
2014-6-16 18:12
本帖最後由 sasho 於 2014-6-16 18:16 編輯
回復
2#
GBKEE
感謝G大的指點,但可能我表達的不是很清楚,所以讓大家誤會我的問題了
跟G大所示範的一樣,程式再匯入網頁資料後都會另存成一個CSV檔案並關閉,
同時,將所新增的querytable delete掉,程式碼如下:
Sub 取得資料(strURL As String, Table As String)
Dim xlSheet As Excel.Worksheet
Set xlSheet = Sheets("Temp")
Do
Application.DisplayAlerts = False
With xlSheet.QueryTables.Add("URL;" & strURL, xlSheet.Cells(1, 1))
.WebFormatting = xlWebFormattingNone
.WebSelectionType = xlSpecifiedTables
.WebTables = Table
.BackgroundQuery = False
On Error Resume Next
Do
Err.Clear
.Refresh 0
If Err.Number Then
Application.Wait Now + TimeValue("00:00:01")
End If
Loop Until Err.Number = 0
.Delete
'If Err.Number <> 0 Then Err.Clear: MsgBox Err.Number '被免資料抓取不成功,而顯示訊息
On Error GoTo 0
End With
If Err.Number = 0 Then
Application.DisplayAlerts = True
Exit Sub
End If
Loop
儲存CSV DownloadDate,id
End Sub
Sub 儲存CSV(SaveDate As String, CSVname As String)
Dim TestObj As Object
Dim CSVfile As String, CSVfolder As String
Dim TestFolder As Boolean
FilePath = "D:\TSE\"
CSVfolder = FilePath & SaveDate & "\"
CSVfile = CSVfolder & CSVname & "_" & SaveDate & ".csv"
'Debug.Print CSVfile
Set TestObj = CreateObject("Scripting.FileSystemObject")
TestFolder = TestObj.FolderExists(CSVfolder)
If TestFolder = False Then TestObj.CreateFolder (CSVfolder)
On Error Resume Next
Kill CSVfile
On Error GoTo 0
Worksheets(Tempname).Copy
Application.DisplayAlerts = False
With ActiveWorkbook
.SaveAs FileName:=CSVfile, FileFormat:=xlCSV
.Close 0
End With
Application.DisplayAlerts = True
End Sub
複製代碼
我的問題是在於,由於我用迴圈一共要抓幾百筆的資料
我從儲存下來的檔案最後變動時間觀察,發現一開始可能一秒鐘可以抓五到六個檔案
但到後面,可能一秒鐘就只能抓兩個檔案,檔案大小都差不多
一直不知道為什麼速度會越來越慢,每抓完一筆檔案就執行一次 doevents
所以應該不至於造成CPU資源被吃光
唯一能想到的就是,每次另存一個檔案,就會新開一個活頁簿
比方說,活頁簿1、活頁簿2、活頁簿3....到最後的活頁簿999
雖然把資料複製過去後、另存新檔後該活頁簿就會關閉,但EXCEL好像還是會占用到記憶體資源
是不是因為這樣才導致速度越來越慢呢?
還是說,是因為我複製的方式不正確才會導致記憶體增加的呢?
G大跟我的差別在於,匯入資料後G大是workbook.add然後複製匯入的range
而我則是直接將整個worksheet copy
不曉得這兩者的方式有差嗎
謝謝各位前輩指點 m(_ _)m
作者:
stillfish00
時間:
2014-6-16 19:42
回復
4#
sasho
我會先把呼叫
儲存CSV DownloadDate,id
複製代碼
改為
t = timer
儲存CSV DownloadDate,id
debug.print timer-t
複製代碼
先觀察確認 時間變長的地方 是在 存檔這部分。
作者:
sasho
時間:
2014-6-16 23:02
回復
5#
stillfish00
時間變長的部分,應該是在匯入網頁資料所花的時間
一開始匯入很順,但越到後面就越卡,不知道是什麼原因
同時我有觀察EXCEL在工作管理員中占用的記憶體容量
從程式執行前到程式執行後,整個記憶體增加了不少
一樣也是不知道原因
懇請各位大大解救一下
作者:
stillfish00
時間:
2014-6-17 09:55
回復
7#
GBKEE
Hi 版大,
我不曉得是不是EXCEL版本差異,
我在Excel2010中,刪除QueryTable物件就會直接刪除Names中的項目了。
作者:
stillfish00
時間:
2014-6-17 14:08
本帖最後由 stillfish00 於 2014-6-17 14:09 編輯
回復
9#
GBKEE
QueryTables.Delete 只會刪除連線和定義的名稱,不會刪除取得的資料
而樓主用迴圈執行多筆時資料會一直往右累積增加
這也許可以改RefreshStyle為xlOverwriteCells來防止(預設是xlInsertDeleteCells)
像這樣
Sub Ex()
Dim i As Integer, Rng As Range
With ActiveSheet
For i = 3 To 7 Step 2
With .QueryTables.Add("URL;http://forum.twbts.com/thread-635-1-1.html", .Cells(1, 1))
.WebSelectionType = xlSpecifiedTables
.WebFormatting = xlWebFormattingNone
.WebTables = i & ""
.WebPreFormattedTextToColumns = True
.WebConsecutiveDelimitersAsOne = True
.WebSingleBlockTextImport = False
.WebDisableDateRecognition = False
.WebDisableRedirections = False
.RefreshStyle = xlOverwriteCells
.Refresh BackgroundQuery:=False
Wb_Save .ResultRange, i
.Delete
End With
Next
End With
End Sub
複製代碼
作者:
sasho
時間:
2014-6-17 17:16
回復
8#
stillfish00
我使用的是EXCEL 2010,執行QueryTables.delete並不會將匯入資料刪除,只會把連線刪除而已
另外,我在另存匯入資料後,也會在執行worksheets.cells.delete,所以資料是不會向右累積的
感謝
作者:
GBKEE
時間:
2014-6-18 06:17
回復
9#
sasho
參考 這裡
作者:
sasho
時間:
2014-6-18 17:03
回復
10#
GBKEE
感謝G大的分享
不過小弟我還是不太了解,為什麼在EXCEL中用迴圈匯入資料會越匯越慢....
我使用的是win 7、64位元、6G記憶體
平常有清除系統垃圾的習慣,且在執行程式時,不論CPU或是記憶體都顯示尚有多餘的資源(從工作管理員觀察)
況且,如果是因為系統資源不足所導致的問題,應該不會出現執行程式初期速度較快,而執行到後面速度就整個拖下來的情況才是
應該是從頭到尾都很慢才是
不曉得我這樣推論是否正確呢?
作者:
GBKEE
時間:
2014-6-18 17:13
回復
11#
sasho
你使用的是EXCEL 2010 ,可否上傳你的檔案.
我來試試 XP、32位元、1G記憶體 ,2003版本的速度
作者:
sasho
時間:
2014-6-24 18:45
回復
12#
GBKEE
感謝G版大的幫忙,因為我無法上傳檔案,所以把檔案放在如下的dropbox的網址
另外,最近幾天測試的結果,發現問題是出在兩個部分
一個是匯入網頁資料,另一個是將匯入資料另存成csv檔
根據記錄的資料,這兩個動作都會隨著迴圈越跑越多而越來越慢
還請G版大指點一下,謝謝
https://www.dropbox.com/s/n0ycnc7q4gqtbrb/%E6%8A%93%E5%8F%96%E4%B8%8A%E5%B8%82%E8%B3%87%E6%96%99.xls
作者:
GBKEE
時間:
2014-6-25 13:20
本帖最後由 GBKEE 於 2014-6-25 13:39 編輯
回復
13#
sasho
你原本的 Sub 執行(),在我的PC沒有你所說的越來越慢的情形.速度與Main() 的記錄檔差不多.
整理一下,附上 Sub Main() 的記錄檔
[attach]18553[/attach]
Option Explicit
Dim IE As Object, Query_Sh As Worksheet, CsvPath As String, SaveDate As String
Dim t As Date, StartTime As Date, 記錄檔 As String, stockid As Range, spListCount As Integer
Sub Main()
Dim i As Integer
t = Time
StartTime = Time
CsvPath = "D:\TSE\"
目錄 CsvPath
記錄檔 = CsvPath & "Main_Record.TXT"
If Dir(記錄檔) <> "" Then Kill 記錄檔
暫存頁 "temp"
xRecond 0, "程式開始執行" & vbCrLf
Set stockid = Sheets("工作表1").Range("A2")
stockid.Parent.Activate
Do While stockid <> ""
Application.ScreenUpdating = True
stockid.Select
Application.ScreenUpdating = False
StartTime = Time
spListCount = 資料頁數
If spListCount > 0 Then
i = i + 1
xRecond i, stockid & vbTab & "資料匯入"
資料匯入
整理
存檔
xRecond i, stockid.Value & vbTab & "存檔完畢 " & Format(Time - StartTime, "共SS秒") & vbCrLf
End If
Set stockid = stockid.Offset(1)
Loop
IE.Quit
Application.DisplayAlerts = False
Query_Sh.Delete
Application.DisplayAlerts = True
Workbooks.Open 記錄檔
MsgBox "共存 ""(" & i & ") csv檔完畢" & vbTab & "費時 " & Format(Time - t, "nn分ss秒")
End Sub
Private Sub 暫存頁(temp As String)
On Error Resume Next
Set Query_Sh = Sheets(temp)
If Err.Number = 9 Then
Sheets.Add(, Sheets(1)).Name = temp
Set Query_Sh = Sheets(temp)
End If
End Sub
Private Sub 資料匯入()
Dim strURL As String
strURL = "URL;" & "http://bsr.twse.com.tw/bshtm/bsContent.aspx?StartNumber=" & stockid & "&FocusIndex=All_" & spListCount
With Query_Sh
.UsedRange.Clear
With .QueryTables.Add(strURL, Query_Sh.[a1])
.WebFormatting = xlWebFormattingNone
.WebSelectionType = xlSpecifiedTables
.WebTables = "5,table2"
.Refresh 0
.Delete
End With
End With
End Sub
Private Sub 整理()
Dim i As Integer
With Sheets("temp")
SaveDate = Format(.Range("B1"), "YYYYMMDD")
With .UsedRange.Range("A:A")
.SpecialCells(xlCellTypeConstants, xlTextValues).EntireRow.Delete
.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End With
.UsedRange.Columns("F:J").Cut
.Range("A" & .Rows.Count).End(xlUp).Offset(1).Insert Shift:=xlDown
.UsedRange.Sort Key1:=.Range("A1"), Order1:=xlAscending, Header:=xlNo
.UsedRange.Columns("B:B").Insert Shift:=xlToRight
.UsedRange.Columns(1) = SaveDate
.UsedRange.Columns(2) = stockid
For i = 1 To .UsedRange.Rows.Count
.Cells(i, 3) = Left(.Cells(i, 3), 4)
.Cells(i, 5) = .Cells(i, 5).Value / 1000
.Cells(i, 6) = .Cells(i, 6).Value / 1000
Next
End With
End Sub
Private Sub 目錄(xPath As String)
Dim SP As Variant, P As String, i As Integer
SP = Split(xPath, "\")
P = SP(0)
With CreateObject("Scripting.FileSystemObject")
For i = 1 To UBound(SP)
P = P & "\" & SP(i)
If .FolderExists(P) = False Then .CreateFolder (P)
Next
End With
End Sub
Private Sub 存檔()
Dim CSVfolder As String, CSVfile As String
CSVfolder = CsvPath & SaveDate & "\"
目錄 CSVfolder
CSVfile = CSVfolder & stockid & "_" & SaveDate & ".csv"
If Dir(CSVfile) <> "" Then Kill CSVfile
Query_Sh.Copy
With ActiveWorkbook
.SaveAs Filename:=CSVfile, FileFormat:=xlCSV
.Close 0
End With
End Sub
Private Sub xRecond(i As Integer, xSub As String)
Dim S As String
S = Time & vbTab & Format(Time - t, " 第nn分ss秒") & vbTab & " 第 " & i & " 個Csv檔 " & xSub
Close #1
Open 記錄檔 For Append As #1
Print #1, S
Close #1
Application.StatusBar = S
End Sub
Private Function 資料頁數() As Integer '取得頁數
If IE Is Nothing Then
Set IE = CreateObject("InternetExplorer.Application")
IE.Navigate "http://bsr.twse.com.tw/bshtm/bsMenu.aspx"
IE.Visible = True '可不顯示
End If
With IE
Do: Loop While .Busy Or IE.ReadyState <> 4
With .document
.getElementByID("txtTASKNO").Value = stockid
.getElementByID("btnOK").Click
Do: Loop While IE.Busy Or IE.ReadyState <> 4 Or .getElementByID("sp_ListCount") Is Nothing
資料頁數 = Val(.getElementByID("sp_ListCount").innertext)
End With
End With
End Function
複製代碼
作者:
sasho
時間:
2014-6-27 21:36
回復
14#
GBKEE
實際測試G大所提供的程式,確實比之前的那個還要快很多
仔細比對後,我發現主要的問題是在於調整格式這部分
因為這些資料我會再匯入到mysql當中,所以資料格式不能出錯
所以在最一開始我所提供的程式中,在sub 調整格式()的最後,我有再加上一段指令
cells.NumberFormatLocal = "G/通用格式"
而這段指令是G大所提供的程式碼中所沒有的
目前推測就是因為這段敘述,導致整個程式速度降了下來
以及程式執行過程,EXCEL所占用的記憶體不斷攀高
感謝G大的指點,受教了!
作者:
GBKEE
時間:
2014-6-28 04:39
回復
15#
sasho
Cells.NumberFormatLocal = "G/通用格式"
可改成(縮小範圍)
UsedRange.NumberFormatLocal = "G/通用格式"
複製代碼
歡迎光臨 麻辣家族討論版版 (http://forum.twbts.com/)