假設你有一個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
最后的最后,不要忘了關注公眾號[編程之路漫漫],碼途求知己,天涯覓一心。