VBA ByVal用法屬於進階技巧,通常在開發大型專案可能會用到。本文以實際具體的範例,介紹程式該怎麼優化的流程,補充有遇到執行階段錯誤91該如何處理。
目錄
Toggle上一節已經成功單一網頁取得特定資料,例如書籍頁面的出版社及分類,實務情況常常需要取得大量相同性質的網頁資料,以本書範例而言,每天有100本書的排行榜,為了取得更完整分析所需資料,有必要依次訪問100個各別書籍網頁,取得100組出版社及分類,這便是一般所謂的網路爬蟲。本節分享在上一節VBA程式碼的基礎,利用先前章節介紹的迴圈流程,循環執行大量網頁爬蟲取得資料。
一、自動下載多網頁(ByVal傳遞參數)
隨著VBA程式專案的發展,常常用由不同的模組和程序共同合作,例如這裡的「取得大量網頁資料1()」和「出版社1(WebUrl,1)」,前者是以本章第二節所建立的連結網址為主,執行1到100的迴圈控制流程,,後者則是上一節取得書籍網頁出版社的程式碼。在第i次迴圈執行時,先將Excel工作表上的連結網址設定為變數WebUrl的值,透過「Call 出版社1(WebUrl,i)」和「Sub 出版社1(WebUtl,i)」的設定,讓「(WebUtl,i)」兩個變數在不同程序之間傳遞,取得出版社資料後,寫入Excel的Cells(i,3)」的位置。
關於VBA傳遞參數其實有分為ByVal及ByRef,差別在單純傳遞值(ByVal)或者傳遞參照位址(ByRef),這裡沒有特地標記,作用相當於預設的ByVal。這裡的範例只是純粹減少程式碼,避免一再重覆設定日期,因此直接簡化處理。
Sub 取得大量網頁資料1()
=>建立程序。
For i = 1 To 10
=>建立1到10迴圈,原始排行榜有100筆,這裡迴圈只設到10,主要作為程式測試使用。
WebUrl = Cells(i, 2).Value
=>設定變數為特定的儲存格值
Call 出版社1(WebUrl, i)
=>呼叫執行程式「出版社1」,同時傳遞WebUrl,及i兩個變數值
Next i
=>迴圈事執行下一個i
End Sub
=>結束程序
Sub 出版社1(WebUrl, i)
=>建立程序
‘取得網頁原始碼特定標籤文字
Dim ExlIE As Object, WebTxt As String
=>定義物件及文件變數
Set ExlIE = CreateObject(“InternetExplorer.Application”)
=>設定要引用的ie瀏覽器外掛程式
With ExlIE
=>針對ExlIE 這個ie瀏覽器外掛設定屬性
.Visible = False
=>將瀏覽器可見性設為假,測覽器執行時將隱藏
.Navigate WebUrl
=>ie瀏覽器前往WebUrl這個網址
Do Until .ReadyState = READYSTATE_COMPLETE
=>一直迴圈執行直到取得能完整瀏覽的網頁
Loop
=>配合上一行程式形成迴圈,直到取得完整網頁
End With
=>結束針對ExlIE 這個ie瀏覽器外掛的設定
With ExlIE.Document
=>針對ExlIE所取得的網頁原始文件作設定
WebTxt = .getElementsByTagName(“meta”)(“description”).outerhtml
=>先取得所有「TagName」為「meta」的標籤集合,再限定其中的「description」標籤項目,「outerhtml」意思是連同標籤文字本身也要
End With
=>結束ExlIE對象的設定
ExlIE.Quit
=>關閉ExlIE瀏覽器
Set ExlIE = Nothing
=>將ExlIE從電腦記憶體空間中移除
‘處理所取得網頁標籤文字
Dim BookDes, BookPub
=>定義沒有預設資料型態的變數
BookDes = Split(WebTxt, “,”)
=>以「,」資料剖析切割WebTxt變數
BookPub = BookDes(5)
=> BookDes陣列資料中取第5項為變數值
BookPub = Replace(BookDes(5), “出版社:”, “”)
=>將「出版社:」以空白取代,實質效果為刪除
Cells(i, 3) = BookPub
=>最後將出版社資料寫入儲存格中
End Sub
=>結束程序

二、錯誤排除(出版社資訊錯誤)
雖然是延用上一節成功取得出版社的程式,本節將資料擴大到100筆,馬上發現原有程式的問題,大部份是出版社(「方智),但也有一部份是作者「作者:黃越緩」。
實際比較兩本書籍網頁,會發現一個英文書、一個是中文書,英文書會多一個原文名稱,而程式是單純取得第5項陣列資料「BookPub = BookDes(5)」,因此造成錯誤。

