Python基础+刷廖雪峰教程笔记(七)——Pillow、requests、chardet、psutil、virtualenv、图形界面、TCP、UDP、SMTP、POP3

本文主要是介绍Python基础+刷廖雪峰教程笔记(七)——Pillow、requests、chardet、psutil、virtualenv、图形界面、TCP、UDP、SMTP、POP3,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

由于在深度学习的路上,发现自己两年前学习的python有些遗忘,在面向对象这一块尤其不熟悉,故刷一遍廖雪峰老师的官方教程,梳理一下遗漏的知识点。

参考网址:https://www.liaoxuefeng.com/wiki/1016959663602400

1.Pillow

PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。

Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。

demo1:

from PIL import Image, ImageFilter# 打开一个jpg图像文件,注意是当前路径:
im = Image.open('C:\\Users\\DENGFY\\Desktop\\cat.jpg')
# 获得图像尺寸:
w, h = im.size
print('Original image size: %sx%s' % (w, h))
# 缩放到50%:
im.thumbnail((w//2, h//2))
print('Resize image to: %sx%s' % (w//2, h//2))
# 把缩放后的图像用jpeg格式保存:
im.save('C:\\Users\\DENGFY\\Desktop\\thumbnail.jpg', 'jpeg')# 应用模糊滤镜:
im2 = im.filter(ImageFilter.BLUR)
im2.save('C:\\Users\\DENGFY\\Desktop\\blur.jpg', 'jpeg')

out1:
【缩小后】
在这里插入图片描述
【模糊后】
在这里插入图片描述
PIL的ImageDraw提供了一系列绘图方法,让我们可以直接绘图。比如要生成随机字母验证码图片:

demo2:

from PIL import Image, ImageDraw, ImageFont, ImageFilterimport random# 随机字母:
def rndChar():return chr(random.randint(65, 90))# 随机颜色1:
def rndColor():return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))# 随机颜色2:
def rndColor2():return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))# 240 x 60:
width = 60 * 4
height = 60
image = Image.new('RGB', (width, height), (255, 255, 255))
# 创建Font对象:
font = ImageFont.truetype('arial.ttf', 36)
# 创建Draw对象:
draw = ImageDraw.Draw(image)
# 填充每个像素:
for x in range(width):for y in range(height):draw.point((x, y), fill=rndColor())
# 输出文字:
for t in range(4):draw.text((60 * t + 10, 10), rndChar(), font=font, fill=rndColor2())
# 模糊:
image = image.filter(ImageFilter.BLUR)
image.save('C:\\Users\\DENGFY\\Desktop\\code.jpg', 'jpeg')

out2:
在这里插入图片描述
2.requests

Python内置的urllib模块,用于访问网络资源。但是,它用起来比较麻烦,而且,缺少很多实用的高级功能。

更好的方案是使用requests。它是一个Python第三方库,处理URL资源特别方便。

demo3:

