Python自动化运维1

时间:2022-07-28
本文章向大家介绍Python自动化运维1,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

[TOC]

0x00 快速入门

系统基础信息

psutil-系统性能信息模块

psutil是一个跨平台库,用于检索Python中运行的进程和系统利用率(CPU、内存、磁盘、网络、传感器)的信息。支持平台:

  • Linux
  • Windows
  • macOS
  • FreeBSD
  • OpenBSD
  • NetBSD
  • Sun Solaris
  • AIX

下载与使用:

pip install psutil
>>> help(psutil)

使用案例:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#功能:系统基础信息模块收集
import psutil
import datetime
from subprocess import PIPE

def Memory():
    mem = psutil.virtual_memory()
    swap = psutil.swap_memory()
    total = round(mem.total / pow(1024,3),2)
    free = round(mem.available / pow(1024,3),2)
    use = total - free
    print("系统内存信息:")
    print("tTotal:{1:.2f} {0} ntUse:{2:.2f} {0} ntFree: {3:.2f} {0} 
         nt内存使用百分比:{4:.2f}% n".format("GB",total,use,free,mem.percent))  # 格式化数据
    print("tswapTotal:{1:.2f} {0} ntswapUse:{2:.2f} {0} ntswapFree: {3:.2f} {0} 
         nt交换分区使用百分比:{4:.2f}% n".format("GB",swap.total / pow(1024,3)  ,swap.used / pow(1024,3) ,swap.free / pow(1024,3),swap.percent))  # 格式化数据

def Cpu():
    cpu = psutil.cpu_times()
    cpucount = psutil.cpu_count()
    print("系统处理器信息:")
    print("tUser:{1:.2f} n
        Sysetm:{2:.2f} n
        idle: {3:.2f} n
        CPU逻辑个数:{4:d} nt".format("",cpu.user,cpu.system,cpu.idle,cpucount))  # 格式化数据

def Disk():
    disk = psutil.disk_partitions();
    diskio = psutil.disk_io_counters()
    print("系统磁盘信息:")
    for i in disk:
        print(i.device,i.fstype,i.opts,end="n")
        diskname = psutil.disk_usage(i.device)
        diskvalue = []
        for j in diskname:
            diskvalue.append(j)
        print("Total: {1:.2f}{0} Used: {2:.2f}{0} Free: {3:.2f}{0} 磁盘使用百分百:{4:.2f}%n".format("GB",diskvalue[0] / pow(1024,3),diskvalue[1] / pow(1024,3),diskvalue[2] / pow(1024,3),diskvalue[3]))
    print("磁盘IO信息:Read:",diskio.read_count," Read_time: ",diskio.read_time,"tWrite:",diskio.write_count," Write_time: ",diskio.write_time,"n")       

def Net():
    ip = psutil.net_if_addrs()
    netio = psutil.net_io_counters()
    print("系统网卡信息:")
    for i in ip:
        print("t",i,end="t")
        for x in ip[i]:
            print(x.address,end="  ")
        print("")
    print("nt Bytes_sent : {0:d}  Bytes_recv : {1:d} nt packets_sent(发送数据包) : {2:d}  packets_recv(接收数据包):{3:d}n".format(netio.bytes_sent,netio.bytes_recv,netio.packets_sent,netio.packets_recv))
    

def Info():
    users = psutil.users()
    print("系统用户信息:")
    for i in users:
        print("t",i,end=" ")
    print("t系统开启时间:",datetime.datetime.fromtimestamp(users[0].started).strftime("%Y-%m-%d %H:%M:%S"))
    print("t开机时间:",datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S"),end="nn")


def Process():
    print("系统进程信息:")
    pid = psutil.pids();
    for pidnum in pid:
        print("t",psutil.Process(pidnum).pid,":",psutil.Process(pidnum).name())


#系统性能信息:内存 / CPU / 磁盘 / 网络 / 杂类  
def systemPerformance():
   Cpu()
   Memory()
   Disk()
   Net()
   Info()
   Process()
   
   #通过psutil的Popen()方法启动应用程序,可以跟踪该程序运行的所有相关信息
   p = psutil.Popen(["python","-c","print('Hello World!')"],stdout=PIPE)
   print(p)
   print(p.username(),p.communicate())


if __name__ == "__main__":
    systemPerformance()

系统监控

dnspython-域名轮询业务监控

描述:常用的DNS解析都是一个域名对应一个IP地址,但是通过DNS轮询技术可以做到一个域名对应多个IP;

  • 好处:实现最简单且高效的负载平衡;
  • 缺点:目标主机不可用时无法被自动剔除;

实际案例: + Github查看

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : dnsmonitorweb.py.py
# @CreateTime : 2019/7/10 16:58
# @Author : WeiyiGeek
# @Function : 实现DNS域名轮询业务监控
# @Software: PyCharm

import dns.resolver
import os
import urllib.request
import ssl

iplist=[]
domain="www.qq.com.cn"

def get_ip(domain=""):
    try:
        A = dns.resolver.query(domain,'A')
    except Exception as e:
        print("DNS resolver error : %s " % str(e))
        return False
    for i in A.response.answer:
        for j in i.items:
            if j.address not in iplist:
                iplist.append(j.address)
                print("DNS A 记录:%s" %j)
    return True


def checkweb(ipaddr):
    url = "http://"+ipaddr
    content =""
    try:
        response = urllib.request.urlopen(url,timeout=5,context=ssl._create_unverified_context())  #定义http链接超时时间15秒,支持https请求
    except Exception as e:
        print("[URL请求错误]: n%s" % str(e))
    finally:
        print("请求URL:", response.geturl())
        print("状态码:", response.getcode())
        print("返回消息头:n%s" %response.info())

        if int(response.getcode()) == 200:
            print(ipaddr + " [alive]")
        else:
            print(ipaddr + " [Error]")

if __name__ == '__main__':
    if get_ip(domain) and len(iplist) > 0:
        for ip in iplist:
            checkweb(ip)
        else:
            print("监测完成")
    else:
        print("DNS Resolver Error!")

WeiyiGeek.web示例

文件比对

文件内容差异对比

(1)difflib模块 描述:difflib作为Python的标准库模块,作用是对比文件之间的差异并将结果输出到HTML文档中; 符号含义说明:

# '-' : 在头部表示在一个序列中,但不包含在第二个序列行中,当在底部的时候代表则多余差异;
# '+' : 在头部表示在二个序列中,但不包含在第一个序列行中
# ' ' : 两个序列是一致的
# '?' : 标志两个序列行存在增量差异
# '^' : 标志出两个序列行存在的差异字符

实际案例:(可以采用此种方法来对比linux配置文件的差异)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : difflibfile.py
# @CreateTime : 2019/7/11 17:34
# @Author : WeiyiGeek
# @Function : 实现两个文本之间的字符串比较
# @Software: PyCharm

import difflib

t1 = """text1
this is a demo!
front not diff file 
add string
"""

t2 = """text2
this is a demo!
front diff file
del string
"""
    
def main():
   t1line = t1.splitlines()  #以行为分割
   t2line = t2.splitlines()

   #示例1.输出到shell中
   diff = difflib.Differ()  #创建Differ对象
   result = diff.compare(t1line,t2line)
   # print("n".join(list(result)))  #同下
   for i in list(result):     #缓缓输出结果 输出到标准输入内
       print(i)

    ##输出结果##
    # - text1
    # ?     ^
    #
    # + text2
    # ?     ^
    #
    #   this is a demo!

    # - front not diff file
    # ?     ----           -
    #
    # + front diff file  #这里不存在增量差异
    # - add string
    # ? - ^
    #
    # + del string
    # ?  ^^


   #示例2.生产美观的对比HTML格式文档
   d = difflib.HtmlDiff()
   with open("diff.html",'w+') as f:
       f.writelines(d.make_file(t1line,t2line))

if __name__ == '__main__':
    main()

WeiyiGeek.difflib示例

(2)filecmp模块 描述:filecmp是Python自带模块,可实现文件/目录/遍历子目录的差异对比功能; 比如报告中输出目标目录比原始多出的文件或者子目录,即使文件同名也会判断是否为同一文件(内容级对比);

filecmp三个操作方法:

(1)单文件对比:filecmp.cmp(f1,f2[,shallow]) #比较f1,f2文件是否相同 True/Fasle
 - shallow:缺省为True,表示不根据os.stat()方法返回文件基础信息进行对比判断如访问与修改时间,如为Fasle则根据

(2)多文件对比:filecmp.cmp(dir1,dir2,common[,shallow]) #比对两个目录中的文件,返回三个列表分别是匹配,不匹配以及错误
- common : 列表表示需要对比的文件[f1,f2,f3,f4]
- 匹配为包含匹配的文件列表,反之则不匹配,错误列表代表了目录中不存在的文件以及权限的原因导致不能比较的文件清单等等

[dir1]
ee38a408f20702ccc05fb39a04ee251c  f1
1139929d8855ced585631c0e3fe8ad8d  f2
c35957891d72c0c32b53a01911224e13  f3

[dir2]
ee38a408f20702ccc05fb39a04ee251c  f1
9939929d8855ced625631c0e3fe8ad8d  f2
c35957891d72c0c32b53a01911224e13  f4

#执行结果
(['f1'],['f2'],['f3'])

(3)目录对比:dircmp(a,b[,ignore [,hide]]) #创建一个目录比较对象,支持递归会显示出a目录中包括文件以及ab都存在的子目录的匹配文件
- ignore : 文件名忽略列表 ['RCS',cvs,'tags']
- hide : 隐藏列表默认[os.curdir,os.pardir]
# python -c "import os;print (os.curdir,os.pardir)"
# ('.', '..')

补充说明:

  • report() : 比较当前指定目录中内容
  • report_partial_closure() : 比较当前指定目录及第一级子目录中的内容
  • report_full_closure() : 递归比较所有指定目录的内容
  • left : 左目录
  • right : 右目录
  • left_list:左边文件夹中的文件与文件夹列表;
  • right_list:右边文件夹中的文件与文件夹列表;
  • left_only:只在左边文件夹中存在的文件或文件夹;
  • right_only:只在右边文件夹中存在的文件或文件夹;
  • common:两边文件夹中都存在的文件或文件夹;
  • common_dirs:两边文件夹都存在的子文件夹;
  • common_files:两边文件夹都存在的子文件;
  • common_funny:两边文件夹都存在的子文件夹;
  • same_files:匹配的文件;
  • diff_files:不匹配的文件;
  • funny_files:两边文件夹中都存在,但无法比较的文件;
  • subdirs:将 common_dirs 目录名映射到新的dircmp对象,格式为字典类型

实际案例:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : filedircmp.py
# @CreateTime : 2019/7/12 10:21
# @Author : WeiyiGeek
# @Function : 文件/目录之间的对比
# @Software: PyCharm

import filecmp
import pprint

file1 = "./diff.html"
file2 = "./difflibfile.py"
dir1 = "../"
dir2 = "../../"

def main():
  #示例1.文件对比不同
  print("文件是否相同:%s" % str(filecmp.cmp(file1,file2,False)))
  print("文件是否相同:%s" % str(filecmp.cmp(file1,file2,True)))

    #示例2.实现目录差异对比功能
    #比较当前指定目录中内容
  dirobj = filecmp.dircmp(dir1,dir2,['diff.html']) #目录比较,忽略test.py文件
  print("n[*]  report ")
  dirobj.report()

  #较当前指定目录及第一级子目录中的内容
  print("n[*]  report partial closure ")
  dirobj.report_partial_closure()

  #递归比较所有指定目录的内容
  print("n[*]  report full closure ")
  dirobj.report_full_closure()

    #dircmp类的其他属性参考上方
  print("dircmp类的left_list属性:"+str(dirobj.left_list))
  print("dircmp类的left_only属性:")
  pprint.pprint(dirobj.left_only)


if __name__ == '__main__':
  main()

#补充执行结果:
dircmp类的left_list属性:['.gitignore', '.idea', 'Day1', 'Day2', 'Day3', 'Day4', 'Day5', 'Day6', 'Day7', 'Python安全平台建设', 'Python自动化运维']
dircmp类的left_only属性:
['.gitignore',
 '.idea',
 'Day1',
 'Day2',
 'Day3',
 'Day4',
 'Day5',
 'Day6',
 'Day7',
 'Python安全平台建设',
 'Python自动化运维']

WeiyiGeek.filecmp示例

信息发送

电子邮件发送

smtplib 发送电子邮件模块 描述:SMTP(Simple Mail Transfer Protocol)是简单传输协议,它是一组用于用于由源地址到目的地址的邮件传输规则。 python对SMTP的支持: ①email模块:负责构建邮件

②smtplib模块:负责发送邮件

  • smtplib模块定义了一个SMTP客户机会话对象,可以使用该对象向任何具有SMTP或ESMTP侦听器守护进程的Internet机器发送邮件
  • Python中采用smtplib模式实现邮件的发送功能,电子邮件是最流行的互联网应用之一,运维人员常常通过邮件来发送告警信息,业务质量报表等等;

stmplib模块常用:

#实例化对象SMTP
SMTP = smtplib.SMTP(host='', port=0, local_hostname=None, [timeout, ]source_address=None)   #初始化返回一个非SSL的smtp对象 默认25端口
SMTP =  smtplib.SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None, [timeout, ]context=None, source_address=None) #返回一个SSLsmtp对象,默认465端口

LMTP = smtplib.LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None) #LMTP协议与ESMTP非常相似,主要基于标准SMTP客户机。


#异常抛出
exception smtplib.SMTPException  #OSError的子类,它是此模块提供的所有其他异常的基异常类。

exception smtplib.SMTPServerDisconnected  #当服务器意外断开连接时,或者在将SMTP实例连接到服务器之前尝试使用它时,会引发此异常。

exception smtplib.SMTPResponseException  #包含SMTP错误代码的所有异常的基类当SMTP服务器返回错误代码时,会生成这些异常。
#错误代码存储在错误的smtp_code属性中,smtp_error属性设置为错误消息。

exception smtplib.SMTPSenderRefused  #发送方地址拒绝了。除了所有SMTPResponseException异常所设置的属性之外,还将' sender '设置为SMTP服务器拒绝的字符串。

exception smtplib.SMTPRecipientsRefused #所有收件人地址均被拒绝。每个收件人的错误都可以通过属性收件人访问,属性收件人是一个与SMTP.sendmail()返回的排序完全相同的字典。

exception smtplib.SMTPDataError  #SMTP服务器拒绝接受消息数据。

exception smtplib.SMTPConnectError  #在与服务器建立连接时发生错误。
exception smtplib.SMTPAuthenticationError   #与服务器建立认证的时候出错

exception smtplib.SMTPHeloError  #服务器拒绝了我们的HELO消息。
exception smtplib.SMTPNotSupportedError  #服务器不支持尝试的命令或选项。



#SMTP代表对象
SMTP.connect(host='localhost', port=0)  #当初始化未进行设置host以及默认port=25的时候需要进行设置,在LMTP中使用Unix套接字是很常见的,因此我们的connect()方法必须支持这一点
SMTP.helo(name='') #使用HELO向SMTP服务器标识自己。主机名参数默认为本地主机的完全限定域名。服务器返回的消息存储为对象的helo_resp属性。
SMTP.ehlo(name='') #使用EHLO向ESMTP服务器标识自己。主机名参数默认为本地主机的完全限定域名。
SMTP.login(user, password, *, initial_response_ok=True)  #登录需要身份验证的SMTP服务器。参数是要进行身份验证的用户名和密码,错误返回异常
SMTP.auth(mechanism, authobject, *, initial_response_ok=True) #有关受支持的身份验证方法的列表,请参见auth()
SMTP.starttls(keyfile=None, certfile=None, context=None) #将SMTP连接置于TLS(传输层安全)模式。接下来的所有smtpcommand都将被加密。然后应该再次调用ehlo() 
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())  #邮件发送 , msg可以是包含ASCII范围内字符的字符串,也可以是字节字符串
SMTP.send_message(msg, from_addr=None, to_addrs=None, mail_options=(), rcpt_options=()) #这是调用sendmail()的一个方便方法,使用email.message表示的消息。 如果from_addr为None或to_addrs为None,将会提取msg中的地址
SMTP.set_debuglevel(level)  #设置调试输出级别 1/true / 2(在版本3.0add)
SMTP.quit() #终止SMTP会话并关闭连接
SMTP.close()

email包常用模块:

#定制个性化邮件格式方法
•email.message  #邮件构造
  - email.message.EmailMessage()  # 返回对象 msg,构建发信文本并且存入数组
•email.parser  #解析邮件信息

•email.mime #从头创建电子邮件和MIME对象 (支持HTML)
  - email.mime.text.MIMEText(""" HTML """,_subtype='plain', _charset=None, *, policy=compat32)  #HTML/subtype:文本类型(plain或者html)/字符编码
  - email.mime.multipart.MIMEMultipart(_subtype='mixed', boundary=None, _subparts=None, *, policy=compat32, **_params) #生成包含多个部分的邮件体的MIME对象,subtype三种子类型:mixed)(带附件邮件体),related(构建内嵌资源邮件体),alternative(构建纯文本与超文本共存的邮件体);
  - email.mime.audio.MIMEAudio(_audiodata, _subtype=None, _encoder=email.encoders.encode_base64, *, policy=compat32, **_params) #创建包含音频数据的邮件体,audiodata原始二进制音频数据的字节字符串;
   email.mime.image.MIMEImage(_imagedata, _subtype=None, _encoder=email.encoders.encode_base64, *, policy=compat32, **_params) #创建包含图片数据的邮件体,imagedata原始二进制图片数据的字节字符串;

模块示例:

#示例1.SMTP类支持with语句。当这样使用之后退出时,SMTP QUIT命令将自动发出 (可以,判断连接是否正常)
>>> with smtplib.SMTP("smtp.qq.com",25) as smtp: 
  smtp.noop()  #25端口 非SSL
    smtp.helo('[email protected]')

(250, b'Ok')
(250, b'smtp.qq.com')

>>> with smtplib.SMTP_SSL("smtp.qq.com",465) as smtp:  
  smtp.noop()  #465端口 SSL
    smtp.helo('[email protected]')

(250, b'Ok')
(250, b'smtp.qq.com')


#异常抛出与helo标识自己
>>> try:
  smtp = smtplib.SMTP("smtp.sina.com",25)
  smtp.helo('[email protected]')
    smtp.ehlo("[email protected]")
  smtp.quit('flag') 

except smtplib.SMTPException as connect:
  print("+ [ Error ]:"+connect)

  
(250, b'smtp-5-123.smtpsmail.fmail.xd.sinanode.com')
(221, b'smtp-5-123.smtpsmail.fmail.xd.sinanode.com')
(250, b'smtp.sina.comnPIPELININGnSIZE 73400320nSTARTTLSnAUTH LOGIN PLAINnAUTH=LOGINnMAILCOMPRESSn8BITMIME')


# 登录以及异常抛出
>>> smtp = smtplib.SMTP("smtp.qq.com",25)
>>> try:
  res = smtp.login('[email protected]','itfbbfqxnbhda')
  print(res)
except smtplib.SMTPException as test:
  print("Error:"+str(test))
#认证成功
(235, b'Authentication successful')  

#异常抛出
>>> smtp = smtplib.SMTP("smtp.qq.com",25)
>>> try:
  res = smtp.login('[email protected]','itfbbfqxnbhda')
  print(res)
except smtplib.SMTPException as test:
  print("Error:"+str(test))

Error:(535, b'Error: xc7xebxcaxb9xd3xc3xcaxdaxc8xa8xc2xebxb5xc7xc2xbcxa1xa3xcfxeaxc7xe9xc7xebxbfxb4: http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=1001256')


#认证流程 
# SMTP(host,port) -> login
# SMTP() -> connect(host,port) -> ehlo('[email protected]') -> login
>>> smtp = smtplib.SMTP("smtp.qq.com",25)
>>> try:
  res = smtp.login('[email protected]','iocztfbbfqxnbhda')
  print(res)
  smtp.close()
  smtp.connect("smtp.qq.com",25)
  smtp.ehlo("[email protected]")
  res = smtp.login('[email protected]','iocztfbbfqxnbhda')
  print(res)
except smtplib.SMTPException as test:
  print("Error:"+str(test))

  
(235, b'Authentication successful')
(220, b'smtp.qq.com Esmtp QQ Mail Server')
(250, b'smtp.qq.comnPIPELININGnSIZE 73400320nSTARTTLSnAUTH LOGIN PLAINnAUTH=LOGINnMAILCOMPRESSn8BITMIME')
(235, b'Authentication successful')

邮件发送示例

import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Login Smtp Succeeful'
msg['From'] = 'WeiyiGEEK'
msg['To'] = '[email protected]'
msg.set_content("测试连接服务器(1)")
smtp = smtplib.SMTP("smtp.qq.com",25)
try:
  res = smtp.login('WeiyiGEEK','iocztfbbfqxnbhda') 
  smtp.send_message(msg)
  print(res)
  smtp.close()
except smtplib.SMTPException as test:
  print("Error:"+str(test))

实际案例(1) 文本HTML信息发信:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : stmpDemo.py
# @CreateTime : 2019/7/26 16:17
# @Author : WeiyiGeek
# @Function : 实现邮件的发送
# @Software: PyCharm

import smtplib
import socket
from email.mime.text import MIMEText

SMTPHOST = "smtp.qq.com"
SMTPUSER = ""
SMTPPASS = "jxgovvrd"
FROM = ""
TO = ""
Subject = "测试smtplib发信模块"


def info():
    # 获取计算机名称
    hostname = socket.gethostname()
    # 获取本机IP
    ip = socket.gethostbyname(hostname)
    return hostname + ip


def sendmail(From,To,boby):
    try:
        # server = smtplib.SMTP() #创建一个SMTP对象
        # server = smtplib.connect(SMTPHOST,"25") #非SSL方式连接smtp主机
        server = smtplib.SMTP_SSL(SMTPHOST)     # 通过SSL方法连接smtp主机默认使用465端口
        # server.starttls()  #启用安全传输模式
        server.login(SMTPUSER, SMTPPASS)  # 账号邮箱认证
        server.sendmail(From, To, boby)
        server.quit()
        print("发信成功! dindong")
    except Exception as e:
        print("[发信失败]!:" + str(e))


def main():
    Content = info()+"rn 我是一名电脑技术爱好者,我正在学习Python运维 "
    msg = MIMEText("""
    #创建一个MIMEtext对象,分别插入HTML内容/类型/文本/字符编码,
    <table width="400" border="1px solid red">
    <th>序号1</th><th>序号2</th>
    <tr>
        <td>主机名称</td><td>IP地址</td>
    <tr>
    </table>
    """+Content, "HTML" ,"UTF-8") #组装sendmail主体内容

    msg['Subject'] = Subject  #必须放在MIMEText后
    msg['From'] = FROM
    msg['To'] = TO
    sendmail(FROM, TO, msg.as_string())

if __name__ == '__main__':
    main()

WeiyiGeek.发信测试

实际案例(2) 图片格式与附件文档发信:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : stmplibsimple3.py
# @CreateTime : 2019/7/30 16:07
# @Author : WeiyiGeek
# @Function : 截屏图片发送与附件格式邮件发信
# @Software: PyCharm

import smtplib,os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from PIL import ImageGrab


SMTPHOST = "smtp.qq.com"
SMTPUSER = "[email protected]"
SMTPPASS = "iocztfbbfqxnbhda"
SUBJECT = u"截屏图片发送与附件格式邮件发信测试"
FROM = SMTPUSER
TO = "[email protected]"


def captrue():
    img = ImageGrab.grab(bbox=(0, 0, 1141, 610))
    img.save('captrue.jpg','JPEG')
    os.system("ipconfig > ip.txt")

def addimg(src, imgid):
    """
    添加图片与iD到mimeiMAGE对象中
    :param src: 图片路径 
    :param imgid: 图片id
    :return: 回imgImage对象
    """
    with open(src, 'rb') as img:
        msgimage = MIMEImage(img.read())  #创建MIMEImage对象以读取图片内容作为参数
        msgimage.add_header('Content-ID', imgid) #指定图片文件的Content-ID,img标签中使用
        return msgimage  #返回MIMEImage对象


def addacc():
    #创建一个MIMEText对象附加文档到邮件之中
    attach = MIMEText(open('./ip.txt','r').read(),"plain","utf-8")
    attach["Context-type"] = "text/plain"  #指定文件类型
    attach["Content-Disposition"] = "attachment; filename='ip.txt'"  #设置下载对话框
    return attach


def sendmain(msg):
    try:
        server = smtplib.SMTP_SSL(SMTPHOST)
        server.login(SMTPUSER, SMTPPASS)
        server.sendmail(FROM, TO, msg.as_string())
        server.quit()
        print("邮件发送:成功")
    except Exception as err:
        print("发送失败:" + str(err))


def emailContent():
    message = MIMEMultipart('related')  #创建MIMEutilpart对象并且采用related定义内嵌资源的邮件体
    msgtext = MIMEText("""
        <font color=red> 官网业务附件 </font>
        <table border="0" cellspacing="0" cellpadding="2">
            <tr bgcolor="#CECFAD" height="20" style="front-size:14px">
                <td colspan="2"> 系统截屏图片邮件发送 </td>
            </tr>
            <tr>
                <td>
                    <a href="www.weiyigeek.github.io"><img src="cid:captrue" alt="Python屏幕截图"></a>
                </td>
            </tr>
        </table>
        
    """,'html','utf-8')  #img标签的图片是通过Content-ID来引用的
    message.attach(msgtext)
    message.attach(addimg("./captrue.jpg", "captrue"))
    message.attach(addacc())
    message['Subject'] = SUBJECT
    message['From'] = FROM
    message['To'] = TO
    sendmain(message)  #邮件发送

def main():
    captrue()  #截屏
    emailContent() #邮件构造

if __name__ == '__main__':
    main()

WeiyiGeek.邮件发送图片和文档

补充参考:

# See also:
Module smtplib : SMTP (Simple Mail Transport Protocol) client
Module poplib : POP (Post Office Protocol) client
Module imaplib : IMAP (Internet Message Access Protocol) client
Module nntplib : NNTP (Net News Transport Protocol) client
Module mailboxTools : 用于使用各种标准格式在磁盘上创建、读取和管理消息集合的工具。
Module smtpd  : SMTP server framework (primarily useful for testing)
 测试环境

在client.jaylin.com上发送邮件:

# telnet mail.jaylin.com 25

Trying 192.168.1.9...

Connected to mail.jaylin.com (192.168.1.9).

Escape character is '^]'.

220 mail.jaylin.com ESMTP Sendmail 8.13.8/8.13.8; Wed, 21 Oct 2009 05:12:41 +0800

EHLO mail.jaylin.com

250-mail.jaylin.com Hello [192.168.1.7], pleased to meet you

250-ENHANCEDSTATUSCODES

250-PIPELINING

250-8BITMIME

250-SIZE

250-DSN

250-ETRN

250-DELIVERBY

250 HELP

MAIL FROM:[email protected]

250 2.1.0 [email protected] Sender ok

RCPT TO:[email protected]

250 2.1.5 [email protected] Recipient ok

DATA

354 Enter mail, end with "." on a line by itself

SUBJECT xinxin

lala~

.

250 2.0.0 n9KLCfJo004052 Message accepted for delivery

quit

221 2.0.0 mail.jaylin.com closing connection

Connection closed by foreign host.

在client.xin.com上接收邮件:# telnet mail.xin.com 110

Trying 192.168.2.11...

Connected to mail.xin.com (192.168.2.11).

Escape character is '^]'.

+OK Dovecot ready.

User xin

+OK

Pass ******

+OK Logged in.

List

+OK 12 messages:

1 621

2 624

3 619

4 619

5 620

6 841

7 839

8 757

9 738

10 737

11 742

12 740

.

retr 12

+OK 740 octets

Return-Path: <[email protected]>

Received: from smarthost.jaylin.com (smarthost.jaylin.co [192.168.1.6] (may be forged))

        by mail.xin.com (8.13.8/8.13.8) with ESMTP id n9KLDC2H004460

        for <[email protected]>; Wed, 21 Oct 2009 05:13:12 +0800

Received: from mail.jaylin.com (mail.jaylin.com [192.168.1.9])

        by smarthost.jaylin.com (8.13.8/8.13.8) with ESMTP id n9KLD7VC006062

        for <[email protected]>; Wed, 21 Oct 2009 05:13:07 +0800

Received: from mail.jaylin.com ([192.168.1.7])

        by mail.jaylin.com (8.13.8/8.13.8) with ESMTP id n9KLCfJo004052

        for [email protected]; Wed, 21 Oct 2009 05:12:54 +0800

Date: Wed, 21 Oct 2009 05:12:41 +0800

From: [email protected]

Message-Id: <[email protected]>

 

SUBJECT xinxin

lala~

.

quit

+OK Logging out.

Connection closed by foreign host.

 

 

为了验证Smarthost生效了,我们查看一下三台Sendmail服务器的/var/log/maillog日志文件。

发送方mail.jaylin.com:

Oct 21 05:13:02 mail sendmail[4052]: n9KLCfJo004052: [email protected], size=21, class=0, nrcpts=1, msgid=<[email protected]>, proto=ESMTP, daemon=MTA, relay=[192.168.1.7]

Oct 21 05:13:02 mail sendmail[4054]: n9KLCfJo004052: [email protected], [email protected] (501/501), delay=00:00:08, xdelay=00:00:00, mailer=relay, pri=120021, relay=smarthost.jaylin.com [192.168.1.6], dsn=2.0.0, stat=Sent (n9KLD7VC006062 Message accepted for delivery)

作为Smarthost的smarthost.jaylin.com:

Oct 21 05:13:07 client1 sendmail[6062]: n9KLD7VC006062: from=<[email protected]>, size=304, class=0, nrcpts=1, msgid=<[email protected]>, proto=ESMTP, daemon=MTA, relay=mail.jaylin.com [192.168.1.9]

Oct 21 05:13:07 client1 sendmail[6064]: n9KLD7VC006062: to=<[email protected]>, delay=00:00:00, xdelay=00:00:00, mailer=esmtp, pri=120304, relay=mail.xin.com. [192.168.2.11], dsn=2.0.0, stat=Sent (n9KLDC2H004460 Message accepted for delivery)

接收方mail.xin.com:

Oct 21 05:13:12 mail sendmail[4460]: n9KLDC2H004460: from=<[email protected]>, size=489, class=0, nrcpts=1, msgid=<[email protected]>, proto=ESMTP, daemon=MTA, relay=smarthost.jaylin.co [192.168.1.6] (may be forged)

Oct 21 05:13:12 mail sendmail[4461]: n9KLDC2H004460: to=<[email protected]>, delay=00:00:00, xdelay=00:00:00, mailer=local, pri=30702, dsn=2.0.0, stat=Sent

Oct 21 05:13:35 mail dovecot: pop3-login: Login: user=<xin>, method=PLAIN, rip=::ffff:192.168.2.8, lip=::ffff:192.168.2.11

Oct 21 05:13:41 mail dovecot: POP3(xin): Disconnected: Logged out top=0/0, retr=1/756, del=0/12, size=8497