假設你有一個Excel,其中列出了所有收件人的資訊,如下所示:
如果需要向列表中的每個使用者傳送一封郵件,最好使用當前記錄生成一個附件,並且格式如下:
姓名,
傳送訊息
你應該怎麼辦?一個一個拷貝傳送?用python?
答案是,都不用,Excel自己解決。
通過本文,你將知道以下問題的答案:
- 什麼是VBA
- VBA能夠做什麼
- 怎麼編輯VBA
- 如何將VBA儲存到Excel檔案
- 為你的VBA指令碼建立一個快捷鍵
- VBA如何建立一個Excel檔案
- VBA如何將本Excel中的資料讀出並寫到另一個檔案
- VBA如何生成併傳送一個郵件?
- 傳送郵件過程總述
1. 什麼是VBA
根據微軟官網的解釋:
Office Visual Basic for Applications (VBA) 是事件驅動的程式語言,可以藉助它擴充套件 Office 應用程式。
根據官網定義,我們不難理解,VBA是用來擴充套件Office軟體功能的一門程式語言。並且VBA不僅僅可以用在Excel,還能用在Outlook,Access,Word等Office軟體中。
這就為我們使用VBA讀取Excel內容併傳送郵件奠定了基礎。
2. VBA能夠做什麼
作為一門程式語言,理論上講,VBA可以做到任何程式語言可以做到的事情,比如:
- 根據Excel中資料進行資料統計,並生成報表
- 訪問網路,並進行資料採集(網路爬蟲)
- 進行資料遷移,過濾...
可以說,只要有Office軟體存在的地方,VBA都可以有用武之地。
3. 怎麼編輯VBA
編輯VBA的時候,通常使用Visual Basic編輯器進行。要訪問Visual Basic編輯器,需要到功能區的"開發工具"選項卡中查詢。
在手動啟用"開發工具"選項卡之前,它預設是禁用掉的,我們可以通過如下方式啟用"開發工具"選項卡:
- 在 “檔案” 選項卡上,選擇 “選項” 以開啟 “選項” 對話方塊。
- 選擇該對話方塊左側的 “自定義功能區”。
- 在該對話方塊左側的 “從下列位置選擇命令” 下,選擇 “常用命令”。
- 在該對話方塊右側的 “自定義功能區” 下,從下拉選單框中選擇 “主選項卡”,然後選中 “開發工具” 核取方塊。
- 選擇“確定”。
備註:在 Office 2007 中,顯示 “開發工具” 選項卡的方法是選擇 Office 按鈕,選擇 “選項”,然後在 “選項” 對話方塊的 “常用” 類別中選中 “在功能區顯示‘開發工具’選項卡” 核取方塊。
https://docs.microsoft.com/zh-cn/office/vba/library-reference/concepts/getting-started-with-vba-in-office
啟用"開發工具"選項卡之後,要編輯VBA就很簡單了,只要切換到"開發工具"選項卡,點選"Visual Basic"按鈕,就會彈出Visual Basic編輯器了:
-
點選 "Visual Basic" 按鈕
-
彈出Visual Basic編輯器
在彈出的"Visual Basic" 編輯器中,我們可以看到,左側顯示了工程框和屬性框。
在工程框中,列出了當前以開啟的所有的Excel檔案資訊,如圖所示,當前,我開啟了兩個Excel檔案,分別為 "工作簿2.xlsx" 和 "工作簿4)。
雙擊左側"工作簿2.xlsx"節點下的 "Microsoft Excel 物件" -> Sheet1(Sheet1) ,在右側就會顯示編輯器的編輯區:
讓我們寫一行程式碼,打個招呼,複製如下程式碼到編輯區:
Sub SayHello()
MsgBox "Hello"
End Sub
點選工具欄的執行圖示,如圖所示:
然後程式會彈出一個對話方塊,讓你選擇一個巨集,來執行,如下:
在對話方塊中,我們看到了我們定義的SayHello,選中它,點選右側的"執行"按鈕。
現在,激動人心的時刻到來了,程式彈出了一個對話方塊:
到此為止,我們已經讓VBA彈出了一個對話方塊,接下來儲存檔案。
之後,我們發現,我們寫的程式碼在"工作簿2.xlsx"中消失了。
接下來,我們聊聊怎麼把程式碼儲存到Excel中。
4. 如何將VBA儲存到Excel檔案
在預設情況下,office 檔案(.xls,.xlsx,*.doc...)不允許儲存巨集(VBA程式碼),這個時候就需要將我們的檔案儲存為一種特殊的可以包含巨集指令碼的檔案格式,對於Excel來說,執行如下過程儲存:
1. 點選 "檔案"-->"另存為"
2. 選擇檔案格式為"Excel啟用巨集的工作簿"
3. 點選"儲存"
點選儲存之後,我們就得到了我們的目標檔案。
最後,我們發現,我們的副檔名變成了"xlsm",這就是我們要儲存的目標檔案了,我們的指令碼就儲存在這個檔案中。
關閉當前Excel,然後再開啟新檔案,我們發現,我們的指令碼已經原樣儲存了:
5. 為你的VBA指令碼建立一個快捷鍵
如果我們要執行一段程式碼,每次都要開啟程式碼編輯器,然後去點選啟動按鈕,也太麻煩了。那麼有沒有一種快速執行程式碼的方法呢?答案當然是肯定的,那就是為程式碼設定一個快捷鍵。
設定快捷鍵的過程如下:
1. 在Excel中選擇"開發工具"皮膚,點選"巨集"按鈕
2. 在彈出的巨集對話方塊中,選中要執行的巨集,這裡為"Sheet1.SayHello",之後點選右側的"選項"按鈕
3. 在彈出的"巨集選項"對話方塊中,在快捷鍵輸入快捷鍵,這裡以 r 為例
點選"確定"按鈕之後,啟用當前Excel窗體,按下 "Ctrl + r"快捷鍵,我們發現彈出了我們要的訊息框,如下:
6. VBA如何建立一個Excel檔案
經歷以上內容,我們已經可以開啟Visual Basic編輯器,可以寫程式碼,可以將程式碼儲存到檔案,最終,我們還為我們的程式碼執行建立了快捷鍵。
那麼接下來,為了給我們的郵件新增一個附件,我們需要先建立一個新的Excel工作簿文件,怎麼做呢?
在我們寫程式碼之前,請先參考如下資料:
瞭解 Visual Basic 語法
https://docs.microsoft.com/zh-cn/office/vba/language/concepts/getting-started/understanding-visual-basic-syntax
Office VBA入門
https://docs.microsoft.com/zh-cn/office/vba/library-reference/concepts/getting-started-with-vba-in-office
Application 物件 (Excel Graph)
https://docs.microsoft.com/zh-cn/office/vba/api/excel.application-graph-object
在瞭解以上資訊之後,我們不難理解如下程式碼:
Sub SayHello()
' 定義一個變數,用於引用新建的 Workbook
Dim newWorkbook As Workbook
' 新增一個 Workbook,並引用
Set newWorkbook = Workbooks.Add
On Error GoTo E
' 將新建的 Workbook 儲存到 "D:\xx.xlsx" 路徑。
' 這裡如果檔案已存在,會提示是否覆蓋.
' 路徑要使用 '\' 進行目錄隔離,使用'/'會報錯
newWorkbook.SaveAs ("D:\xx.xlsx")
On Error GoTo Dispose
Dispose:
' 最後,關閉新建的 Workbook。
newWorkbook.Close
E:
End Sub
接下來,我們為新建的 Workbook 新增一個 Worksheet,用於寫入資料:
Sub SayHello()
' 定義一個變數,用於引用新建的 Workbook
Dim newWorkbook As Workbook
' 定義一個變數,用於引用新增的 Worksheet
Dim newWorksheet As Worksheet
' 新增一個 Workbook,並引用
Set newWorkbook = Workbooks.Add
On Error GoTo E
' 新增一個 Worksheet
Set newWorksheet = newWorkbook.Sheets.Add
On Error GoTo E
' 將新建的 Worksheet 命名為 'attachment'
newWorksheet.Name = "attachment"
' 將新建的 Workbook 儲存到 "D:\xx.xlsx" 路徑。
' 這裡如果檔案已存在,會提示是否覆蓋.
' 路徑要使用 '\' 進行目錄隔離,使用'/'會報錯
newWorkbook.SaveAs ("D:\xx.xlsx")
On Error GoTo Dispose
Dispose:
' 最後,關閉新建的 Workbook。
newWorkbook.Close
E:
End Sub
在這裡,我們主要是新增了一個工作表,並將工作包的名字命名為 'attachment',執行以上程式碼,我們看到在 D 盤下,生成了一個新檔案 xx.xlsx,並且有一個工作表名字為 'attachment':
7. VBA如何將本Excel中的資料讀出並寫到另一個檔案
至第6節為止,我們已經可以使用VBA建立一個Excel檔案了,那麼接下來,我們聊聊怎麼向新增的檔案中新增內容,將程式碼修改為如下:
Sub SayHello()
' 定義一個變數,用於引用新建的 Workbook
Dim newWorkbook As Workbook
' 定義一個變數,用於引用新增的 Worksheet
Dim newWorksheet As Worksheet
' 定義一個工作表引用,用於引用當前工作簿的 'datasource' 工作表
Dim srcWorksheet As Worksheet
' 分別定義資料來源標題的 Range 和資料 Range,用於獲取資料
Dim rgTitleSrc As Range
Dim rgDataSrc As Range
' 分別定義目標標題的 Range 和資料 Range,用於寫入資料
Dim rgTitleDest As Range
Dim rgDataDest As Range
' 標記當前選中行
Dim selectedRow As Integer
' 新增一個 Workbook,並引用
Set newWorkbook = Workbooks.Add
On Error GoTo E
' 新增一個 Worksheet
Set newWorksheet = newWorkbook.Sheets.Add
On Error GoTo Dispose
' 將新建的 Worksheet 命名為 'attachment'
newWorksheet.Name = "attachment"
' 獲取到當前工作簿的 'datasource' 工作表引用
Set srcWorksheet = ThisWorkbook.Worksheets("datasource")
On Error GoTo Dispose
' 啟用資料來源工作表,以複製資料
srcWorksheet.Activate
On Error GoTo Dispose
' 設定當前選中行
selectedRow = Selection.Row
On Error GoTo Dispose
' 選中標題區域 title
Set rgTitleSrc = srcWorksheet.Range("A1", "C1")
On Error GoTo Dispose
' 選中資料區域,當前選中行
Set rgDataSrc = srcWorksheet.Range("A" & selectedRow, "C" & selectedRow)
On Error GoTo Dispose
With newWorksheet
' 複製資料來源標題
rgTitleSrc.Copy
' 將複製內容貼上到 A1
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
Application.CutCopyMode = False
' 複製資料來源資料
rgDataSrc.Copy
.Cells(2, "A").PasteSpecial Paste:=8
.Cells(2, "A").PasteSpecial xlPasteValues, , False, False
.Cells(2, "A").PasteSpecial xlPasteFormats, , False, False
' 啟用並選中目標工作表
newWorkbook.Activate
newWorkbook.Sheets(newWorksheet.Index).Select
'最終選中 A1 單元格
.Cells(1).Select
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo Dispose
End With
' 將新建的 Workbook 儲存到 "D:\xx.xlsx" 路徑。
' 這裡如果檔案已存在,會提示是否覆蓋.
' 路徑要使用 '\' 進行目錄隔離,使用'/'會報錯
newWorkbook.SaveAs ("D:\xx.xlsx")
On Error GoTo Dispose
Dispose:
' 最後,關閉新建的 Workbook。
newWorkbook.Close
E:
End Sub
好了,讓我們試試成果,按照如下步驟操作,看看有沒有生成我們要的檔案?
1. 選中我們原始檔中要新增到目標檔案資料的那一行的任何一個單元格,如下:
2.
可以看到,我們需要的資料已經放到目標檔案中去了。怎麼做到的呢?看看程式碼中以單引號開始的行吧,有說明
。
8. VBA如何生成併傳送一個郵件?
到目前為止,雖然我們成功的生成了我們的目標檔案,但是還沒有關係到郵件傳送。
本節,我們將詳細討論傳送郵件的過程。
首先,讓我們給我們剛開始定義的子程式SayHello改個名,叫做GenerateAttachment,如下:
Sub GenerateAttachment()
' 定義一個變數,用於引用新建的 Workbook
Dim newWorkbook As Workbook
' 定義一個變數,用於引用新增的 Worksheet
Dim newWorksheet As Worksheet
' 定義一個工作表引用,用於引用當前工作簿的 'datasource' 工作表
Dim srcWorksheet As Worksheet
' 分別定義資料來源標題的 Range 和資料 Range,用於獲取資料
Dim rgTitleSrc As Range
Dim rgDataSrc As Range
' 分別定義目標標題的 Range 和資料 Range,用於寫入資料
Dim rgTitleDest As Range
Dim rgDataDest As Range
' 標記當前選中行
Dim selectedRow As Integer
' 新增一個 Workbook,並引用
Set newWorkbook = Workbooks.Add
On Error GoTo E
' 新增一個 Worksheet
Set newWorksheet = newWorkbook.Sheets.Add
On Error GoTo Dispose
' 將新建的 Worksheet 命名為 'attachment'
newWorksheet.Name = "attachment"
' 獲取到當前工作簿的 'datasource' 工作表引用
Set srcWorksheet = ThisWorkbook.Worksheets("datasource")
On Error GoTo Dispose
' 啟用資料來源工作表,以複製資料
srcWorksheet.Activate
On Error GoTo Dispose
' 設定當前選中行
selectedRow = Selection.Row
On Error GoTo Dispose
' 選中標題區域 title
Set rgTitleSrc = srcWorksheet.Range("A1", "C1")
On Error GoTo Dispose
' 選中資料區域,當前選中行
Set rgDataSrc = srcWorksheet.Range("A" & selectedRow, "C" & selectedRow)
On Error GoTo Dispose
With newWorksheet
' 複製資料來源標題
rgTitleSrc.Copy
' 將複製內容貼上到 A1
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
Application.CutCopyMode = False
' 複製資料來源資料
rgDataSrc.Copy
.Cells(2, "A").PasteSpecial Paste:=8
.Cells(2, "A").PasteSpecial xlPasteValues, , False, False
.Cells(2, "A").PasteSpecial xlPasteFormats, , False, False
' 啟用並選中目標工作表
newWorkbook.Activate
newWorkbook.Sheets(newWorksheet.Index).Select
'最終選中 A1 單元格
.Cells(1).Select
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo Dispose
End With
' 將新建的 Workbook 儲存到 "D:\xx.xlsx" 路徑。
' 這裡如果檔案已存在,會提示是否覆蓋.
' 路徑要使用 '\' 進行目錄隔離,使用'/'會報錯
newWorkbook.SaveAs ("D:\xx.xlsx")
On Error GoTo Dispose
Dispose:
' 最後,關閉新建的 Workbook。
newWorkbook.Close
E:
End Sub
那麼現在,GenerateAttachment存在的意義,就只剩下在"D:\xx.xlsx"生成附件檔案了。
接下來,讓我們在GenerateAttachment上方新增一個函式,如下:
Sub SendMail()
GenerateAttachment
End Sub
從程式碼我們可以看到,SendMail子程式呼叫了GenerateAttachment子程式,經過測試,這樣和只有一個GenerateAttachment子程式產生的結果是一樣的。
那麼,接下來我們怎麼辦呢?
我們先建立一個Outlook程式,然後建立一個郵件訊息,然後從我們的Excel中讀取訊息,設定新建郵件訊息的內容以及將之前生成的附件新增到郵件中,修改SendMail程式碼如下:
Sub SendMail()
' 宣告一個引用,用於引用我們的 OutLook 例項。
Dim mailApp As Object
' 宣告引用,用於引用我們的郵件例項。
Dim mail As Object
' 用於訪問源工作表中資料
Dim srcWorksheet As Worksheet
' 用於記錄當前選中行
Dim selectedRow As Integer
' 生成附件
GenerateAttachment
' 獲取到當前工作簿的 'datasource' 工作表引用
Set srcWorksheet = ThisWorkbook.Worksheets("datasource")
On Error GoTo E
' 啟用資料來源工作表,以複製資料
srcWorksheet.Activate
On Error GoTo E
' 設定當前選中行
selectedRow = Selection.Row
On Error GoTo E
' 生成 Outlook 程式物件
Set mailApp = CreateObject("Outlook.Application")
On Error GoTo Dispose
' 生成一個郵件資訊
Set mail = mailApp.CreateItem(olMailItem)
On Error GoTo Dispose
With mail
' 設定收件人為源工作表的當前選中行的B列單元格的值
.To = srcWorksheet.Cells(selectedRow, "B").Value
' 設定抄送人
.CC = ""
' 設定密送人
.BCC = ""
' 設定郵件標題
.Subject = "一封新郵件"
' 設定附件,附件已經由 GenerateAttachment 子程式放在
' D:\xx.xlsx,所以這裡我們直接將其新增進來
.Attachments.Add "D:\xx.xlsx"
' 設定郵件內容文字,其中從A列取使用者名稱,C列取訊息
' 然後合併,作為郵件體
.Body = srcWorksheet.Cells(selectedRow, "A").Value & "," & vbNewLine & srcWorksheet.Cells(selectedRow, "C").Value
' 最後,顯示郵件資訊
.Display
End With
Dispose:
E:
End Sub
試執行,我們發現,生成了目標附件,並且彈出了一個Outlook新建郵件的視窗,如下:
嗯,看起來不錯,我們得到了郵件,然後我們再編輯快捷方式,將 SendMail的呼叫快捷方式改為 "Ctrl+r",那麼每次我們選中一行資料,並且按下快捷鍵的時候,就會自動生成我們要傳送的檔案了。
注意:
- 這裡為了演示方便,我們將生成附件的路徑寫死了,請根據你的實際情況修改;
- 在執行巨集的時候,有可能遇到巨集被禁用的情況,這種情況下,開啟Excel(xlsm)檔案時,在Excel上方會顯示啟用巨集的提示,只要點選啟用就可以了。
- 在執行我們的程式的時候,目標Excel(xx.xlsx)不能開啟,否則會導致生成附件失敗。
9. 傳送郵件過程總述
好了,我們總結一下使用Excel傳送郵件的主流程:
- 使用 Workbooks.Add 方法,新建一個Excel附件工作簿;
- 使用 newWorkbook.Sheets.Add 方法,新增一個工作表;
- 使用 newWorksheet.Name,設定新建工作表的名稱;
- 使用 newWorksheet.Range 方法,分別選中要新增到目標檔案的區域;
- 使用Range.Copy以及Cells.PasteSpecial.Paste等,將複製的區域複製到目標工作表的指定位置;
- 使用newWorkbook.SaveAs方法,將工作表儲存到我們預定義的位置;
- 使用 CreateObject("Outlook.Application") 呼叫,生成一個Outlook程式物件;
- 使用 mailApp.CreateItem(olMailItem)呼叫,生成一個郵件物件;
- 分別設定郵件物件的屬性;
- 呼叫mail.Display顯示郵件或者呼叫mail.Send傳送郵件;
到了最後,我們的全部程式碼如下:
Sub SendMail()
' 宣告一個引用,用於引用我們的 OutLook 例項。
Dim mailApp As Object
' 宣告引用,用於引用我們的郵件例項。
Dim mail As Object
' 用於訪問源工作表中資料
Dim srcWorksheet As Worksheet
' 用於記錄當前選中行
Dim selectedRow As Integer
' 生成附件
GenerateAttachment
' 獲取到當前工作簿的 'datasource' 工作表引用
Set srcWorksheet = ThisWorkbook.Worksheets("datasource")
On Error GoTo E
' 啟用資料來源工作表,以複製資料
srcWorksheet.Activate
On Error GoTo E
' 設定當前選中行
selectedRow = Selection.Row
On Error GoTo E
' 生成 Outlook 程式物件
Set mailApp = CreateObject("Outlook.Application")
On Error GoTo Dispose
' 生成一個郵件資訊
Set mail = mailApp.CreateItem(olMailItem)
On Error GoTo Dispose
With mail
' 設定收件人為源工作表的當前選中行的B列單元格的值
.To = srcWorksheet.Cells(selectedRow, "B").Value
' 設定抄送人
.CC = ""
' 設定密送人
.BCC = ""
' 設定郵件標題
.Subject = "一封新郵件"
' 設定附件,附件已經由 GenerateAttachment 子程式放在
' D:\xx.xlsx,所以這裡我們直接將其新增進來
.Attachments.Add "D:\xx.xlsx"
' 設定郵件內容文字,其中從A列取使用者名稱,C列取訊息
' 然後合併,作為郵件體
.Body = srcWorksheet.Cells(selectedRow, "A").Value & "," & vbNewLine & srcWorksheet.Cells(selectedRow, "C").Value
' 最後,顯示郵件資訊
.Display
End With
Dispose:
E:
End Sub
Sub GenerateAttachment()
' 定義一個變數,用於引用新建的 Workbook
Dim newWorkbook As Workbook
' 定義一個變數,用於引用新增的 Worksheet
Dim newWorksheet As Worksheet
' 定義一個工作表引用,用於引用當前工作簿的 'datasource' 工作表
Dim srcWorksheet As Worksheet
' 分別定義資料來源標題的 Range 和資料 Range,用於獲取資料
Dim rgTitleSrc As Range
Dim rgDataSrc As Range
' 分別定義目標標題的 Range 和資料 Range,用於寫入資料
Dim rgTitleDest As Range
Dim rgDataDest As Range
' 標記當前選中行
Dim selectedRow As Integer
' 新增一個 Workbook,並引用
Set newWorkbook = Workbooks.Add
On Error GoTo E
' 新增一個 Worksheet
Set newWorksheet = newWorkbook.Sheets.Add
On Error GoTo Dispose
' 將新建的 Worksheet 命名為 'attachment'
newWorksheet.Name = "attachment"
' 獲取到當前工作簿的 'datasource' 工作表引用
Set srcWorksheet = ThisWorkbook.Worksheets("datasource")
On Error GoTo Dispose
' 啟用資料來源工作表,以複製資料
srcWorksheet.Activate
On Error GoTo Dispose
' 設定當前選中行
selectedRow = Selection.Row
On Error GoTo Dispose
' 選中標題區域 title
Set rgTitleSrc = srcWorksheet.Range("A1", "C1")
On Error GoTo Dispose
' 選中資料區域,當前選中行
Set rgDataSrc = srcWorksheet.Range("A" & selectedRow, "C" & selectedRow)
On Error GoTo Dispose
With newWorksheet
' 複製資料來源標題
rgTitleSrc.Copy
' 將複製內容貼上到 A1
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
Application.CutCopyMode = False
' 複製資料來源資料
rgDataSrc.Copy
.Cells(2, "A").PasteSpecial Paste:=8
.Cells(2, "A").PasteSpecial xlPasteValues, , False, False
.Cells(2, "A").PasteSpecial xlPasteFormats, , False, False
' 啟用並選中目標工作表
newWorkbook.Activate
newWorkbook.Sheets(newWorksheet.Index).Select
'最終選中 A1 單元格
.Cells(1).Select
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo Dispose
End With
' 將新建的 Workbook 儲存到 "D:\xx.xlsx" 路徑。
' 這裡如果檔案已存在,會提示是否覆蓋.
' 路徑要使用 '\' 進行目錄隔離,使用'/'會報錯
newWorkbook.SaveAs ("D:\xx.xlsx")
On Error GoTo Dispose
Dispose:
' 最後,關閉新建的 Workbook。
newWorkbook.Close
E:
End Sub
最後的最後,不要忘了關注公眾號[程式設計之路漫漫],碼途求知己,天涯覓一心。