三、程式執行錯誤(網路爬蟲中斷)
除了出版社問題,還有可能跑到一半中斷,回應「執行階段錯誤’91」,錯如截圖畫面,經過幾次測試,這並不是程式設計本身有問題,而是要讓VBA跑100遍IE取得網頁資料,程式會變得不太穩定,因此必須再優化。

四、錯誤預防機制(On Error GoTo)
「出版社2(WebUrl, i)」相較於「出版社1(WebUrl, i)」有兩個變動:
第一個變動是多了「On Error GoTo」、「Exit Sub」、「IEWrong: i = i – 1」的架構:
(一)On Error GoTo:在執行網路爬蟲出現錯誤時,直接跳到程序最後特定位置,也就是程式最後的「IEWrong: i = i – 1」,這個稍後第三項有補充說明。
(二)Exit Sub:作用是強制結束程式,這裡代表如果沒有錯誤的話,在還沒執行到最後先行中斷結束。
(三)IEWrong: i = i – 1:作用是當網路爬蟲出現錯誤,可想而知並沒有順利得到第i本書籍的出版社資料,所以將i-1之後,回到「取得大量網頁資料」的程序繼續i+1的迴圈,在一減一加的過程中,等於會再重覆執行第i次的迴圈。
第二個變動是「BookPub = Filter(BookDes, “出版社:”, , vbBinaryCompare)」,這裡為了避免以特定位置定位出版社的話,可能會出現像本節第二步驟中英書籍類型的錯誤,因此改為以關鍵字定位,篩選含有「出版社:」的項目建立新的陣列,或者可稱之為文字數列,再因為VBA程式中的陣列是0,1,2,3,…這樣從0開始的,所以取第0個陣列項目將「出版社:」去掉,得到剩下的出版社資料:「BookPub = Replace(BookPub(0), “出版社:”, “”)」。

五、爬蟲緩衝機制(Do…Loop Until)
「取得大量網頁資料2」相較於「取得大量網頁資料1」也有兩個主要的變動:
第一個變動是Do…Loop迴圈事件。它和For…Next同樣都是迴圈控制流程,差別在於For…Next是固定次數迴圈,Do…Loop則是依照特定條件是否成立,決定是否再繼續執行迴圈,因此是不定次數的迴圈。在VBA程式中Do…Loop搭配While或者Until有四種型式,四種各自的程式和英文意思是相對應的。
例如原有程式裡已經有的「Do Until .ReadyState = READYSTATE_COMPLETE Loop」架構,意思是一直到整個網頁開啟完畢才結束迴圈。這裡也是Do…Loop Until的型式,意思是直到i>100條件成立時才結束迴圈。配合上個步驟「出版社2(WebUrl, i)」中的「On Error GoTo…IEWrong: i = i – 1)」,等於是控制程式重覆迴圈直到全部100個網頁出版社資訊都得到才結束。
第二個變動是「Application.Wait Now + TimeValue(“00:00:10”)」,作用是程式執行到這裡時暫時中止,等待10秒鐘的時間,這個是為了避免大量網路爬蟲出現第三步驟時的防衛機制,讓程式在每取得一次網頁之後,暫停10秒鐘,雖然整個程式執行時間會變得很久,但比較穩定一點。

六、更多資料取得(取得分類資訊)
有了取得100個出版社網頁資料的基礎,要再設計取得100個書籍分類的VBA程式相同容易,將原來取得一個網頁的程式碼,依照100個分類程式的思惟架構加以擴充即可。

七、完整程式執行(100筆網路爬蟲)
將這一節修改後的程式全部執行完畢,順利取得當日排行榜100本書的出版社及書籍分類資料。

Excel VBA專案:測試、思考、執行、修正
這一節特地保留原始程式測試、思考、修正、完成的過程, 一方面是網頁結構隨著時間變動,有可能要因應情況而有所調整,因此在此也無法保證本節程式在往後期間也能正常發揮作用,不過設計的原理是相同的,真有需要的話,請讀者參考本節修正。另外本書在後面預計會介紹機器學習,人在面對變化時必須修正調整,像這一節的過程如果能教給機器去嘗試學習,是在更高一層次善用機器提高作業效率,也許沒辦法完全替代,但至少是個值得努力的方向。
贊贊小屋VBA教學中心:
Excel巨集錄製教學、Excel巨集程式、VBA編輯器、VBA自學入門、VBA基礎語法、VBA基本應用、VBA UserForm、VBA VLOOKUP。
取得範例程式請前往VBA社團,訂閱請加入VBA Line社群。
VBA課程推薦:零基礎入門進階的20小時完整內容