import requests
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
url = 'https://www.douban.com/'
r = requests.get(url, headers=headers) #网站基本有安全验证
print(r.status_code)
#print(r.text),打印网站内容r1 = requests.get('https://www.douban.com/search', params={'q': 'python', 'cat': '1001'})#带参数的URL,传入一个dict作为params参数
print(r1.url) # 实际请求的URLr1.encoding #requests自动检测编码,可以使用encoding属性查看#print(r.content)  无论响应是文本还是二进制内容,我们都可以用content属性获得bytes对象:#要发送POST请求,只需要把get()方法变成post(),然后传入data参数作为POST请求的数据:
r2 = requests.post('https://accounts.douban.com/login', data={'form_email': 'abc@example.com', 'form_password': '123456'})#requests默认使用application/x-www-form-urlencoded对POST数据编码。如果要传递JSON数据,可以直接传入json参数:
params = {'key': 'value'}
r3 = requests.post(url, json=params) # 内部自动序列化为JSON'''
#类似的,上传文件需要更复杂的编码格式,但是requests把它简化成files参数:
upload_files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=upload_files)
'''#获取响应头
print(r.headers)
print(r.headers['Content-Type'])#要在请求中传入Cookie,只需准备一个dict传入cookies参数
cs = {'token': '12345', 'status': 'working'}
r = requests.get(url, cookies=cs)#最后,要指定超时,传入以秒为单位的timeout参数:
r = requests.get(url, timeout=2.5) # 2.5秒后超时

out3:

200
https://www.douban.com/search?q=python&cat=1001
{'Date': 'Mon, 12 Jul 2021 07:07:16 GMT', 'Content-Type': 'text/html; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=30', 'Vary': 'Accept-Encoding, Accept-Encoding', 'X-Xss-Protection': '1; mode=block', 'X-Douban-Mobileapp': '0', 'Expires': 'Sun, 1 Jan 2006 01:00:00 GMT', 'Pragma': 'no-cache', 'Cache-Control': 'must-revalidate, no-cache, private', 'Set-Cookie': 'll="118331"; path=/; domain=.douban.com; expires=Tue, 12-Jul-2022 07:07:16 GMT, bid=mSqxb78ak4Y; Expires=Tue, 12-Jul-22 07:07:16 GMT; Domain=.douban.com; Path=/', 'X-DAE-App': 'sns', 'X-DAE-Instance': 'home', 'X-DAE-Mountpoint': 'True', 'X-DOUBAN-NEWBID': 'mSqxb78ak4Y', 'Server': 'dae', 'X-Frame-Options': 'SAMEORIGIN', 'Strict-Transport-Security': 'max-age=15552000;', 'Content-Encoding': 'gzip'}
text/html; charset=utf-8

在读取文件时,注意务必使用’rb’即二进制模式读取,这样获取的bytes长度才是文件的长度。

把post()方法替换为put(),delete()等,就可以以PUT或DELETE方式请求资源。

除了能轻松获取响应内容外,requests对获取HTTP响应的其他信息也非常简单。

3.chardet

chardet这个第三方库用来检测编码,简单易用.

demo4:

import chardet
print(chardet.detect(b'Hello, world!')) #confidence字段,表示检测的概率是1.0(即100%)#检测GBK编码的中文
data = '离离原上草,一岁一枯荣'.encode('gbk')
print(chardet.detect(data)) #检测的编码是GB2312,注意到GBK是GB2312的超集,两者是同一种编码,检测正确的概率是74%,language字段指出的语言是'Chinese'#对UTF-8编码进行检测
data = '离离原上草,一岁一枯荣'.encode('utf-8')
print(chardet.detect(data))#对日文进行检测
data = '最新の主要ニュース'.encode('euc-jp')
print(chardet.detect(data))

out4:

{'encoding': 'ascii', 'confidence': 1.0, 'language': ''}
{'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'}
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
{'encoding': 'EUC-JP', 'confidence': 0.99, 'language': 'Japanese'}

4.psutil

用Python来编写脚本简化日常的运维工作是Python的一个重要用途。

在Python中获取系统信息的一个好办法是使用psutil这个第三方模块。顾名思义,psutil = process and system utilities,它不仅可以通过一两行代码实现系统监控,还可以跨平台使用,支持Linux/UNIX/OSX/Windows等,是系统管理员和运维小伙伴不可或缺的必备模块。

demo5:

import psutil#获取CPU信息
print(psutil.cpu_count()) # CPU逻辑数量
print(psutil.cpu_count(logical=False)) # CPU物理核心, 8是8核非超线程#统计CPU的用户/系统/空闲时间
print(psutil.cpu_times())#实现类似top命令的CPU使用率,每秒刷新一次,累计10次
for x in range(10):print(psutil.cpu_percent(interval=1, percpu=True))#获取内存信息
#使用psutil获取物理内存和交换内存信息,分别使用
print(psutil.virtual_memory())
print(psutil.swap_memory())#获取磁盘信息
#可以通过psutil获取磁盘分区、磁盘使用率和磁盘IO信息
print(psutil.disk_partitions())# 磁盘分区信息
print(psutil.disk_usage('/')) # 磁盘使用情况
print(psutil.disk_io_counters()) # 磁盘IO#获取网络信息
#psutil可以获取网络接口和网络连接信息:
print(psutil.net_io_counters()) # 获取网络读写字节/包的个数
print(psutil.net_if_addrs()) # 获取网络接口信息
print(psutil.net_if_stats()) # 获取网络接口状态
print(psutil.net_connections())  #获取当前网络连接信息#获取进程信息
#通过psutil可以获取到所有进程的详细信息:print(psutil.pids()) # 所有进程ID
p = psutil.Process(1444) # 获取指定进程ID=1444,其实就是当前Python交互环境
print(p.name()) # 进程名称
print(p.exe()) # 进程exe路径

out5:

16
8
#这里输出很长,已隐去
svchost.exe
C:\Windows\System32\svchost.exe

5.virtualenv

virtualenv就是用来为一个应用创建一套“隔离”的Python运行环境。

参考这里

6.图形界面

Tkinter

编写的Python代码会调用内置的Tkinter,Tkinter封装了访问Tk的接口;

Tk是一个图形库,支持多个操作系统,使用Tcl语言开发;

Tk会调用操作系统提供的本地GUI接口,完成最终的GUI。

所以,我们的代码只需要调用Tkinter提供的接口就可以了。

demo6:

#第一步是导入Tkinter包的所有内容
from tkinter import *
import tkinter.messagebox as messagebox#第二步是从Frame派生一个Application类,这是所有Widget的父容器
class Application(Frame):def __init__(self, master=None):Frame.__init__(self, master)self.pack()self.createWidgets()def createWidgets(self):self.nameInput = Entry(self)self.nameInput.pack()self.alertButton = Button(self, text='Hello', command=self.hello)self.alertButton.pack()def hello(self):name = self.nameInput.get() or 'world'messagebox.showinfo('Message', 'Hello, %s' % name)#第三步,实例化Application,并启动消息循环
app = Application()
# 设置窗口标题:
app.master.title('Hello World')
# 主消息循环:
app.mainloop()

out6:
在这里插入图片描述
在这里插入图片描述

在GUI中,每个Button、Label、输入框等,都是一个Widget。Frame则是可以容纳其他Widget的Widget,所有的Widget组合起来就是一棵树。

pack()方法把Widget加入到父容器中,并实现布局。pack()是最简单的布局,grid()可以实现更复杂的布局。

在createWidgets()方法中,我们创建一个Label和一个Button,当Button被点击时,触发self.quit()使程序退出。

GUI程序的主线程负责监听来自操作系统的消息,并依次处理每一条消息。因此,如果消息处理非常耗时,就需要在新线程中处理。

当用户点击按钮时,触发hello(),通过self.nameInput.get()获得用户输入的文本后,使用tkMessageBox.showinfo()可以弹出消息对话框。

7.TCP编程

Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。

客户端

大多数连接都是可靠的TCP连接。创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器。

举个例子,当我们在浏览器中访问新浪时,我们自己的计算机就是客户端,浏览器会主动向新浪的服务器发起连接。如果一切顺利,新浪的服务器接受了我们的连接,一个TCP连接就建立起来的,后面的通信就是发送网页内容了。

客户端要主动发起TCP连接,必须知道服务器的IP地址和端口号。

demo7:

# 导入socket库:
import socket# 创建一个socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议
# 建立连接:
s.connect(('www.sina.com.cn', 80))#新浪提供网页服务的服务器必须把端口号固定在80端口,因为80端口是Web服务的标准端口,参数是一个tuple# 发送数据,向新浪服务器发送请求,要求返回首页的内容
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')# 接收数据:
#接收数据时,调用recv(max)方法,一次最多接收指定的字节数,因此,在一个while循环中反复接收,直到recv()返回空数据,表示接收完毕,退出循环。
buffer = []
while True:# 每次最多接收1k字节:d = s.recv(1024)if d:buffer.append(d)else:break
data = b''.join(buffer)# 关闭连接:
s.close()#接收到的数据包括HTTP头和网页本身,我们只需要把HTTP头和网页分离一下,把HTTP头打印出来,网页内容保存到文件
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
# 把接收的数据写入文件:
with open('C:\\Users\\DENGFY\\Desktop\\sina.html', 'wb') as f:f.write(html)

out7:

HTTP/1.1 302 Moved Temporarily
Server: nginx
Date: Mon, 12 Jul 2021 08:30:27 GMT
Content-Type: text/html
Content-Length: 138
Connection: close
Location: https://www.sina.com.cn/
X-Via-CDN: f=edge,s=cmcc.chongqing.union.171.nb.sinaedge.com,c=183.222.32.25;

现在,只需要在浏览器中打开这个sina.html文件,就可以看到新浪的首页了。

服务器

和客户端编程相比,服务器编程就要复杂一些。

服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。

所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。

但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。

我们来编写一个简单的服务器程序,它接收客户端连接,把客户端发过来的字符串加上Hello再发回去。

服务器程序:

# 导入socket库:
import socket
import time, threading#首先,创建一个基于IPv4和TCP协议的Socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)''''
然后,我们要绑定监听的地址和端口。服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用0.0.0.0绑定到所有的网络地址,
还可以用127.0.0.1绑定到本机地址。127.0.0.1是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。
端口号需要预先指定。因为我们写的这个服务不是标准服务,所以用9999这个端口号。请注意,小于1024的端口号必须要有管理员权限才能绑定:
'''# 监听端口:
s.bind(('127.0.0.1', 9999))#紧接着,调用listen()方法开始监听端口,传入的参数指定等待连接的最大数量:
s.listen(5)
print('Waiting for connection...')#每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接:
def tcplink(sock, addr):print('Accept new connection from %s:%s...' % addr)sock.send(b'Welcome!')while True:data = sock.recv(1024)time.sleep(1)if not data or data.decode('utf-8') == 'exit':breaksock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))sock.close()print('Connection from %s:%s closed.' % addr)#利用accept()进行接收新连接并进行处理#接下来,服务器程序通过一个永久循环来接受来自客户端的连接,accept()会等待并返回一个客户端的连接:
while True:# 接受一个新连接:sock, addr = s.accept()# 创建新线程来处理TCP连接:t = threading.Thread(target=tcplink, args=(sock, addr))t.start()

客户端程序:

#连接建立后,服务器首先发一条欢迎消息,然后等待客户端数据,并加上Hello再发送给客户端。如果客户端发送了exit字符串,就直接关闭连接。
#要测试这个服务器程序,我们还需要编写一个客户端程序:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接:
s.connect(('127.0.0.1', 9999))
# 接收欢迎消息:
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:# 发送数据:s.send(data)print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

运行结果:
在这里插入图片描述
同一个端口,被一个Socket绑定了以后,就不能被别的Socket绑定了。

8.UDP编程

TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据。相对TCP,UDP则是面向无连接的协议。

使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。

虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。

我们来看看如何通过UDP协议传输数据。和TCP类似,使用UDP的通信双方也分为客户端和服务器。服务器首先需要绑定端口:

服务器程序:

import sockets = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口:
s.bind(('127.0.0.1', 9999))#创建Socket时,SOCK_DGRAM指定了这个Socket的类型是UDP。
#绑定端口和TCP一样,但是不需要调用listen()方法,而是直接接收来自任何客户端的数据
print('Bind UDP on 9999...')
while True:# 接收数据:data, addr = s.recvfrom(1024)print('Received from %s:%s.' % addr)s.sendto(b'Hello, %s!' % data, addr)#recvfrom()方法返回数据和客户端的地址与端口,
#这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端

客户端程序:

import sockets = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:# 发送数据:s.sendto(data, ('127.0.0.1', 9999))# 接收数据:print(s.recv(1024).decode('utf-8'))
s.close()

在这里插入图片描述
9.SMTP发送邮件

SMTP是发送邮件的协议,Python内置对SMTP的支持,可以发送纯文本邮件、HTML邮件以及带附件的邮件。

Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件。

发送纯文本

首先,我们来构造一个最简单的纯文本邮件:

demo8:

import smtplib
import threading
from email.mime.text import MIMEText#注意到构造MIMEText对象时,第一个参数就是邮件正文,第二个参数是MIME的subtype,传入'plain'表示纯文本,
#最终的MIME就是'text/plain',最后一定要用utf-8编码保证多语言兼容性。
msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')# 输入Email地址和口令:
from_addr = input('From: ')
password = input('Password: ')#这里的密码是邮箱的授权码
# 输入收件人地址:
to_addr = input('To: ')
# 输入SMTP服务器地址:
smtp_server = input('SMTP server: ')server = smtplib.SMTP(smtp_server, 25) # SMTP协议默认端口是25
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

我们用set_debuglevel(1)就可以打印出和SMTP服务器交互的所有信息。SMTP协议就是简单的文本命令和响应。login()方法用来登录SMTP服务器,sendmail()方法就是发邮件,由于可以一次发给多个人,所以传入一个list,邮件正文是一个str,as_string()把MIMEText对象变成str。

邮件主题、如何显示发件人、收件人等信息并不是通过SMTP协议发给MTA,而是包含在发给MTA的文本中的,所以,我们必须把From、To和Subject添加到MIMEText中,才是一封完整的邮件:

demo9:

import smtplib
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddrdef _format_addr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))from_addr = input('From: ')
password = input('Password: ')
to_addr = input('To: ')
smtp_server = input('SMTP server: ')msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
msg['From'] = _format_addr('元爸 <%s>' % from_addr)
msg['To'] = _format_addr('管理员 <%s>' % to_addr)
msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

发送HTML邮件

如果我们要发送HTML邮件,而不是普通的纯文本文件怎么办?方法很简单,在构造MIMEText对象时,把HTML字符串传进去,再把第二个参数由plain变为html就可以了:

msg = MIMEText('<html><body><h1>Hello</h1>' +'<p>send by <a href="http://www.python.org">Python</a>...</p>' +'</body></html>', 'html', 'utf-8')

发送附件

如果Email中要加上附件怎么办?带附件的邮件可以看做包含若干部分的邮件:文本和各个附件本身,所以,可以构造一个MIMEMultipart对象代表邮件本身,然后往里面加上一个MIMEText作为邮件正文,再继续往里面加上表示附件的MIMEBase对象即可:

demo10:

from email import encoders
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.utils import parseaddr, formataddrimport smtplibdef _format_addr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))from_addr = input('From: ')
password = input('Password: ')
to_addr = input('To: ')
smtp_server = input('SMTP server: ')# 邮件对象:
msg = MIMEMultipart()
msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
msg['To'] = _format_addr('管理员 <%s>' % to_addr)
msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()# 邮件正文是MIMEText:
msg.attach(MIMEText('send with file...', 'plain', 'utf-8'))# 添加附件就是加上一个MIMEBase,从本地读取一个图片:
with open('C:\\Users\\DENGFY\\Desktop\\cat.jpg', 'rb') as f:# 设置附件的MIME和文件名,这里是png类型:mime = MIMEBase('image', 'jpg', filename='cat.jpg')# 加上必要的头信息:mime.add_header('Content-Disposition', 'attachment', filename='test.png')mime.add_header('Content-ID', '<0>')mime.add_header('X-Attachment-Id', '0')# 把附件的内容读进来:mime.set_payload(f.read())# 用Base64编码:encoders.encode_base64(mime)# 添加到MIMEMultipart:msg.attach(mime)server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

发送图片

如果要把一个图片嵌入到邮件正文中怎么做?直接在HTML邮件中链接图片地址行不行?答案是,大部分邮件服务商都会自动屏蔽带有外链的图片,因为不知道这些链接是否指向恶意网站。

要把图片嵌入到邮件正文中,我们只需按照发送附件的方式,先把邮件作为附件添加进去,然后,在HTML中通过引用src="cid:0"就可以把附件作为图片嵌入了。如果有多个图片,给它们依次编号,然后引用不同的cid:x即可。

把上面代码加入MIMEMultipart的MIMEText从plain改为html,然后在适当的位置引用图片:

demo11:

from email import encoders
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.utils import parseaddr, formataddrimport smtplibdef _format_addr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))from_addr = input('From: ')
password = input('Password: ')
to_addr = input('To: ')
smtp_server = input('SMTP server: ')# 邮件对象:
msg = MIMEMultipart()
msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
msg['To'] = _format_addr('管理员 <%s>' % to_addr)
msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()# 邮件正文是MIMEText:,,,修改了该段
msg.attach(MIMEText('<html><body><h1>Hello</h1>' +'<p><img src="cid:0"></p>' +'</body></html>', 'html', 'utf-8'))# 添加附件就是加上一个MIMEBase,从本地读取一个图片:
with open('C:\\Users\\DENGFY\\Desktop\\cat.jpg', 'rb') as f:# 设置附件的MIME和文件名,这里是png类型:mime = MIMEBase('image', 'jpg', filename='cat.jpg')# 加上必要的头信息:mime.add_header('Content-Disposition', 'attachment', filename='test.png')mime.add_header('Content-ID', '<0>')mime.add_header('X-Attachment-Id', '0')# 把附件的内容读进来:mime.set_payload(f.read())# 用Base64编码:encoders.encode_base64(mime)# 添加到MIMEMultipart:msg.attach(mime)server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

同时支持HTML和Plain格式

如果我们发送HTML邮件,收件人通过浏览器或者Outlook之类的软件是可以正常浏览邮件内容的,但是,如果收件人使用的设备太古老,查看不了HTML邮件怎么办?

办法是在发送HTML的同时再附加一个纯文本,如果收件人无法查看HTML格式的邮件,就可以自动降级查看纯文本邮件。

利用MIMEMultipart就可以组合一个HTML和Plain,要注意指定subtype是alternative:

msg = MIMEMultipart('alternative')
msg['From'] = ...
msg['To'] = ...
msg['Subject'] = ...msg.attach(MIMEText('hello', 'plain', 'utf-8'))
msg.attach(MIMEText('<html><body><h1>Hello</h1></body></html>', 'html', 'utf-8'))
# 正常发送msg对象...

加密SMTP

使用标准的25端口连接SMTP服务器时,使用的是明文传输,发送邮件的整个过程可能会被窃听。要更安全地发送邮件,可以加密SMTP会话,实际上就是先创建SSL安全连接,然后再使用SMTP协议发送邮件。

某些邮件服务商,例如Gmail,提供的SMTP服务必须要加密传输。我们来看看如何通过Gmail提供的安全SMTP发送邮件。

必须知道,Gmail的SMTP端口是587,因此,修改代码如下:

smtp_server = 'smtp.gmail.com'
smtp_port = 587
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
# 剩下的代码和前面的一模一样:
server.set_debuglevel(1)
...

只需要在创建SMTP对象后,立刻调用starttls()方法,就创建了安全连接。后面的代码和前面的发送邮件代码完全一样。

10.POP3收取邮件

收取邮件就是编写一个MUA作为客户端,从MDA把邮件获取到用户的电脑或者手机上。收取邮件最常用的协议是POP协议,目前版本号是3,俗称POP3。

Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件。

注意到POP3协议收取的不是一个已经可以阅读的邮件本身,而是邮件的原始文本,这和SMTP协议很像,SMTP发送的也是经过编码后的一大段文本。

要把POP3收取的文本变成可以阅读的邮件,还需要用email模块提供的各种类来解析原始文本,变成可阅读的邮件对象。

所以,收取邮件分两步:

第一步:用poplib把邮件的原始文本下载到本地;

第二部:用email解析原始文本,还原为邮件对象。

通过POP3下载邮件

demo12:

import poplib
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr# 输入邮件地址, 口令和POP3服务器地址:
email = input('Email: ')
password = input('Password: ')
pop3_server = input('POP3 server: ')# 连接到POP3服务器:
server = poplib.POP3(pop3_server)
# 可以打开或关闭调试信息:
server.set_debuglevel(1)
# 可选:打印POP3服务器的欢迎文字:
print(server.getwelcome().decode('utf-8'))# 身份认证:
server.user(email)
server.pass_(password)# stat()返回邮件数量和占用空间:
print('Messages: %s. Size: %s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
print(mails)# 获取最新一封邮件, 注意索引号从1开始:
index = len(mails)
resp, lines, octets = server.retr(index)# lines存储了邮件的原始文本的每一行,
# 可以获得整个邮件的原始文本:
msg_content = b'\r\n'.join(lines).decode('utf-8')
# 稍后解析出邮件:
msg = Parser().parsestr(msg_content)# 可以根据邮件索引号直接从服务器删除邮件:
# server.dele(index)
# 关闭连接:
server.quit()

out12:

Email: 617633113@qq.com
Password: xxxxxx
POP3 server: pop.qq.com
+OK XMail POP3 Server v1.0 Service Ready(XMail v1.0)
*cmd* 'USER 617633113@qq.com'
*cmd* 'PASS xxxxxx'
*cmd* 'STAT'
*stat* [b'+OK', b'5', b'34882']
Messages: 5. Size: 34882
*cmd* 'LIST'
[b'1 9671', b'2 4546', b'3 8529', b'4 8529', b'5 3607']
*cmd* 'RETR 5'
*cmd* 'QUIT'

解析邮件

解析邮件的过程和上一节构造邮件正好相反。

demo13:

import poplib
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr# 邮件的Subject或者Email中包含的名字都是经过编码后的str,要正常显示
# 就必须decode
def decode_str(s):# 在不转换字符集的情况下解码消息头值,返回一个listvalue, charset = decode_header(s)[0]if charset:value = value.decode(charset)return value# 文本邮件的内容也是str,还需要检测编码,
# 否则,非UTF-8编码的邮件都无法正常显示:def guess_charset(msg):print('msg::%s' % msg)# 得到字符集charset = msg.get_charset()print('charset::%s' % charset)if charset is None:# lower:所有大写字符为小写content_type = msg.get('Content-Type', '').lower()print('content_type::%s' % content_type)# find:检测字符串中是否包含子字符串# 返回charset=头字符的位置pos = content_type.find('charset=')print('pos::', pos)if pos >= 0:# strip:移除字符串头尾指定的字符(默认为空格)charset = content_type[pos + 8:].strip()print('charset::%s' % charset)return charset# indent用于缩进显示:
def print_info(msg, indent=0):# 初始分析if indent == 0:# 遍历获取 发件人,收件人,主题for header in ['From', 'To', 'Subject']:# 获得对应的内容value = msg.get(header, '')# 有内容if value:# 如果是主题if header == 'Subject':# 解码主题value = decode_str(value)else:# parseaddr:解析字符串中的email地址hdr, addr = parseaddr(value)# 解码主题name = decode_str(hdr)# 合成内容value = u'%s <%s>' % (name, addr)print('%s%s:%s' % (' ' * indent, header, value))# 如果消息由多个部分组成,则返回True。if msg.is_multipart():# 返回list,包含所有的子对象parts = msg.get_payload()# enumerate将其组成一个索引序列,利用它可以同时获得索引和值for n, part in enumerate(parts):# 打印消息模块编号print('%s part %s' % (' ' * indent, n))# 打印分割符号print('%s--------------------' % (' ' * indent))# 递归打印print_info(part, indent + 1)else:# 递归结束条件,打印最基本模块# 返回消息的内容类型。content_type = msg.get_content_type()# 如果是text类型或者是html类型if content_type == 'text/plain' or content_type == 'text/html':# 返回list,包含所有的子对象,开启解码content = msg.get_payload(decode=True)# 猜测字符集charset = guess_charset(msg)# 字符集不为空if charset:# 解密content = content.decode(charset)# 打印内容print('%s Text: %s' % (' ' * indent, content + '...'))else:print('%s Attachment: %s' % (' ' * indent, content_type))# # 输入邮件地址,口令和POP3服务器地址
email = input('Email:')
password = input('Password:')
pop3_server = input('POP 3 server:')# 连接到POP3服务器
server = poplib.POP3_SSL(pop3_server)
# 可以打开或者关闭调试信息:
server.set_debuglevel(1)
# 可选:打印POP3服务器的欢迎文字
print(server.getwelcome().decode('utf-8'))# 身份认证:
server.user(email)
server.pass_(password)# stat()返回邮件数量和占用空间:
print('Message:%s.Size:%s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
print(mails)# 获取最新一封邮件,注意索引号从1开始:
index = len(mails)
resp, lines, octets = server.retr(index)# lines存储了邮件的原始文本的每一行,
# 可以获得整个邮件的原始文本:
msg_content = b'\r\n'.join(lines).decode('utf-8')
# 稍后解析出邮件:
msg = Parser().parsestr(msg_content)print_info(msg)
# 可以根据邮件索引号直接从服务器删除邮件:
# server.dele(index)
# 关闭连接:
server.quit()

原教程网址

这篇关于Python基础+刷廖雪峰教程笔记(七)——Pillow、requests、chardet、psutil、virtualenv、图形界面、TCP、UDP、SMTP、POP3的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/385014

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者