使用Python脚本来收发Gmail, Say no to GFW
学校里面的网络环境一直令人不快,天天喊着要建立国际一流大学,就校园网这质量只能是下辈子的事了,再加上国内目前对Google方面的不友好,我会经常性的不能正常通过浏览器连接到我的Gmail邮箱,捉急的时候真是很后悔当初选择了Gmail,虽然它看起来又高端又时尚又有Geek风范。对Outlook和Mac上自带Mail软件的失望让我有了这一想法,本文里面说到这个版本还处于初级阶段,不过应应急看邮件回复紧急事情肯定是够用了。
废话不多说直接进入正题了,本文将要告诉你的事情是,如果你也想我一样需要一个应急的接收Gmail的小插件,这篇文章可以帮助你在数分钟之内搭建一个收发Gmail的小平台,当然前提是你也和我一样喜欢使用CLI的形式与你的计算机交互。
首先我们先来看看接收邮件的part
如果你想敲几个命令就连上你的邮箱大致需要下面的几个步骤:第一,你要与你的邮箱服务器建立一个成功的连接,当然我使用的是IMAP的协议,个人觉着比POP更好,当然人人都有自己的喜好,这也不是啥关键的事情。第二,在连接成功建立的基础上你要使用自己的帐号和口令成功登陆,也就是去的认证获取对该用户邮箱的操作权。第三,在前两者都OK的基础上你可以开始搞一些事情了,例如获取目前的邮箱一些基本情况(邮件总数,未读数量,已读数量,容量情况),在此基础上你就可以读取某一封邮件了(目前在带有附件,HTML元素,图片的邮件上还比较无力)。
虽然看起来功能弱爆了,但是对我来讲已经足够了,想象一下一个焦急等待面试结果或者等待女朋友是否同意求婚而苦守在邮箱门前却因为GFW的缘故无法得知结果的可怜人的痛苦心情吧!代码的实现大概是这样子的(只贴上关键部分,对于怎么组织所有代码是个太灵活的事情,谁让咱们有那么多模式呢)。
def __init__(self):
self.IMAP_SERVER='imap.gmail.com'
self.IMAP_PORT=993
self.M = None
self.respons
self.mailboxes = []
def login(self, username, password):
self.M = imaplib.IMAP4_SSL(self.IMAP_SERVER, self.IMAP_PORT)
rc, self.response = self.M.login(username, password)
return rc
这部分就是第一步和第二步所做到事情。
def receive_mail(self):
recvMail = receiveMail.ReceiveMail(self.M)
mailCounts = recvMail.get_mail_count()
print 'A total of '+ mailCounts +' mails in your input mailbox.'
print 'A total of '+recvMail.get_unread_count()+ ' UNREAD mails in your input mailbox.'
recvMail.get_imap_quota()
mailBody = recvMail.check_易做图Info(mailCounts) #返回值是邮件content
if mailBody != 0:
recvMail.check_detailInfo(mailBody)
return
上面的这些代码是出现在调用层的,主要是调用了具体实现功能的下面几个函数。
下面的这些则在receiveMail.py文件中。
def check_易做图Info(self, mailCounts):
print "Input 'y' to check the lasted UNread mail. Other cmds to abandon!"
while True:
argv = string.split(raw_input('$====>'))
if len(argv)!=0:
if argv[0] == 'y':
mailBody = self.get_mail_易做图Info_from_id(mailCounts)
return mailBody
else:
return 0
else:
pass
else:
pass
调用check_易做图Info()函数查阅某邮件的简略信息,包括邮件来自谁,发送时间,主题等。 调用check_detailInfo()函数来查看邮件的详细信息也就是邮件内容。当然都看得出来get_mail_易做图Info_from_id()才是主要进行了获取内容的工作,下面来看一下这个函数。
def get_mail_易做图Info_from_id(self, id):
status, response = self.M.fetch(id,"(RFC822)")
mailText = response[0][1]
mail_message = email.message_from_string(mailText)
subject = unicode(email.Header.make_header(email.Header.decode_header(mail_message['subject'])))
mail_from = email.utils.parseaddr(mail_message["from"])[1]
mail_to = email.utils.parseaddr(mail_message["to"])[1]
print '['+mail_message['Date']+']'+'\n'+'From:'+mail_from+ ' To:'+mail_to+'\n'+'Subject:'+subject+'\n'
return self.get_first_text_block(mail_message)
由于我们在工作中会使用到中文,所以在这里我们还需要关注两个有可能产生乱码的地方,一个是邮件头部分在代码中使用了email.Header对其进行解码,email.Header.decode_header(subject)对邮件主体可能出现的乱码进行了处理。关于此处stackoverflow上有一热心的哥们坚定的表示像以往那样只是简单的对他们进行解码是不够的,因为此处的mail_message['subject']有可能回返回多个实体,而不仅仅是一个,所以为了保证数据的完整采用了列表解析循环获取数据,他强烈推荐我使用subject = u''.join(unicode(strs,t) for strs,t in email.Header.decode_header(mail_message['subject'])),从这句代码可以看出python是多么省事儿的语言。但是这里还是不够严密的,那就是有些邮件在发送的时候没有指定明确的编码格式,这就导致了在一定的几率下,email.Header.decode_header()会返回None作为第二个返回值,会造成unicode()的失败,于是此大神在报告bug的时候推荐给我了上面的那句代码,subject = unicode(email.Header.make_header(email.Header.decode_header(mail_message['subject'])))。
显然get_first_text_block()函数对邮件的主要内容进行处理,用来判断这个作为参数传进来的邮件实体的主体类型是哪个?如果还是multipart类型还需要继续分解一下最后的目标就是将他们全部分解为text类型。
def get_first_text_block(self,email_message_instance):
maintype = email_message_instance.get_content_maintype()
if maintype == 'multipart':
for part in email_message_instance.get_payload():
if part.get_content_maintype() == 'text':
return part.get_payload(decode=True).strip()
elif maintype == 'text':
return email_message_instance.get_payload(decode=True).strip()
其中part.get_payload(decode=True)函数对解析的邮件主要内容进行了解码,避免将一些乱七八糟的mojibake直接显示在你的终端里。说到这里不能不再啰嗦一件事,虽然很不起眼但是却很容易再这里出问题,那就是你terminal的编码格式,如果你的编码格式不支持中文或者utf-8的画python会报错的,没错你没有看错,terminal的编码错误会造成python的崩溃,我就遇到了此问题,虽说Mac的terminal是支持各种编码的但是正巧我做这一部分的时候是在使用公司的Thinkpad,于是在window的CMD下就果断悲剧了,直接报了python的编码不能找到定义字符集(UnicodeDecodeError),所以此处是需要注意的。
下面的这个函数其实没有什么必要说的,将其单独拿出来完全是为了业务逻辑上的考虑,此函数中传入的参数就是get_first_text_block()函数的返回值,在调用层的时候将其赋值给了mailBody,可以参见上面的receive_mail函数。
def check_detailInfo(self, mailBody):
&
补充:Web开发 , Python ,