README
https://github.com/guoruibiao/worktools/edit/master/mailer/README.md

最终效果

sendmail.py -h
usage: sendmail.py [-h] [-s SENDER] [-p PASSWORD] [-H HOST] [-P PORT]
                   [-r RECEIVERS [RECEIVERS ...]] [-a ATTACHMENT] [-t TEXT]
                   [-S SUBJECT]

命令行邮件发送工具

optional arguments:
  -h, --help            show this help message and exit
  -s SENDER, --sender SENDER
                        邮件发送人邮件地址
  -p PASSWORD, --password PASSWORD
                        邮件发送人对应的密码
  -H HOST, --host HOST  邮件服务器主机
  -P PORT, --port PORT  邮件服务器端口,默认25
  -r RECEIVERS [RECEIVERS ...], --receivers RECEIVERS [RECEIVERS ...]
                        收件人列表,注意不能少于一个,而且多个收件人要用空格隔开
  -a ATTACHMENT, --attachment ATTACHMENT
                        附件的全路径,注意windows和linux上会稍有不同~, 多个附件的时候需要重复指定此参数~
  -t TEXT, --text TEXT  邮件正文部分,相当于纯文本模式
  -S SUBJECT, --subject SUBJECT
                        邮件主题

因为毕设的缘故,不能待在公司了。但是偶尔公司的活还是要做,基本上来说,给运营跑的数据都很小,几十上百行就能搞定了。然后自己复制粘贴即可。但是有时候跑的数据结果集贼大,动辄上万行,这个时候就不能使用复制粘贴了。由于外网限制,好多端口都是用不了的,所以通过HTTP服务的方式就放弃了。转而采用曲线救国的方式来实现,那就是使用“邮件”

由于我是后台开发,一般工作的时候需要先登录公司的一台跳板机,然后再从跳板机登录到对应的服务器。所以这样是没法使用GUI界面的邮件工具的。因此转而使用命令行版。详见下面的代码,不依赖标准库之外的其他库,所以可以很方便的进行移植。

# coding:utf8
import re
import os
import smtplib
import argparse
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication


class Mailer(object):
    """
    邮件工具箱, 用于发送通用文本邮件以及各种附件形式的邮件。
    """
    def __init__(self, username="spidersmall@163.com", password="", host="smtp.163.com", port=25):
        if self._checkMailAddress(mailaddress=username) == False:
            raise Exception("发件人邮件地址有误,当前值:{}".format(username))
        self.username = str(username)
        # 初始化收件人列表
        self.receivers = []
        self._login(str(username), str(password), str(host), int(port))
        self.message = MIMEMultipart()

    def _login(self, username, password, host, port):
        self.client = smtplib.SMTP()
        self.client.connect(host, port)
        self.client.login(username, password)

    def _checkMailAddress(self, mailaddress=""):
        if mailaddress == "" or mailaddress is None:
            raise Exception("邮箱地址为空,当前值:{}".format(mailaddress))
        if len(mailaddress) < 6:
            raise Exception("邮箱[{}]长度太少了吧,确定没有输入错误吗?".format(mailaddress))
        regpattern = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$"
        if re.match(re.compile(regpattern), mailaddress) is not None:
            return True
        else:
            return False

    def addReceiver(self, receiver=""):
        if not self._checkMailAddress(mailaddress=receiver):
            raise Exception("收件人邮箱格式有误,当前值:{}".format(receiver))
        self.receivers.append(receiver)

    def _getContent(self, filename="", mode="r"):
        """
        这里mode最重要,需要自己指定读文件的模式,像xlsx,mp3这种二进制的,一定要用rb;普通文件用r模式即可。
        """
        content = ""
        mode = str(mode).lower()
        if os.path.exists(filename) == False:
            raise Exception("附件路径不存在,请确认后再发送吧~ \n当前值: {}".format(filename))
        if mode == "r":
            with open(filename, mode, encoding='utf8') as f:
                content = f.read()
                f.close()
        elif mode == "rb":
            with open(filename, mode) as f:
                content = f.read()
                f.close()
        else:
            raise Exception("暂不支持的读文件模式,当前值:{}".format(mode))
        return content

    def addAttchment(self, filename=""):
        content = self._getContent(filename=filename, mode='rb')
        attachment = MIMEApplication(content)
        attachment.add_header("Content-Disposition", "attachment", filename=filename)
        self.message.attach(attachment)

    def addMailBody(self, filename=""):
        content = self._getContent(filename=filename, mode='r')
        mailbody = MIMEText(content)
        self.message.attach(mailbody)

    def addMailContent(self, content=""):
        if content is None or content == "":
            raise Exception("一句话邮件正文不能为空哈~")
        mailcontent = MIMEText(content)
        self.message.attach(mailcontent)

    def send(self, subject="忘了写标题~"):
        self.message['Subject'] = subject
        self.message["From"] = self.username
        self.message['To'] = ",".join(self.receivers)
        try:
            self.client.sendmail(self.username, self.receivers, self.message.as_string())
            self.client.quit()
            print("主题为:[{}]的邮件发送成功~\n".format(subject))
        except Exception as e:
            print("出了点问题,具体信息为:{}".format(e))


