Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
、安裝Nuget包MailKit,引用命名空間。
using MailKit.Net.Smtp;
using MimeKit;
注意:引用MailKit對(duì)應(yīng)最新版本
2、定義收發(fā)地址和標(biāo)題
MimeMessage message=new MimeMessage();
MailboxAddress from=new MailboxAddress("Admin","admin@example.com");
message.From.Add(from);
MailboxAddress to=new MailboxAddress("User", "user@example.com");
message.To.Add(to);
message.Subject="This is email subject";
注意:Admin,User分別對(duì)應(yīng)發(fā)送接收郵箱前綴
3、編寫(xiě)內(nèi)容
BodyBuilder bodyBuilder=new BodyBuilder();
bodyBuilder.HtmlBody="<h1>Hello World!</h1>";
bodyBuilder.TextBody="Hello World!";
message.Body=bodyBuilder.ToMessageBody();
注意:也可以自定義模板,插入圖片等等。
4、連接SMTP服務(wù)器發(fā)送郵件
SmtpClient client=new SmtpClient();client.Connect("smtp_address_here", port_here, true); //例如:smtp.exmail.qq.com,465client.Authenticate("admin@example.com", "password"); //發(fā)送郵件的賬戶密碼client.Send(message);client.Disconnect(true);client.Dispose();
時(shí)候我們需要以郵件形式發(fā)送附有條形碼的門(mén)票、實(shí)施通訊、請(qǐng)柬。那么面對(duì)這樣的情況我們?cè)撛趺刺幚砟兀拷裉煳覀儗⒔榻B如何用Barcode Professional生成及發(fā)送有條形碼的HTML電子郵件。
參考步驟:
VB
Private?Function?GetBarcodeImage?As?System.IO.MemoryStream ?'Create?an?instance?of?BarcodeProfessional?class ?Dim?bcp?As?New?Neodynamic.WebControls.BarcodeProfessional.BarcodeProfessional ? ?'Set?barcode?settings... ?'Code?128?symbology ?bcp.Symbology?=?Neodynamic.WebControls.BarcodeProfessional.Symbology.Code128 ?'Set?a?fictitious?value?to?encode ?bcp.Code?=?Guid.NewGuid.ToString.Replace("-",?"").Substring(0,?20).ToUpper ? ?'Return?barcode?stream ?Return?New?System.IO.MemoryStream(bcp.GetBarcodeImage(System.Drawing.Imaging.ImageFormat.Png)) ?End?Function
C#
private?System.IO.MemoryStream?GetBarcodeImage ?{ ?//Create?an?instance?of?BarcodeProfessional?class ?Neodynamic.WebControls.BarcodeProfessional.BarcodeProfessional?bcp?=?new?Neodynamic.WebControls.BarcodeProfessional.BarcodeProfessional; ? ?//Set?barcode?settings... ?//Code?128?symbology ?bcp.Symbology?=?Neodynamic.WebControls.BarcodeProfessional.Symbology.Code128; ?//Set?a?fictitious?value?to?encode ?bcp.Code?=?Guid.NewGuid.ToString.Replace("-","").Substring(0,20).ToUpper; ? ?//Return?barcode?stream ?return?new?System.IO.MemoryStream(bcp.GetBarcodeImage(System.Drawing.Imaging.ImageFormat.Png)); ?}
VB
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click 'Create the mail message Dim mail As New System.Net.Mail.MailMessage 'Set the email addresses mail.From=New System.Net.Mail.MailAddress("me@mycompany.com") mail.To.Add(Me.TextBox1.Text) 'Set the subject mail.Subject="John Doe in Concert - Barcode Ticket" 'Create the Html part. 'To embed the barcode image, we need to use the prefix cid in the img src attribute. 'The cid value will map to the Content-Id of a Linked resource. 'Example:
will map to a LinkedResource with a ContentId of barcodeticket Dim htmlContent1 As String="
NEOMIX | |
ADMIT ONE | |
NEO STADIUM | |
GENERAL ADMISSION | |
John Doe in Concert | |
May 19 2007 | SATURDAY 8:00 PM |
$ 98.00 |
" Dim htmlView As System.Net.Mail.AlternateView=System.Net.Mail.AlternateView.CreateAlternateViewFromString(htmlContent1 + htmlContent2 + htmlContent3, Nothing, "text/html") 'Create the LinkedResource (embedded barcode image) Dim barcode As New System.Net.Mail.LinkedResource(Me.GetBarcodeImage, "image/png") barcode.ContentId="barcodeticket" 'Add the LinkedResource to the view htmlView.LinkedResources.Add(barcode) 'Add the view mail.AlternateViews.Add(htmlView) 'specify the mail server address Dim smtp As New System.Net.Mail.SmtpClient("127.0.0.1") 'send the message smtp.Send(mail) End Sub
C#
protected void Button1_Click(object sender, EventArgs e) { //Create the mail message System.Net.Mail.MailMessage mail=new System.Net.Mail.MailMessage; //Set the email addresses mail.From=new System.Net.Mail.MailAddress("me@mycompany.com"); mail.To.Add(this.TextBox1.Text); //Set the subject mail.Subject="John Doe in Concert - Barcode Ticket"; //Create the Html part. //To embed the barcode image, we need to use the prefix 'cid' in the img src attribute. //The cid value will map to the Content-Id of a Linked resource. //Example:
will map to a LinkedResource with a ContentId of 'barcodeticket' string htmlContent1="
NEOMIX | |
ADMIT ONE | |
NEO STADIUM | |
GENERAL ADMISSION | |
John Doe in Concert | |
May 19 2007 | SATURDAY 8:00 PM |
$ 98.00 |
"; System.Net.Mail.AlternateView htmlView=System.Net.Mail.AlternateView.CreateAlternateViewFromString(htmlContent1 + htmlContent2 + htmlContent3, null, "text/html"); //Create the LinkedResource (embedded barcode image) System.Net.Mail.LinkedResource barcode=new System.Net.Mail.LinkedResource(this.GetBarcodeImage, "image/png"); barcode.ContentId="barcodeticket"; //Add the LinkedResource to the view htmlView.LinkedResources.Add(barcode); //Add the view mail.AlternateViews.Add(htmlView); //specify the mail server address System.Net.Mail.SmtpClient smtp=new System.Net.Mail.SmtpClient("127.0.0.1"); //send the message smtp.Send(mail); }
當(dāng)你指定有效地址并點(diǎn)擊Send Barcode Ticket后,你將收到上文附有條碼的HTML郵件
本文譯自neodynamic
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯
查和答復(fù)電子郵件會(huì)占用大量的時(shí)間。當(dāng)然,你不能只寫(xiě)一個(gè)程序來(lái)處理所有電子郵件,因?yàn)槊總€(gè)消息都需要有自己的回應(yīng)。但是,一旦知道怎么編寫(xiě)收發(fā)電子郵件的程序,就可以自動(dòng)化大量與電子郵件相關(guān)的任務(wù)。
例如,也許你有一個(gè)電子表格,包含許多客戶記錄,希望根據(jù)他們的年齡和位置信息,向每個(gè)客戶發(fā)送不同格式的郵件。商業(yè)軟件可能無(wú)法做這一點(diǎn)。好在,可以編寫(xiě)自己的程序來(lái)發(fā)送這些電子郵件,節(jié)省了大量復(fù)制和粘貼電子郵件的時(shí)間。
也可以編程發(fā)送電子郵件和短信,即使你遠(yuǎn)離計(jì)算機(jī)時(shí),也能通知你。如果要自動(dòng)化的任務(wù)需要執(zhí)行幾個(gè)小時(shí),你不希望每過(guò)幾分鐘就回到計(jì)算機(jī)旁邊,檢查程序的狀態(tài)。相反,程序可以在完成時(shí)向手機(jī)發(fā)短信,讓你在離開(kāi)計(jì)算機(jī)時(shí),能專注于更重要的事情。
正如HTTP是計(jì)算機(jī)用來(lái)通過(guò)因特網(wǎng)發(fā)送網(wǎng)頁(yè)的協(xié)議,簡(jiǎn)單郵件傳輸協(xié)議(SMTP)是用于發(fā)送電子郵件的協(xié)議。SMTP 規(guī)定電子郵件應(yīng)該如何格式化、加密、在郵件服務(wù)器之間傳遞,以及在你點(diǎn)擊發(fā)送后,計(jì)算機(jī)要處理的所有其他細(xì)節(jié)。但是,你并不需要知道這些技術(shù)細(xì)節(jié),因?yàn)镻ython的smtplib模塊將它們簡(jiǎn)化成幾個(gè)函數(shù)。
SMTP只負(fù)責(zé)向別人發(fā)送電子郵件。另一個(gè)協(xié)議,名為IMAP,負(fù)責(zé)取回發(fā)送給你的電子郵件,在16.3節(jié)“IMAP”中介紹。
你可能對(duì)發(fā)送電子郵件很熟悉,通過(guò)Outlook、Thunderbird或某個(gè)網(wǎng)站,如Gmail或雅虎郵箱。遺憾的是,Python沒(méi)有像這些服務(wù)一樣提供一個(gè)漂亮的圖形用戶界面。作為替代,你調(diào)用函數(shù)來(lái)執(zhí)行SMTP的每個(gè)重要步驟,就像下面的交互式環(huán)境的例子。
{注意}
不要在IDLE中輸入這個(gè)例子,因?yàn)閟mtp.example.com、bob@example.com、MY_ SECRET_PASSWORD和alice@example.com只是占位符。這段代碼僅僅勾勒出Python發(fā)送電子郵件的過(guò)程。
>>> smtpObj=smtplib.SMTP('smtp.example.com', 587)
>>> smtpObj.ehlo()
(250, b'mx.example.com at your service, [216.172.148.131]\nSIZE 35882577\
n8BITMIME\nSTARTTLS\nENHANCEDSTATUSCODES\nCHUNKING')
>>> smtpObj.starttls()
(220, b'2.0.0 Ready to start TLS')
>>> smtpObj.login('bob@example.com', 'MY_SECRET_PASSWORD')
(235, b'2.7.0 Accepted')
>>> smtpObj.sendmail('bob@example.com', 'alice@example.com', 'Subject: So
long.\nDear Alice, so long and thanks for all the fish. Sincerely, Bob')
{}
>>> smtpObj.quit()
(221, b'2.0.0 closing connection ko10sm23097611pbd.52 - gsmtp')
在下面的小節(jié)中,我們將探討每一步,用你的信息替換占位符,連接并登錄到SMTP服務(wù)器,發(fā)送電子郵件,并從服務(wù)器斷開(kāi)連接。
如果你曾設(shè)置了Thunderbird、Outlook或其他程序,連接到你的電子郵件賬戶,你可能熟悉配置SMTP服務(wù)器和端口。這些設(shè)置因電子郵件提供商而不同,但在網(wǎng)上搜索“< 你的提供商> SMTP設(shè)置”,應(yīng)該能找到相應(yīng)的服務(wù)器和端口。
SMTP服務(wù)器的域名通常是電子郵件提供商的域名,前面加上SMTP。例如,Gmail的 SMTP 服務(wù)器是smtp.gmail.com。表 16-1 列出了一些常見(jiàn)的電子郵件提供商及其SMTP服務(wù)器(端口是一個(gè)整數(shù)值,幾乎總是587,該端口由命令加密標(biāo)準(zhǔn)TLS使用)。
表16-1 電子郵件提供商及其SMTP服務(wù)器
得到電子郵件提供商的域名和端口信息后,調(diào)用smtplib.SMTP()創(chuàng)建一個(gè)SMTP對(duì)象,傳入域名作為一個(gè)字符串參數(shù),傳入端口作為整數(shù)參數(shù)。SMTP對(duì)象表示與SMTP郵件服務(wù)器的連接,它有一些發(fā)送電子郵件的方法。例如,下面的調(diào)用創(chuàng)建了一個(gè)SMTP對(duì)象,連接到Gmail:
>>> smtpObj=smtplib.SMTP('smtp.gmail.com', 587)
>>> type(smtpObj)
< class 'smtplib.SMTP'>
輸入type(smtpObj)表明,smtpObj中保存了一個(gè)SMTP對(duì)象。你需要這個(gè)SMTP對(duì)象,以便調(diào)用它的方法,登錄并發(fā)送電子郵件。如果smtplib.SMTP()調(diào)用不成功,你的SMTP服務(wù)器可能不支持TLS端口587。在這種情況下,你需要利用smtplib.SMTP_SSL()和465端口,來(lái)創(chuàng)建SMTP對(duì)象。
>>> smtpObj=smtplib.SMTP_SSL('smtp.gmail.com', 465)
{注意}
如果沒(méi)有連接到因特網(wǎng),Python將拋出socket.gaierror: [Errno 11004] getaddrinfo failed或類(lèi)似的異常。
對(duì)于你的程序,TLS和SSL之間的區(qū)別并不重要。只需要知道你的SMTP服務(wù)器使用哪種加密標(biāo)準(zhǔn),這樣就知道如何連接它。在接下來(lái)的所有交互式環(huán)境示例中,smtpObj變量將包含smtplib.SMTP()或smtplib.SMTP_SSL()函數(shù)返回的SMTP對(duì)象。
得到SMTP對(duì)象后,調(diào)用它的名字古怪的EHLO()方法,向SMTP電子郵件服務(wù)器“打招呼”。這種問(wèn)候是SMTP中的第一步,對(duì)于建立到服務(wù)器的連接是很重要的。你不需要知道這些協(xié)議的細(xì)節(jié)。只要確保得到SMTP對(duì)象后,第一件事就是調(diào)用ehlo()方法,否則以后的方法調(diào)用會(huì)導(dǎo)致錯(cuò)誤。下面是一個(gè)ehlo()調(diào)用和返回值的例子:
>>> smtpObj.ehlo()
(250, b'mx.google.com at your service, [216.172.148.131]\nSIZE 35882577\
n8BITMIME\nSTARTTLS\nENHANCEDSTATUSCODES\nCHUNKING')
如果在返回的元組中,第一項(xiàng)是整數(shù)250(SMTP中“成功”的代碼),則問(wèn)候成功了。
如果要連接到SMTP服務(wù)器的587端口(即使用TLS加密),接下來(lái)需要調(diào)用starttls()方法。這是為連接實(shí)現(xiàn)加密必須的步驟。如果要連接到465端口(使用SSL),加密已經(jīng)設(shè)置好了,你應(yīng)該跳過(guò)這一步。
下面是starttls()方法調(diào)用的例子:
>>> smtpObj.starttls()
(220, b'2.0.0 Ready to start TLS')
starttls()讓SMTP連接處于TLS模式。返回值220告訴你,該服務(wù)器已準(zhǔn)備就緒。
到SMTP服務(wù)器的加密連接建立后,可以調(diào)用login()方法,用你的用戶名(通常是你的電子郵件地址)和電子郵件密碼登錄。
>>> smtpObj.login('my_email_address@gmail.com', 'MY_SECRET_PASSWORD')
(235, b'2.7.0 Accepted')
傳入電子郵件地址字符串作為第一個(gè)參數(shù),密碼字符串作為第二個(gè)參數(shù)。返回值235表示認(rèn)證成功。如果密碼不正確,Python會(huì)拋出smtplib. SMTPAuthenticationError異常。
將密碼放在源代碼中要當(dāng)心。如果有人復(fù)制了你的程序,他們就能訪問(wèn)你的電子郵件賬戶!調(diào)用input(),讓用戶輸入密碼是一個(gè)好主意。每次運(yùn)行程序時(shí)輸入密碼可能不方便,但這種方法不會(huì)在未加密的文件中留下你的密碼,黑客或筆記本電腦竊賊不會(huì)輕易地得到它。
登錄到電子郵件提供商的SMTP服務(wù)器后,可以調(diào)用的sendmail()方法來(lái)發(fā)送電子郵件。sendmail()方法調(diào)用看起來(lái)像這樣:
>>> smtpObj.sendmail('my_email_address@gmail.com', 'recipient@example.com',
'Subject: So long.\nDear Alice, so long and thanks for all the fish. Sincerely,
Bob')
{}
sendmail()方法需要三個(gè)參數(shù)。
電子郵件正文字符串必須以’Subject: \n’開(kāi)頭,作為電子郵件的主題行。’\n’換行符將主題行與電子郵件的正文分開(kāi)。
sendmail()的返回值是一個(gè)字典。對(duì)于電子郵件傳送失敗的每個(gè)收件人,該字典中會(huì)有一個(gè)鍵值對(duì)。空的字典意味著對(duì)所有收件人已成功發(fā)送電子郵件。
{Gmail應(yīng)用程序?qū)S妹艽a!!}
Gmail有針對(duì)谷歌賬戶的附加安全功能,稱為應(yīng)用程序?qū)S妹艽a。如果當(dāng)你的程序試圖登錄時(shí),收到“需要應(yīng)用程序?qū)S妹艽a”的錯(cuò)誤信息,就必須在Python腳本設(shè)置這樣一個(gè)密碼。具體如何設(shè)置谷歌賬戶的應(yīng)用程序?qū)S妹艽a,參見(jiàn)http://nostarch.com/automatestuff/。
確保在完成發(fā)送電子郵件時(shí),調(diào)用quit()方法。這讓程序從SMTP服務(wù)器斷開(kāi)。
>>> smtpObj.quit()
(221, b'2.0.0 closing connection ko10sm23097611pbd.52 - gsmtp')
返回值221表示會(huì)話結(jié)束。
要復(fù)習(xí)連接和登錄服務(wù)器、發(fā)送電子郵件和斷開(kāi)的所有步驟,請(qǐng)參閱 16.2節(jié)“發(fā)送電子郵件”。
正如SMTP是用于發(fā)送電子郵件的協(xié)議,因特網(wǎng)消息訪問(wèn)協(xié)議(IMAP)規(guī)定了如何與電子郵件服務(wù)提供商的服務(wù)器通信,取回發(fā)送到你的電子郵件地址的電子郵件。Python帶有一個(gè)imaplib模塊,但實(shí)際上第三方的imapclient模塊更易用。本章介紹了如何使用IMAPClient,完整的文檔在http://imapclient.readthedocs.org/。
imapclient模塊從IMAP服務(wù)器下載電子郵件,格式相當(dāng)復(fù)雜。你很可能希望將它們從這種格式轉(zhuǎn)換成簡(jiǎn)單的字符串。pyzmail模塊替你完成解析這些郵件的辛苦工作。在http://www.magiksys.net/pyzmail/可以找到PyzMail的完整文檔。
從終端窗口安裝imapclient和pyzmail。附錄A包含了如何安裝第三方模塊的步驟。
在Python中,查找和獲取電子郵件是一個(gè)多步驟的過(guò)程,需要第三方模塊imapclient和pyzmail。作為概述,這里有一個(gè)完整的例子,包括登錄到IMAP服務(wù)器,搜索電子郵件,獲取它們,然后從中提取電子郵件的文本。
>>> import imapclient
>>> imapObj=imapclient.IMAPClient('imap.gmail.com', ssl=True)
>>> imapObj.login('my_email_address@gmail.com', 'MY_SECRET_PASSWORD')
'my_email_address@gmail.com Jane Doe authenticated (Success)'
>>> imapObj.select_folder('INBOX', readonly=True)
>>> UIDs=imapObj.search(['SINCE 05-Jul-2014'])
>>> UIDs
[40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040, 40041]
>>> rawMessages=imapObj.fetch([40041], ['BODY[]', 'FLAGS'])
>>> import pyzmail
>>> message=pyzmail.PyzMessage.factory(rawMessages[40041]['BODY[]'])
>>> message.get_subject()
'Hello!'
>>> message.get_addresses('from')
[('Edward Snowden', 'esnowden@nsa.gov')]
>>> message.get_addresses('to')
[(Jane Doe', 'jdoe@example.com')]
>>> message.get_addresses('cc')
[]
>>> message.get_addresses('bcc')
[]
>>> message.text_part !=None
True
>>> message.text_part.get_payload().decode(message.text_part.charset)
'Follow the money.\r\n\r\n-Ed\r\n'
>>> message.html_part !=None
True
>>> message.html_part.get_payload().decode(message.html_part.charset)
'< div dir="ltr">< div>So long, and thanks for all the fish!< br>< br>< /div>-
Al< br>< /div>\r\n'
>>> imapObj.logout()
你不必記住這些步驟。在詳細(xì)介紹每一步之后,你可以回來(lái)看這個(gè)概述,加強(qiáng)記憶。
就像你需要一個(gè)SMTP對(duì)象連接到SMTP服務(wù)器并發(fā)送電子郵件一樣,你需要一個(gè)IMAPClient對(duì)象,連接到IMAP服務(wù)器并接收電子郵件。首先,你需要電子郵件服務(wù)提供商的IMAP服務(wù)器域名。這和SMTP服務(wù)器的域名不同。表16-2列出了幾個(gè)流行的電子郵件服務(wù)提供商的IMAP服務(wù)器。
表16-2 電子郵件提供商及其IMAP服務(wù)器
得到IMAP服務(wù)器域名后,調(diào)用imapclient.IMAPClient()函數(shù),創(chuàng)建一個(gè)IMAPClient對(duì)象。大多數(shù)電子郵件提供商要求SSL加密,傳入SSL=TRUE關(guān)鍵字參數(shù)。在交互式環(huán)境中輸入以下代碼(使用你的提供商的域名):
>>> import imapclient
>>> imapObj=imapclient.IMAPClient('imap.gmail.com', ssl=True)
在接下來(lái)的小節(jié)里所有交互式環(huán)境的例子中,imapObj變量將包含imapclient.IMAPClient()函數(shù)返回的IMAPClient對(duì)象。在這里,客戶端是連接到服務(wù)器的對(duì)象。
取得IMAPClient對(duì)象后,調(diào)用它的login()方法,傳入用戶名(這通常是你的電子郵件地址)和密碼字符串。
>>> imapObj.login('my_email_address@gmail.com', 'MY_SECRET_PASSWORD')
'my_email_address@gmail.com Jane Doe authenticated (Success)'
要記住,永遠(yuǎn)不要直接在代碼中寫(xiě)入密碼!應(yīng)該讓程序從input()接受輸入的密碼。
如果IMAP服務(wù)器拒絕用戶名/密碼的組合,Python會(huì)拋出imaplib.error異常。對(duì)于Gmail賬戶,你可能需要使用應(yīng)用程序?qū)S玫拿艽a。詳細(xì)信息請(qǐng)參閱16.2.5節(jié)中的“Gmail應(yīng)用程序?qū)S妹艽a”。
登錄后,實(shí)際獲取你感興趣的電子郵件分為兩步。首先,必須選擇要搜索的文件夾。然后,必須調(diào)用IMAPClient對(duì)象的search()方法,傳入IMAP搜索關(guān)鍵詞字符串。
幾乎每個(gè)賬戶默認(rèn)都有一個(gè)INBOX文件夾,但也可以調(diào)用IMAPClient對(duì)象的list_folders()方法,獲取文件夾列表。這將返回一個(gè)元組的列表。每個(gè)元組包含一個(gè)文件夾的信息。輸入以下代碼,繼續(xù)交互式環(huán)境的例子:
>>> import pprint
>>> pprint.pprint(imapObj.list_folders())
[(('\\\HasNoChildren',), '/', 'Drafts'),
(('\\\HasNoChildren',), '/', 'Filler'),
(('\\\HasNoChildren',), '/', 'INBOX'),
(('\\\HasNoChildren',), '/', 'Sent'),
--snip--
(('\\\HasNoChildren', '\\\Flagged'), '/', '[Gmail]/Starred'),
(('\\\HasNoChildren', '\\\Trash'), '/', '[Gmail]/Trash')]
如果你有一個(gè)Gmail賬戶,這就是輸出可能的樣子(Gmail將文件夾稱為label,但它們的工作方式與文件夾相同)。每個(gè)元組的三個(gè)值,例如 ((‘\HasNoChildren’,), ‘/‘, ‘INBOX’),解釋如下:
要選擇一個(gè)文件夾進(jìn)行搜索,就調(diào)用IMAPClient對(duì)象的select_folder()方法,傳入該文件夾的名稱字符串。
>>> imapObj.select_folder('INBOX', readonly=True)
可以忽略select_folder()的返回值。如果所選文件夾不存在,Python會(huì)拋出imaplib.error異常。
readonly=True關(guān)鍵字參數(shù)可以防止你在隨后的方法調(diào)用中,不小心更改或刪除該文件夾中的任何電子郵件。除非你想刪除的電子郵件,否則將readonly設(shè)置為T(mén)rue總是個(gè)好主意。
文件夾選中后,就可以用IMAPClient對(duì)象的search()方法搜索電子郵件。search()的參數(shù)是一個(gè)字符串列表,每一個(gè)格式化為IMAP搜索鍵。表16-3介紹了各種搜索鍵。
表16-3 IMAP搜索鍵
請(qǐng)注意,在處理標(biāo)志和搜索鍵方面,某些IMAP服務(wù)器的實(shí)現(xiàn)可能稍有不同。可能需要在交互式環(huán)境中試驗(yàn)一下,看看它們實(shí)際的行為如何。
在傳入search()方法的列表參數(shù)中,可以有多個(gè)IMAP搜索鍵字符串。返回的消息將匹配所有的搜索鍵。如果想匹配任何一個(gè)搜索鍵,使用OR搜索鍵。對(duì)于NOT和OR搜索鍵,它們后邊分別跟著一個(gè)和兩個(gè)完整的搜索鍵。
下面是search()方法調(diào)用的一些例子,以及它們的含義:
imapObj.search([‘ALL’]) 返回當(dāng)前選定的文件夾中的每一個(gè)消息。
imapObj.search([‘ON 05-Jul-2015’])返回在2015年7月5日發(fā)送的每個(gè)消息。
imapObj.search([‘SINCE 01-Jan-2015’, ‘BEFORE 01-Feb-2015’, ‘UNSEEN’])返回2015年1月發(fā)送的所有未讀消息(注意,這意味著從1月1日直到2月1日,但不包括2月1日)。
imapObj.search([‘SINCE 01-Jan-2015’, ‘FROM alice@example.com’])返回自2015年開(kāi)始以來(lái),發(fā)自alice@example.com的消息。
imapObj.search([‘SINCE 01-Jan-2015’, ‘NOT FROM alice@example.com’])返回自2015年開(kāi)始以來(lái),除alice@example.com外,其他所有人發(fā)來(lái)的消息。
imapObj.search([‘OR FROM alice@example.com FROM bob@example.com’])返回發(fā)自alice@example.com或bob@example.com的所有信息。
imapObj.search([‘FROM alice@example.com’, ‘FROM bob@example.com’])惡作劇例子!該搜索不會(huì)返回任何消息,因?yàn)橄⒈仨毱ヅ渌兴阉麝P(guān)鍵詞。因?yàn)橹荒苡幸粋€(gè)“from”地址,所以一條消息不可能既來(lái)自alice@example.com,又來(lái)自bob@example.com。
search()方法不返回電子郵件本身,而是返回郵件的唯一整數(shù)ID(UID)。然后,可以將這些UID傳入fetch()方法,獲得郵件內(nèi)容。
輸入以下代碼,繼續(xù)交互式環(huán)境的例子:
>>> UIDs=imapObj.search(['SINCE 05-Jul-2015'])
>>> UIDs
[40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040, 40041]
這里,search()返回的消息ID列表(針對(duì)7月5日以來(lái)接收的消息)保存在UIDs中。計(jì)算機(jī)上返回的UIDs列表與這里顯示的不同,它們對(duì)于特定的電子郵件賬戶是唯一的。如果你稍后將UID傳遞給其他函數(shù)調(diào)用,請(qǐng)用你收到的UID值,而不是本書(shū)例子中打印的。
如果你的搜索匹配大量的電子郵件,Python可能拋出異常imaplib.error: got more than 10000 bytes。如果發(fā)生這種情況,必須斷開(kāi)并重連IMAP服務(wù)器,然后再試。
這個(gè)限制是防止Python程序消耗太多內(nèi)存。遺憾的是,默認(rèn)大小限制往往太小。可以執(zhí)行下面的代碼,將限制從10000字節(jié)改為10000000字節(jié):
>>> import imaplib
>>> imaplib._MAXLINE=10000000
這應(yīng)該能避免該錯(cuò)誤消息再次出現(xiàn)。也許要在你寫(xiě)的每一個(gè)IMAP程序中加上這兩行。
得到UID的列表后,可以調(diào)用IMAPClient對(duì)象的fetch()方法,獲得實(shí)際的電子郵件內(nèi)容。
UID列表是fetch()的第一個(gè)參數(shù)。第二個(gè)參數(shù)應(yīng)該是[‘BODY[]’],它告訴fetch()下載UID列表中指定電子郵件的所有正文內(nèi)容。
{使用IMAPClient的gmail_search()方法!!}
如果登錄到imap.gmail.com服務(wù)器來(lái)訪問(wèn)Gmail賬戶,IMAPClient對(duì)象提供了一個(gè)額外的搜索函數(shù),模擬Gmail網(wǎng)頁(yè)頂部的搜索欄,如圖16-1中高亮的部分所示。
除了用IMAP搜索鍵搜索,可以使用Gmail更先進(jìn)的搜索引擎。Gmail在匹配密切相關(guān)的單詞方面做得很好(例如,搜索driving也會(huì)匹配drive和drove),并按照匹配的程度對(duì)搜索結(jié)果排序。也可以使用Gmail的高級(jí)搜索操作符(更多信息請(qǐng)參見(jiàn)http://nostarch.com/automatestuff/)。如果登錄到Gmail賬戶,向gmail_search()方法傳入搜索條件,而不是search()方法,就像下面交互式環(huán)境的例子:
>>> UIDs=imapObj.gmail_search('meaning of life')
>> UIDs
[42]
啊,是的,那封電子郵件包含了生命的意義!我一直在期待。
圖16-1 在Gmail網(wǎng)頁(yè)頂部的搜索欄
讓我們繼續(xù)交互式環(huán)境的例子。
>>> rawMessages=imapObj.fetch(UIDs, ['BODY[]'])
>>> import pprint
>>> pprint.pprint(rawMessages)
{40040: {'BODY[]': 'Delivered-To: my_email_address@gmail.com\r\n'
'Received: by 10.76.71.167 with SMTP id '
--snip--
'\r\n'
'------=_Part_6000970_707736290.1404819487066--\r\n',
'SEQ': 5430}}
導(dǎo)入 pprint,將 fetch()的返回值(保存在變量 rawMessages 中)傳入pprint.pprint(),“漂亮打印”它。你會(huì)看到,這個(gè)返回值是消息的嵌套字典,其中以UID作為鍵。每條消息都保存為一個(gè)字典,包含兩個(gè)鍵:’BODY[]’和’SEQ’。’BODY[]’鍵映射到電子郵件的實(shí)際正文。’SEQ’鍵是序列號(hào),它與UID的作用類(lèi)似。你可以放心地忽略它。
正如你所看到的,在’BODY[]’鍵中的消息內(nèi)容是相當(dāng)難理解的。這種格式稱為RFC822,是專為IMAP服務(wù)器讀取而設(shè)計(jì)的。但你并不需要理解RFC 822格式,本章稍后的pyzmail模塊將替你來(lái)理解它。
如果你選擇一個(gè)文件夾進(jìn)行搜索,就用readonly=True關(guān)鍵字參數(shù)來(lái)調(diào)用select_ folder()。這樣做可以防止意外刪除電子郵件,但這也意味著你用fetch()方法獲取郵件時(shí),它們不會(huì)標(biāo)記為已讀。如果確實(shí)希望在獲取郵件時(shí)將它們標(biāo)記已讀,就需要將readonly=False傳入select_folder()。如果所選文件夾已處于只讀模式,可以用另一個(gè) select_folder()調(diào)用重新選擇當(dāng)前文件夾,這次用readonly=False關(guān)鍵字參數(shù):
>>> imapObj.select_folder('INBOX', readonly=False)
對(duì)于只想讀郵件的人來(lái)說(shuō),fetch()方法返回的原始消息仍然不太有用。pyzmail模塊解析這些原始消息,將它們作為PyzMessage對(duì)象返回,使郵件的主題、正文、“收件人”字段、“發(fā)件人”字段和其他部分能用Python代碼輕松訪問(wèn)。
用下面的代碼繼續(xù)交互式環(huán)境的例子(使用你自己的郵件賬戶的UID,而不是這里顯示的):
>>> import pyzmail>>> message=pyzmail.PyzMessage.factory(rawMessages[40041]['BODY[]'])
首先,導(dǎo)入pyzmail。然后,為了創(chuàng)建一個(gè)電子郵件的PyzMessage對(duì)象,調(diào)用pyzmail.PeekMessage.factory()函數(shù),并傳入原始郵件的’BODY[]’部分。結(jié)果保存在message中。現(xiàn)在,message中包含一個(gè)PyzMessage對(duì)象,它有幾個(gè)方法,可以很容易地獲得的電子郵件主題行,以及所有發(fā)件人和收件人的地址。get_subject()方法將主題返回為一個(gè)簡(jiǎn)單字符串。get_addresses()方法針對(duì)傳入的字段,返回一個(gè)地址列表。例如,該方法調(diào)用可能像這樣:
>>> message.get_subject()
'Hello!'
>>> message.get_addresses('from')
[('Edward Snowden', 'esnowden@nsa.gov')]
>>> message.get_addresses('to')
[(Jane Doe', 'my_email_address@gmail.com')]
>>> message.get_addresses('cc')
[]
>>> message.get_addresses('bcc')
[]
請(qǐng)注意,get_addresses()的參數(shù)是’from’、’to’、’cc’或 ‘bcc’。get_addresses()的返回值是一個(gè)元組列表。每個(gè)元組包含兩個(gè)字符串:第一個(gè)是與該電子郵件地址關(guān)聯(lián)的名稱,第二個(gè)是電子郵件地址本身。如果請(qǐng)求的字段中沒(méi)有地址,get_addresses()返回一個(gè)空列表。在這里,’cc’抄送和’bcc’密件抄送字段都沒(méi)有包含地址,所以返回空列表。
電子郵件可以是純文本、HTML 或兩者的混合。純文本電子郵件只包含文本,而HTML電子郵件可以有顏色、字體、圖像和其他功能,使得電子郵件看起來(lái)像一個(gè)小網(wǎng)頁(yè)。如果電子郵件僅僅是純文本,它的PyzMessage對(duì)象會(huì)將html_part屬性設(shè)為None。同樣,如果電子郵件只是HTML,它的PyzMessage對(duì)象會(huì)將text_part屬性設(shè)為None。
否則,text_part或html_part將有一個(gè)get_payload()方法,將電子郵件的正文返回為bytes數(shù)據(jù)類(lèi)型(bytes數(shù)據(jù)類(lèi)型超出了本書(shū)的范圍)。但是,這仍然不是我們可以使用的字符串。啊!最后一步對(duì)get_payload()返回的bytes值調(diào)用decode()方法。decode()方法接受一個(gè)參數(shù):這條消息的字符編碼,保存在text_part.charset或html_part.charset屬性中。最后,這返回了郵件正文的字符串。
輸入以下代碼,繼續(xù)交互式環(huán)境的例子:
? >>> message.text_part !=None
True
>>> message.text_part.get_payload().decode(message.text_part.charset)
? 'So long, and thanks for all the fish!\r\n\r\n-Al\r\n'
? >>> message.html_part !=None
True
? >>> message.html_part.get_payload().decode(message.html_part.charset)
'< div dir="ltr">< div>So long, and thanks for all the fish!< br>< br>< /div>-Al
< br>< /div>\r\n'
我們正在處理的電子郵件包含純文本和HTML內(nèi)容,因此保存在message中的PyzMessage對(duì)象的text_part和html_part屬性不等于None??。對(duì)消息的text_part調(diào)用get_payload(),然后在bytes值上調(diào)用decode(),返回電子郵件的文本版本的字符串?。對(duì)消息的html_part調(diào)用get_payload()和decode(),返回電子郵件的HTML版本的字符串?。
要?jiǎng)h除電子郵件,就向IMAPClient對(duì)象的delete_messages()方法傳入一個(gè)消息UID的列表。這為電子郵件加上\Deleted標(biāo)志。調(diào)用expunge()方法,將永久刪除當(dāng)前選中的文件夾中帶\Deleted標(biāo)志的所有電子郵件。請(qǐng)看下面的交互式環(huán)境的例子:
? >>> imapObj.select_folder('INBOX', readonly=False)
? >>> UIDs=imapObj.search(['ON 09-Jul-2015'])
>>> UIDs
[40066]
>>> imapObj.delete_messages(UIDs)
? {40066: ('\\\Seen', '\\\Deleted')}
>>> imapObj.expunge()
('Success', [(5452, 'EXISTS')])
這里,我們調(diào)用了IMAPClient對(duì)象的select_folder()方法,傳入’INBOX’作為第一個(gè)參數(shù),選擇了收件箱。我們也傳入了關(guān)鍵字參數(shù)readonly=False,這樣我們就可以刪除電子郵件?。我們搜索收件箱中的特定日期收到的消息,將返回的消息ID保存在UIDs中?。調(diào)用delete_message()并傳入U(xiǎn)IDs,返回一個(gè)字典,其中每個(gè)鍵值對(duì)是一個(gè)消息 ID 和消息標(biāo)志的元組,它現(xiàn)在應(yīng)該包含\Deleted標(biāo)志?。然后調(diào)用expunge(),永久刪除帶\Deleted標(biāo)志的郵件。如果清除郵件沒(méi)有問(wèn)題,就返回一條成功信息。請(qǐng)注意,一些電子郵件提供商,如Gmail,會(huì)自動(dòng)清除用delete_messages()刪除的電子郵件,而不是等待來(lái)自IMAP客戶端的expunge命令。
如果程序已經(jīng)完成了獲取和刪除電子郵件,就調(diào)用IMAPClient的logout()方法,從IMAP服務(wù)器斷開(kāi)連接。
>>> imapObj.logout()
如果程序運(yùn)行了幾分鐘或更長(zhǎng)時(shí)間,IMAP服務(wù)器可能會(huì)超時(shí),或自動(dòng)斷開(kāi)。在這種情況下,接下來(lái)程序?qū)MAPClient對(duì)象的方法調(diào)用會(huì)拋出異常,像下面這樣:
imaplib.abort: socket error: [WinError 10054] An existing connection was
forcibly closed by the remote host
在這種情況下,程序必須調(diào)用imapclient.IMAPClient(),再次連接。
喲!齊活了。要跳過(guò)很多圈圈,但你現(xiàn)在有辦法讓Python程序登錄到一個(gè)電子郵件賬戶,并獲取電子郵件。需要回憶所有步驟時(shí),你可以隨時(shí)參考16.4節(jié)“用IMAP獲取和刪除電子郵件”。
假定你一直“自愿”為“強(qiáng)制自愿俱樂(lè)部”記錄會(huì)員會(huì)費(fèi)。這確實(shí)是一項(xiàng)枯燥的工作,包括維護(hù)一個(gè)電子表格,記錄每個(gè)月誰(shuí)交了會(huì)費(fèi),并用電子郵件提醒那些沒(méi)交的會(huì)員。不必你自己查看電子表格,而是向會(huì)費(fèi)超期的會(huì)員復(fù)制和粘貼相同的電子郵件。你猜對(duì)了,讓我們編寫(xiě)一個(gè)腳本,幫你完成任務(wù)。
在較高的層面上,下面是程序要做的事:
這意味著代碼需要做到以下幾點(diǎn):
打開(kāi)一個(gè)新的文件編輯器窗口,并保存為sendDuesReminders.py。
假定用來(lái)記錄會(huì)費(fèi)支付的 Excel 電子表格看起來(lái)如圖 16-2 所示,放在名為duesRecords.xlsx的文件中。可以從http://nostarch.com/automatestuff/下載該文件。
圖16-2 記錄會(huì)員會(huì)費(fèi)支付電子表格
該電子表格中包含每個(gè)成員的姓名和電子郵件地址。每個(gè)月有一列,記錄會(huì)員的付款狀態(tài)。在成員交納會(huì)費(fèi)后,對(duì)應(yīng)的單元格就記為paid。
該程序必須打開(kāi)duesRecords.xlsx,通過(guò)調(diào)用get_highest_column()方法,弄清楚最近一個(gè)月的列(可以參考第12章,了解用openpyxl模塊訪問(wèn)Excel電子表格文件單元格的更多信息)。在文件編輯器窗口中輸入以下代碼:
#! python3
# sendDuesReminders.py - Sends emails based on payment status in spreadsheet.
import openpyxl, smtplib, sys
# Open the spreadsheet and get the latest dues status.
? wb=openpyxl.load_workbook('duesRecords.xlsx')
? sheet=wb.get_sheet_by_name('Sheet1')
? lastCol=sheet.get_highest_column()
? latestMonth=sheet.cell(row=1, column=lastCol).value
# TODO: Check each member's payment status.
# TODO: Log in to email account.
# TODO: Send out reminder emails.
導(dǎo)入openpyxl、smtplib和sys模塊后,我們打開(kāi)duesRecords.xlsx文件,將得到的Workbook對(duì)象保存在wb中?。然后,取得Sheet 1,將得到的Worksheet對(duì)象保存在sheet中?。既然有了Worksheet對(duì)象,就可以訪問(wèn)行、列和單元格。我們將最后一列保存在lastCol中?,然后用行號(hào)1和lastCol來(lái)訪問(wèn)應(yīng)該記錄著最近月份的單元格。取得該單元格的值,并保存在latestMonth 中?。
一旦確定了最近一個(gè)月的列數(shù)(保存在lastCol中),就可以循環(huán)遍歷第一行(這是列標(biāo)題)之后的所有行,看看哪些成員在該月會(huì)費(fèi)的單元格中寫(xiě)著paid。如果會(huì)員沒(méi)有支付,就可以從列1和2中分別抓取成員的姓名和電子郵件地址。這些信息將放入unpaidMembers字典,它記錄最近一個(gè)月沒(méi)有交費(fèi)的所有成員。將以下代碼添加到sendDuesReminder.py中。
#! python3
# sendDuesReminders.py - Sends emails based on payment status in spreadsheet.
--snip--
# Check each member's payment status.
unpaidMembers={}
? for r in range(2, sheet.get_highest_row() + 1):
? payment=sheet.cell(row=r, column=lastCol).value
if payment !='paid':
? name=sheet.cell(row=r, column=1).value
? email=sheet.cell(row=r, column=2).value
? unpaidMembers[name]=email
這段代碼設(shè)置了一個(gè)空字典unpaidMembers,然后循環(huán)遍歷第一行之后所有的行?。對(duì)于每一行,最近月份的值保存在payment中?。如果payment不等于’paid’,則第一列的值保存在name中?,第二列的值保存在email中?,name和email添加到unpaidMembers中?。
得到所有未付費(fèi)成員的名單后,就可以向他們發(fā)送電子郵件提醒了。將下面的代碼添加到程序中,但要代入你的真實(shí)電子郵件地址和提供商的信息:
#! python3
# sendDuesReminders.py - Sends emails based on payment status in spreadsheet.
--snip--
# Log in to email account.
smtpObj=smtplib.SMTP('smtp.gmail.com', 587)
smtpObj.ehlo()
smtpObj.starttls()
smtpObj.login('my_email_address@gmail.com', sys.argv[1])
調(diào)用smtplib.SMTP()并傳入提供商的域名和端口,創(chuàng)建一個(gè)SMTP對(duì)象。調(diào)用ehlo()和starttls(),然后調(diào)用login(),并傳入你的電子郵件地址和sys.argv[1],其中保存著你的密碼字符串。在每次運(yùn)行程序時(shí),將密碼作為命令行參數(shù)輸入,避免在源代碼中保存密碼。
程序登錄到你的電子郵件賬戶后,就應(yīng)該遍歷unpaidMembers字典,向每個(gè)會(huì)員的電子郵件地址發(fā)送針對(duì)個(gè)人的電子郵件。將以下代碼添加到sendDuesReminders.py:
#! python3
# sendDuesReminders.py - Sends emails based on payment status in spreadsheet.
--snip--
# Send out reminder emails.
for name, email in unpaidMembers.items():
? body="Subject: %s dues unpaid.\nDear %s,\nRecords show that you have not
paid dues for %s. Please make this payment as soon as possible. Thank you!'" %
(latestMonth, name, latestMonth)
? print('Sending email to %s...' % email)
? sendmailStatus=smtpObj.sendmail('my_email_address@gmail.com', email, body)
? if sendmailStatus !={}:
print('There was a problem sending email to %s: %s' % (email,
sendmailStatus))
smtpObj.quit()
這段代碼循環(huán)遍歷unpaidMembers中的姓名和電子郵件。對(duì)于每個(gè)沒(méi)有付費(fèi)的成員,我們用最新的月份和成員的名稱,定制了一條消息,并保存在body中?。我們打印輸出,表示正在向這個(gè)會(huì)員的電子郵件地址發(fā)送電子郵件?。然后調(diào)用sendmail(),向它傳入地址和定制的消息?。返回值保存在sendmailStatus中。
回憶一下,如果SMTP服務(wù)器在發(fā)送某個(gè)電子郵件時(shí)報(bào)告錯(cuò)誤,sendmail()方法將返回一個(gè)非空的字典值。for循環(huán)最后部分在?行檢查返回的字典是否非空,如果非空,則打印收件人的電子郵件地址以及返回的字典。
程序完成發(fā)送所有電子郵件后,調(diào)用quit()方法,與SMTP服務(wù)器斷開(kāi)連接。
如果運(yùn)行該程序,輸出會(huì)像這樣:
Sending email to alice@example.com...
Sending email to bob@example.com...
Sending email to eve@example.com...
收件人將收到如圖16-3所示的電子郵件。
圖16-3 從sendDuesReminders.py自動(dòng)發(fā)送的電子郵件
大多數(shù)人更可能靠近自己的手機(jī),而不是自己的電腦,所以與電子郵件相比,短信發(fā)送通知可能更直接、可靠。此外,短信的長(zhǎng)度較短,讓人更有可能閱讀它們。
在本節(jié)中,你將學(xué)習(xí)如何注冊(cè)免費(fèi)的Twilio服務(wù),并用它的Python模塊發(fā)送短信。Twilio是一個(gè)SMS網(wǎng)關(guān)服務(wù),這意味著它是一種服務(wù),讓你通過(guò)程序發(fā)送短信。雖然每月發(fā)送多少短信會(huì)有限制,并且文本前面會(huì)加上Sent from a Twilio trial account,但這項(xiàng)試用服務(wù)也許能滿足你的個(gè)人程序。免費(fèi)試用沒(méi)有限期,不必以后升級(jí)到付費(fèi)的套餐。
Twilio不是唯一的SMS網(wǎng)關(guān)服務(wù)。如果你不喜歡使用Twilio,可以在線搜索free sms gateway、python sms api,甚至twilio alternatives,尋找替代服務(wù)。
注冊(cè)Twilio賬戶之前,先安裝twilio模塊。附錄A詳細(xì)介紹了如何安裝第三方模塊。
本節(jié)特別針對(duì)美國(guó)。Twilio 確實(shí)也在美國(guó)以外的國(guó)家提供手機(jī)短信服務(wù),本書(shū)并不包括這些細(xì)節(jié)。但twilio 模塊及其功能,在美國(guó)以外的國(guó)家也能用。更多信息請(qǐng)參見(jiàn)http://twilio.com/。
訪問(wèn)http://twilio.com/并填寫(xiě)注冊(cè)表單。注冊(cè)了新賬戶后,你需要驗(yàn)證一個(gè)手機(jī)號(hào)碼,短信將發(fā)給該號(hào)碼(這項(xiàng)驗(yàn)證是必要的,防止有人利用該服務(wù)向任意的手機(jī)號(hào)碼發(fā)送垃圾短信)。
收到驗(yàn)證號(hào)碼短信后,在Twilio網(wǎng)站上輸入它,證明你擁有要驗(yàn)證的手機(jī)。現(xiàn)在,就可以用twilio模塊向這個(gè)電話號(hào)碼發(fā)送短信了。
Twilio提供的試用賬戶包括一個(gè)電話號(hào)碼,它將作為短信的發(fā)送者。你將需要兩個(gè)信息:你的賬戶SID和AUTH(認(rèn)證)標(biāo)志。在登錄Twilio賬戶時(shí),可以在Dashboard頁(yè)面上找到這些信息。從Python程序登錄時(shí),這些值將作為你的Twilio用戶名和密碼。
一旦安裝了twilio模塊,注冊(cè)了Twilio賬號(hào),驗(yàn)證了你的手機(jī)號(hào)碼,登記了Twilio電話號(hào)碼,獲得了賬戶的SID和auth標(biāo)志,你就終于準(zhǔn)備好通過(guò)Python腳本向你自己發(fā)短信了。
與所有的注冊(cè)步驟相比,實(shí)際的Python代碼很簡(jiǎn)單。保持計(jì)算機(jī)連接到因特網(wǎng),在交互式環(huán)境中輸入以下代碼,用你的真實(shí)信息替換accountSID、authToken、myTwilioNumber和myCellPhone變量的值:
? >>> from twilio.rest import TwilioRestClient
>>> accountSID='ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
>>> authToken='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
? >>> twilioCli=TwilioRestClient(accountSID, authToken)
>>> myTwilioNumber='+14955551234'
>>> myCellPhone='+14955558888'
? >>> message=twilioCli.messages.create(body='Mr. Watson - Come here - I want
to see you.', from_=myTwilioNumber, to=myCellPhone)
鍵入最后一行后不久,你會(huì)收到一條短信,內(nèi)容為:Sent from your Twilio trial account - Mr. Watson - Come here – I want to see you.。
因?yàn)閠wilio模塊的設(shè)計(jì)方式,導(dǎo)入它時(shí)需要使用from twilio.rest import TwilioRestClient,而不僅僅是import twilio?。將賬戶的SID保存在accountSID,認(rèn)證標(biāo)志保存在authToken中,然后調(diào)用TwilioRestClient(),并傳入accountSID和authToken。TwilioRestClient()調(diào)用返回一個(gè)TwilioRestClient對(duì)象?。該對(duì)象有一個(gè)message屬性,該屬性又有一個(gè)create()方法,可以用來(lái)發(fā)送短信。正是這個(gè)方法,將告訴Twilio的服務(wù)器發(fā)送短信。將你的Twilio號(hào)碼和手機(jī)號(hào)碼分別保存在myTwilioNumber和myCellPhone中,然后調(diào)用create(),傳入關(guān)鍵字參數(shù),指明短信的正文、發(fā)件人的號(hào)碼(myTwilioNumber),以及收信人的電話號(hào)碼(myCellPhone)?
create()方法返回的Message對(duì)象將包含已發(fā)送短信的相關(guān)信息。輸入以下代碼,繼續(xù)交互式環(huán)境的例子:
>>> message.to
'+14955558888'
>>> message.from_
'+14955551234'
>>> message.body
'Mr. Watson - Come here - I want to see you.'
to、from和body屬性應(yīng)該分別保存了你的手機(jī)號(hào)碼、Twilio號(hào)碼和消息。請(qǐng)注意,發(fā)送手機(jī)號(hào)碼是在from屬性中,末尾有一個(gè)下劃線,而不是from。這是因?yàn)閒rom是一個(gè)Python關(guān)鍵字(例如,你在from modulename import *形式的import語(yǔ)句中見(jiàn)過(guò)它),所以它不能作為一個(gè)屬性名。輸入以下代碼,繼續(xù)交互式環(huán)境的例子:
>>> message.status
'queued'
>>> message.date_created
datetime.datetime(2015, 7, 8, 1, 36, 18)
>>> message.date_sent==None
True
status 屬性應(yīng)該包含一個(gè)字符串。如果消息被創(chuàng)建和發(fā)送,date_created 和date_sent屬性應(yīng)該包含一個(gè)datetime對(duì)象。如果已收到短信,而status屬性卻設(shè)置為’queued’,date_sent屬性設(shè)置為None,這似乎有點(diǎn)奇怪。這是因?yàn)槟阆葘essage對(duì)象記錄在message變量中,然后短信才實(shí)際發(fā)送。你需要重新獲取Message對(duì)象,查看它最新的status和date_sent。每個(gè)Twilio消息都有唯一的字符串ID(SID),可用于獲取Message對(duì)象的最新更新。輸入以下代碼,繼續(xù)交互式環(huán)境的例子:
>>> message.sid
'SM09520de7639ba3af137c6fcb7c5f4b51'
? >>> updatedMessage=twilioCli.messages.get(message.sid)
>>> updatedMessage.status
'delivered'
>>> updatedMessage.date_sent
datetime.datetime(2015, 7, 8, 1, 36, 18)
輸入message.sid將顯示這個(gè)消息的SID。將這個(gè)SID傳入Twilio客戶端的get()方法?,你可以取得一個(gè)新的Message對(duì)象,包含最新的信息。在這個(gè)新的Message對(duì)象中,status和date_sent屬性是正確的。
status屬性將設(shè)置為下列字符串之一:’queued’、’sending’、’sent’、’delivered’、’undelivered’或’failed’。這些狀態(tài)不言自明,但對(duì)于更準(zhǔn)確的細(xì)節(jié),請(qǐng)查看http://nostarch. com/automatestuff/的資源。
{用Python接收短信!!}
遺憾的是,用Twilio接收短信比發(fā)送短信更復(fù)雜一些。Twilio需要你有一個(gè)網(wǎng)站,運(yùn)行自己的Web應(yīng)用程序。這已超出了本書(shū)的范圍,但你可以在本書(shū)的資源中找到更多細(xì)節(jié)(http://nostarch.com/automatestuff/)。
最常用你的程序發(fā)短信的人可能就是你。當(dāng)你遠(yuǎn)離計(jì)算機(jī)時(shí),短信是通知你自己的好方式。如果你已經(jīng)用程序自動(dòng)化了一個(gè)無(wú)聊的任務(wù),它需要運(yùn)行幾小時(shí),你可以在它完成時(shí),讓它用短信通知你。或者可以定期運(yùn)行某個(gè)程序,它有時(shí)需要與你聯(lián)系,例如天氣檢查程序,用短信提醒你帶傘。
舉一個(gè)簡(jiǎn)單的例子,下面是一個(gè)Python小程序,包含了textmyself()函數(shù),它將傳入的字符串參數(shù)作為短信發(fā)出。打開(kāi)一個(gè)新的文件編輯器窗口,輸入以下代碼,用自己的信息替換帳戶SID,認(rèn)證標(biāo)志和電話號(hào)碼。將它保存為textMyself.py。
#! python3
# textMyself.py - Defines the textmyself() function that texts a message
# passed to it as a string.
# Preset values:
accountSID='ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
authToken='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
myNumber='+15559998888'
twilioNumber='+15552225678'
from twilio.rest import TwilioRestClient
? def textmyself(message):
? twilioCli=TwilioRestClient(accountSID, authToken)
? twilioCli.messages.create(body=message, from_=twilioNumber, to=myNumber)
該程序保存了賬戶的SID、認(rèn)證標(biāo)志、發(fā)送號(hào)碼及接收號(hào)碼。然后它定義了textmyself(),接收參數(shù)?,創(chuàng)建TwilioRestClient對(duì)象?,并用你傳入的消息調(diào)用create()?。
如果你想讓其他程序使用textmyself()函數(shù),只需將textMyself.py文件和Python的可執(zhí)行文件放在同一個(gè)文件夾中(Windows上是C:\Python34,OS X上是/usr/local/lib/python3.4,Linux上是/usr/bin/python3)。現(xiàn)在,你可以在其他程序中使用該函數(shù)。只要想在程序中發(fā)短信給你,就添加以下代碼:
import textmyself
textmyself.textmyself('The boring task is finished.')
注冊(cè)Twilio和編寫(xiě)短信代碼只要做一次。在此之后,從任何其他程序中發(fā)短信,只要兩行代碼。
通過(guò)因特網(wǎng)和手機(jī)網(wǎng)絡(luò),我們用幾十種不同的方式相互通信,但以電子郵件和短信為主。你的程序可以通過(guò)這些渠道溝通,這給它們帶來(lái)強(qiáng)大的新通知功能。甚至可以編程運(yùn)行在不同的計(jì)算機(jī)上,相互直接通過(guò)電子郵件能信,一個(gè)程序用SMTP發(fā)送電子郵件,另一個(gè)用IMAP收取。
Python 的 smtplib 提供了一些函數(shù),利用 SMTP,通過(guò)電子郵件提供商的SMTP服務(wù)器發(fā)送電子郵件。同樣,第三方的imapclient和pyzmail模塊讓你訪問(wèn)IMAP服務(wù)器,并取回發(fā)送給你的電子郵件。雖然IMAP比SMTP復(fù)雜一些,但它也相當(dāng)強(qiáng)大,允許你搜索特定電子郵件、下載它們、解析它們,提取主題和正文作為字符串值。
短信與電子郵件有點(diǎn)不同,因?yàn)樗幌耠娮余]件,發(fā)送短信不僅需要互聯(lián)網(wǎng)連接。好在,像Twilio這樣的服務(wù)提供了模塊,允許你通過(guò)程序發(fā)送短信。一旦通過(guò)了初始設(shè)置過(guò)程,就能夠只用幾行代碼來(lái)發(fā)送短信。掌握了這些模塊,就可以針對(duì)特定的情況編程,在這些情況下發(fā)送通知或提醒。現(xiàn)在,你的程序?qū)⒊竭\(yùn)行它們的計(jì)算機(jī)!
本文摘自《Python編程快速上手 讓繁瑣工作自動(dòng)化》
本書(shū)是一本面向?qū)嵺`的Python編程實(shí)用指南。本書(shū)的目的,不僅是介紹Python語(yǔ)言的基礎(chǔ)知識(shí),而且還通過(guò)項(xiàng)目實(shí)踐教會(huì)讀者如何應(yīng)用這些知識(shí)和技能。本書(shū)的第一部分介紹了基本的Python編程概念,第二部分介紹了一些不同的任務(wù),通過(guò)編寫(xiě)Python程序,可以讓計(jì)算機(jī)自動(dòng)完成它們。第二部分的每一章都有一些項(xiàng)目程序,供讀者學(xué)習(xí)。每章的末尾還提供了一些習(xí)題和深入的實(shí)踐項(xiàng)目,幫助讀者鞏固所學(xué)的知識(shí)。附錄部分提供了所有習(xí)題的解答。
本書(shū)適合任何想要通過(guò)Python學(xué)習(xí)編程的讀者,尤其適合缺乏編程基礎(chǔ)的初學(xué)者。通過(guò)閱讀本書(shū),讀者將能利用最強(qiáng)大的編程語(yǔ)言和工具,并且將體會(huì)到Python編程的快樂(lè)。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。