def main():
    """
    使用命令行参数,免得密码泄露什么的。
    """
    parser = argparse.ArgumentParser(description="命令行邮件发送工具")
    parser.add_argument("-s", "--sender", help="邮件发送人邮件地址")
    parser.add_argument("-p", "--password", help="邮件发送人对应的密码")
    parser.add_argument("-H", "--host", help="邮件服务器主机")
    parser.add_argument("-P", "--port", help="邮件服务器端口,默认25")
    parser.add_argument("-r", "--receivers", nargs="+", help="收件人列表,注意不能少于一个,而且多个收件人要用空格隔开")
    parser.add_argument("-a", "--attachment", action="append", help="附件的全路径,注意windows和linux上会稍有不同~, 多个附件的时候需要重复指定此参数~")
    parser.add_argument("-t", "--text", help="邮件正文部分,相当于纯文本模式")
    parser.add_argument("-S", "--subject", help="邮件主题")
    args = parser.parse_args()
    if args is None:
        raise Exception("命令行参数有误~")
    sender = args.sender
    password = args.password
    host = args.host
    port = int(args.port)

    mailer = Mailer(username=sender, password=password, host=host, port=port)
    if args.text is not None:
        mailer.addMailContent(content=str(args.text))
    for receiver in args.receivers:
        mailer.addReceiver(receiver=receiver)
    for attach in args.attachment:
        mailer.addAttchment(filename=attach)
    if args.subject is not None:
        mailer.send(subject=str(args.subject))


if __name__ == "__main__":
    main()

使用方法

python sendmail.py -s sender@xxx.com -p your-password -H mailhost(如: smtp.163.com) -P 25(如果是QQ邮箱可能要更改下) - r receiver1 receiver2 ... -a attachment-file-path1 -a attachment-file-path2(-a 选项每次只是添加一个附件的完整路径,多个附件的时候需要多次指定-a参数) -t 邮件正文显示的内容 -S 邮件主题

待改进之处

我自己大致测试了下,感觉还算可以。但是不得不说,这个工具还有其他不够好的地方。大致可以罗列下:

  • 通过命令行输入密码,算是一个比较敏感的问题,可以使用getpass库动态输入密码。

  • 每次都要指定这么一大串参数,有点冗余。这点可以模仿.vimrc这种,通过运行时环境变量来加载,省去每次都要输入一遍。(但是要记得安全保存这些比较敏感的信息)

好了,差不多就这样了。如果以后在用的过程中觉得有不太合适或者可以优化用户体验的,再过来完善吧。(2018年4月12日14:05:44)。


参考链接:
[2]: Python smtplib库发送带有附件的邮件


本文转载:CSDN博客