网络
理论模型,分为七层
- 物理层
- 数据链路层
- 传输层
- 会话层
- 表示层
- 应用层
实际应用,分为四层
- 链路层
- 网络层
- 传输层
- 应用层
IP地址
IPV4 地址族,4个字段组成,每个字段取值0-255
局域网IP,
192.168.XXX,XXX
本机本地地址,
127.0.0.1
IPV6 地址族,8个字段组成,每个字段取值0000-FFFF
端口
设备上每个应用需要与外界通信的地址
范围是 0-65535
知名端口 0-1023
非知名端口 1024-65535
套接字
我理解的套接字相当于与电话,而人则代表每个设备;当两个人需要通信时,需要电话.这就是套接字,而知道电话是不够的,还需要知道电话号码,这样才能确保电话打到你想通话的哪个人去
套接字对象由addr(host,port)地址定义,host是IP地址,port代表端口
host主要用来确定是IP地址为哪一个的电脑
port主要用来确定是该电脑上的哪个软件
域名
网络上计算名,或计算机组的名称
浏览器根据域名来查找对应的IP地址
查找本地hsot文件,检查是否有对应的IP地址
如果没有,查找本地DNS服务器.也就是运营商,检查是否有对应的IP
如果没有,本地DNS服务器查找根DNS服务器,检查是否有对应的IP
如果没有,根DNS服务器返回消息给本地DNS服务器,让其到域服务器查找IP
DNS 域名服务器系统
DNS将域名映射到对应的IP地址
DNS查询
- 递归查询
- 迭代查询
DNS 负载均衡
- 在DNS服务器中,为域名配置多个IP地址,根据距离远近,使用距离较近的IP
socket 库
协议家族
基于文件
- AF_UNIX 同台机器进程之间的通行
基于网络
AF__INET IPV4协议
AF_INET6 IPV6协议
网络协议
SOCK_STREAM
TCP协议 , 连接的套接字,只有当服务器与客户端连接后,才开始传输数据,可以确定数据的先后,也可以确定对方一定能接收到消息SOCK_DGRAM
UDP协议 , 无连接的套接字,服务器不断的监听某个套接字,从这个套接字获取或者发送数据,无法确定数据的先后顺序,也无法确定对方是否收到消息
直接调用的函数
getfqdn(name)
返回name的域名gethostbyname(hostname)
返回主机名的IPV4地址gethostname()
返回正在执行当前解释器的机器的主机名sethostname(name)
将本机主机名设置为name
socket(family,type)
套接字对象类函数
通用函数
bind(addr)
服务器绑定套接字,addr为(host,port)shuntdown()
关闭连接setblocking(boolean)
设置是否为阻塞模式settimeout(second)
设置超时时间None
阻塞模式0
非阻塞模式非0值
在规定时间后抛出异常
close()
关闭套接字
TCP 函数
listen()
监听地址,阻塞式的,不断循环监听地址accept()
服务器接收TCP客户端连接,返回新的连接的套接字和客户端的地址send(data)
发送数据sendall(data)
发送所有数据recv(bufsize)
接收TCP指定字节数的数据,一旦超过指定字节,就需要使用循环接收的方式接收完整的数据短链接法,发送数据完毕后关闭连接,让
recv()
函数获取连接关闭后自动取消阻塞模式末尾标志法,在末尾添加相应哨兵值,在
recv()
函数接收到哨兵值之后跳出接收数据的死循环,进行下一步操作负载长度法,提前告知长度,后根据长度判断是否跳出接收数据的死循环
connect(addr) 发起连接,向addr(host,port)发起连接
# 设置TCP服务器 import socket,time server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('127.0.0.1',12345)) server.listen() con,addr = server.accept() data = con.recv(1024) text = data.decode() print(text) data = '{0} : {1}'.format(time.ctime(),text).encode() con.sendall(data) con.close() server.close() # 设置TCP客户端 import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',12345)) text = 'Hello' client.sendall(text.encode()) data = client.recv(1024) print(data.decode()) client.close()
UDP 函数
recvfrom(bufsize)
接收UDP消息,返回连接的套接字地址sendto(data,addr)
将data数据发送到指定addr地址
# 设置UDP服务器 import socket,time server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server.bind(('127.0.0.1',12345)) data,addr = server.recvfrom(1024) text = data.decode() print(text) server.sendto('{0} : {1}'.format(time.ctime(),text).encode(),addr) server.close() # 设置UDP客户端 import socket client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) text = 'Hello' client.sendto(text.encode(),('127.0.0.1',12345)) data,addr = client.recvfrom(1024) print(data.decode())
socketserver 网络服务器框架模块
此模块提供网络服务器框架,可以使开发者更加快捷的开发出相应的网络服务器
服务器server对象
BaseServer(server_address,RequestHandlerClass)
所有Server对象的超类,同步处理请求,只有当一个请求处理完成之后才能处理下一个请求,每次只能完成一个请求RequestHandlerClass
处理请求类get_request()
接收客户端请求,并返回一个和客户端连接的套接字和客户端地址组成的元组server_bind(addr)
绑定地址serve_forever()
永远处理请求shutdown()
停止处理请求server_close()
关闭服务器
TCPServer (server_address,RequestHandlerClass)
继承于BaseServer,完善了TCP的相关方法,用于TCP服务器的创建,需要重写RequestHandlerClass
# 改写的上面采用socket库编写的TCP服务器 import socketserver,time class Server(socketserver.TCPServer): pass class MyRequestHandler(socketserver.StreamRequestHandler): def handle(self): data = self.request.recv(1024) text = data.decode() print(text) data = '{0} : {1}'.format(time.ctime(),text).encode() self.request.sendall(data) server = Server(('127.0.0.1',12345),MyRequestHandler) server.serve_forever() # 客户端 import socket with socket.socket(socket.AF_INET,socket.SOCK_STREAM)as client: client.connect(('127.0.0.1',12345)) client.sendall('hello'.encode()) data = client.recv(1024) print(data.decode())
UDPServer (server_address,RequestHandlerClass)
继承于BaseServer,完善了TCP的相关方法,用于UDP服务器的创建,需要重写RequestHandlerClass
# 服务器端 import socketserver,time class Server(socketserver.UDPServer): pass class MyRequestHandler(socketserver.DatagramRequestHandler): def handle(self): data,con = self.request print(data.decode()) data = '{0} : {1}'.format(time.ctime(),data.decode()).encode() con.sendto(data,self.client_address) server = Server(('127.0.0.1',12345),MyRequestHandler) server.serve_forever() # 客户端 import socket with socket.socket(socket.AF_INET,socket.SOCK_DGRAM)as client: data = 'hello'.encode() client.sendto(data,('127.0.0.1',12345)) data = client.recv(1024) print(data.decode())
适用于Unix平台的服务器,
UnixStreamServer (server_address,RequestHandlerClass)
UnixDatagramServer (server_address,RequestHandlerClass)
扩展功能的Mixin类
ForkingMixIn
多进程MIXIN类 ,可以与其他Server对象进行组合,适用于Linux 平台ThreadingMixIn
多线程MIXIN类,可以与其他Server对象进行组合
模块中已组合的扩展功能的服务器Server类
ForkingTCPServer(server_address,RequestHandlerClass)
多进程TCP服务器类,ForkingMixIn与TCPServer的组合ForkingUDPServer(server_address,RequestHandlerClass)
多进程UDP服务器类,ForkingMixIn与UDPServer的组合ThreadingTCPServer(server_address,RequestHandlerClass)
多线程TCP服务器类,ThreadingMixIn与TCPServer的组合ThreadingUDPServer(server_address,RequestHandlerClass)
多线程UDP服务器类,ThreadingMixIn与UDPServer的组合
请求处理类
BaseRequestHandler
处理请求类,其中定义了三个方法:setup
、handle
、finish
,都默认什么都不做,需要进行重写才能使用,是其他处理请求类的超类setup()
在handle方法之前调用的操作handle()
处理客户端请求的方法self.request
在不同Server服务器对象中具有不同的含义在TCPServer中,返回的是连接的客户端的套接字
在UDPServer中,返回的数据和连接的客户端套接字的一个元组
self.client_address
返回连接的客户端的地址self.rfile
可以从此属性中读取客户端发送的数据self.wfile
可以从此属性中写入服务器发给客户端的数据
finish()
在handle方法之后调用的操作
StreamRequestHandler
TCP处理请求类,是BaseRequestHandler
的子类,实现了关于TCP的相关方法,但需要实现handle
方法DatagramRequestHandler
UDP处理请求类,是BaseRequestHandler
的子类,实现了关于UDP的相关方法,但需要实现handle
方法
ftplib 模块
FTP (FileTransferProtocal) 文件传输协议
用户
Real账户 注册账户
Guest账户 临时账户
Anonymous 匿名账户
端口
21
端口为控制和命令端口20
端口为数据端口
FTP(host,usr,passwd)类
可以使用上下文管理协议 即
with
语句connect(host,port)
当没有在实例化FTP定义host时,则需要调用connect函数指定host和portlogin(usr,passwd)
给定账号密码登录,默认usr为anonymous,密码为anonymous@abort()
中断传输的文件sendcmd(cmd)
向服务器发送字符串命令,并返回响应代码retrbinary(cmd,callback)
以二进制形式接收文件,callback为用于接收文件的回调函数storbinary(cmd,fp)
以二进制形式上传文件,fp为二进制流retrlines(cmd,callback)
文本形式接收文件storlines(cmd,fp)
文本形式上传文件nlst()
返回文件名列表dir()
输出文件名列表到sys.stdoutrename(old,new)
重命名delete(filename)
删除文件pwd()
返回当前目录路径cwd(path)
将path设置为当前路径mkd(path)
创建新目录rmd(dirname)
删除dirname目录size(file)
返回文件大小quit()
关闭连接
import ftplib
#定义回调函数,将retrbinary函数接收到的数据写入本地文件中
def file(data):
with open(r'1.txt','wb')as f:
f.write(data)
# 指定FTP的host,获得套接字连接
con = ftplib.FTP('ftp.acc.umu.se')
# 连接FTP服务器
con.connect()
# 匿名账户登录
con.login()
con.dir()
# 下载文件,并调用file函数,将数据写入本地文件
con.retrbinary('RETR robots.txt',file)
# 关闭连接
con.close()
email 电子邮件模块
邮件传输模式
MUA 客户端程序
MTA (Mail Transfer Agent) 邮件传输代理,将发件人的邮件传输到发件人邮箱所在的服务器
MDA (Mail Delivery Agent) 邮件投递代理,发件人邮箱所在的服务器将邮件传输到收件人邮箱所在的服务器
MRA (Mail retrieva Agent) 邮件获取代理,收件人邮箱所在的服务器将邮件获取到收件人的邮箱地址中去
邮件传输使用MIME协议(多用途互联网邮件扩展),即电子邮件需要符合相应的MIME格式,才能进行发送
MIME协议中包括邮件标头、邮件消息体两个部分
邮件标头是key-value形式的字符串,每个标头独占一行
标头属性
From
发件人地址To
收件人地址Cc
抄送地址Subject
主题Content-Type
邮件体中包含的内容类型Content-Disposition: "attachment;filename='name.type'"
设置附件的下载方式和显示名称,其中attachment为附件方式打开,inline在邮件中打开
标头后跟着空行,下面为邮件消息体
消息体为邮件的具体内容
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
From: mine<xxxx@163.com>
To: you<xxx@qq.com>
Subject: =?utf-8?b?6YKu5Lu25qCH6aKY?=
6L+Z5piv5LiA5Lu95paH5pys5raI5oGv6YKu5Lu2
mime
构造邮件消息模块每个MIME类型的邮件消息对象只需要添加标头就可以作为邮件进行发送
multipart.MIMEMultipart(subtype ='mixed',boundary = None,spartparts = None,policy = compat32)
构造多个消息组成的总消息application.MIMEApplication(data,subtype = '八位字节流',encoder = email.encoders.encode_base64)
构造MIME邮件的Application消息audio.MIMEAudio(audiodata,subtype = None,encoder = email.encoders.encode_base64)
构造MIME邮件的Audio消息image.MIMEImage(imagedata,subtype = None,encoder = email.encoders.encode_base64)
构造MIME邮件的Image消息text.MIMEText(text,subtype ='plain',charset = None,*,policy = compat32)
构造一个MIME邮件的文本消息
message
邮件消息操作模块EmailMessage
类- 同下
Message
类as_string()
将消息对象作为字符串返回as_bytes()
将消息对象作为二进制流返回attach(message)
将message消息对象添加到当前消息对象items()
返回消息字段标题和值的元组的列表add_header(name,value)
添加邮件标头get_payload(i=None, decode=False)
返回邮件消息中每行内容所组成的列表,选择是否解码,一般需要填入Truewalk()
迭代器,迭代邮件消息的每一行
header
构造邮件标头模块Header(str=None,charset=None,header_name = NONE)
类 创建符合MIME协议的标头邮件标头可以通过两种方式进行构造
- 使用Header类进行构造,使用消息对象的属性进行赋值
msg['From'] = Header('xxxx@xx.com')
add_header(name,value)
函数,使用消息对象的函数进行赋值
msg.add_header('From','xxxx@xx.com')
邮件头的解码
decode_header(_header)
解码标头值,返回(decode_string,charset)组成的元组的列表
parse 邮件解码模块
BytesParser
二进制数据解析类parse(fp,headersonly = False )
解析二进制流,生成消息对象parsebytes(bytes,headersonly = False )
解析二进制数据,生成消息对象BytesHeaderParser()
解析标头二进制数据,返回标头对象
Parser
文本解析类parse(fp,headersonly = False )
解析文本流,生成消息对象parsestr(text,headersonly = False )
解析字符串,生成消息对象HeaderParser()
解析标头文本数据,返回标头对象
email模块下直接使用的解析类
message_from_bytes(s,_class = None,*,policy = policy.compat32 )
从二进制数据返回消息对象message_from_binary_file(fp,_class = None,*,policy = policy.compat32 )
从二进制流返回消息对象message_from_string(s,_class = None,*,policy = policy.compat32 )
从字符串返回消息对象message_from_file(fp,_class = None,*,policy = policy.compat32 )
从文本流中返回消息对象
smtplib SMTP简单邮件发送协议模块
SMTP 是负责邮件发送的协议
SMTP(host,port)
未加密的SMTP类可以使用上下文管理协议,即
with
语句connect(host,port)
连接到邮件服务器login(usr,pwd)
登录sendmail(from_addr,to_addr,msg)
发送邮件 msg为字符串,也就是需要通过消息对象调用as_string()方法quit()
关闭连接
SMTP_SSL(host,port)
ssl加密的SMTP类- 方法同上
发送一封纯文本格式的邮件
import smtplib from email.mime.text import MIMEText usr = 'xxx@163.com' pwd = 'xxxxx' to_mail = 'xxxxx@qq.com' # 构造文本消息邮件 msg = MIMEText('这是一份文本消息邮件') # 添加标头,使之变为邮件 msg['From'] = 'mine<{0}>'.format(usr) msg['To'] ='you<{0}>'.format(to_mail) msg['Subject'] = '邮件标题' # 发送邮件 with smtplib.SMTP()as con: # 变为debug模式,输出信息到控制台 con.set_debuglevel(1) # 连接邮件服务器 con.connect('smtp.163.com',25) # 登录 con.login(usr,pwd) # 发送邮件 con.sendmail(usr,to_mail,msg.as_string())
发送一封html格式的邮件
import smtplib from email.mime.text import MIMEText usr = 'xxx@163.com' pwd = 'xxxxx' to_mail = 'xxxxx@qq.com' # 构造html content ="<html><h1>这是一封html邮件</h1></html>" # 构造文本消息邮件 msg = MIMEText(content,'html','utf-8') # 添加标头,使之变为邮件 msg['From'] = 'mine<{0}>'.format(usr) msg['To'] ='you<{0}>'.format(to_mail) msg['Subject'] = '邮件标题' # 发送邮件 with smtplib.SMTP()as con: # 变为debug模式,输出信息到控制台 con.set_debuglevel(1) # 连接邮件服务器 con.connect('smtp.163.com', 25) # 登录 con.login(usr, pwd) # 发送邮件 con.sendmail(usr, to_mail, msg.as_string())
发送一封带有附件的邮件
import smtplib from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.mime.text import MIMEText usr = 'xxx@163.com' pwd = 'xxxxx' to_mail = 'xxxxx@qq.com' file = r'C:\Users\wudiz\Desktop\BaseServer.png' # 构造文本消息邮件 msg = MIMEMultipart('组合邮件') # 添加标头,使之变为邮件 msg['From'] = 'mine<{0}>'.format(usr) msg['To'] ='you<{0}>'.format(to_mail) msg['Subject'] = '邮件标题' # 构造文本 text = MIMEText('这是一封组合邮件') # 添加到msg组合消息对象中 msg.attach(text) # 构造附件 data = MIMEApplication(open(file,'rb').read()) # 添加MIME类型 data.add_header('Content-Type','application/octet-stream') # 设置附件 data.add_header('Content-Disposition',"attachment;filename='1.png'") # 添加到msg组合消息对象中 msg.attach(data) # 发送邮件 with smtplib.SMTP()as con: # 变为debug模式,输出信息到控制台 con.set_debuglevel(1) # 连接邮件服务器 con.connect('smtp.163.com', 25) # 登录 con.login(usr, pwd) # 发送邮件 con.sendmail(usr, to_mail, msg.as_string())
poplib 邮局协议模块
POP3 (Post Office Protocol - Version 3) 邮局协议版本3
POP3(host,port)
非加密POP3协议类set_debuglevel(1)
设置为调试模式stls()
以tls验证服务器getwelcome()
返回服务器的问候语字符串capa()
查询服务器的功能user(usr)
发送用户名pass_(pwd)
发送密码apop(usr,pwd)
使用APOP身份验证登录邮箱rpop(usr,pwd)
使用RPOP身份验证登录邮箱stat()
获取邮箱状态,返回(message_count,mailbox_size)的元组list(num)
获取邮件列表,如果设置了数字,则为数字所在编号邮件的内容,返回(response,num,octets)组成的元组 response为响应代码,num为邮件数量,octets为邮件大小retr(num)
检索邮件号,返回(response,lines,octets)组成的元组 lines为邮件每行组成的一个二进制列表,需要将其每个一行的组合起来才能变成二进制邮件,二进制邮件解码才能变为邮件dele(num)
标记邮件号,退出登录后删除rset()
立即删除标记的邮件号quit()
关闭连接
POP3_SSL(host,port)
SSL加密POP3协议类- 方法同上,现在一般使用该类
import poplib import email.header # 指定连接POP3服务器的host和port con = poplib.POP3('pop3.163.com',110) con.set_debuglevel(1) con.user('xxx') con.pass_('xxx') print(con.getwelcome()) # 选定邮箱中的第三封邮件 list = con.retr(3) # 设置二进制对象 data = b'' # 将二进制邮件的每一行写入data中,构建完整的邮件,按每行进行写入 for i in list[1]: data = data + i + b'\r\n' con.quit() # 解析为MIME消息对象 mail = email.message_from_bytes(data) # 解析标头中的标题 subject = email.header.decode_header(mail.get('Subject')) # 解码Subject标头值的字符串,并打印 print(subject[0][0].decode(subject[0][1])) # 按base64解码消息中的内容 content = mail.get_payload(decode=True) # 按UTF-8解码内容 print(content.decode())
http 超文本传输协议模块
http协议 是Hyper Text Transfer Protocol的缩写,简称为超文本传输协议
HTTP 的默认端口为80
HTTPS的默认端口为443
永远是客户端先发起请求,然后服务器响应请求
工作模式
建立连接
- 需要进行三次握手
- 客户端发送一个请求给服务端要求建立连接,进入等待状态(SYN_SEND)
- 服务端同意连接,发送一个响应给客户端,进入等待状态(SYN_SEND)
- 客户端接收这个响应,并与之建立连接(ESTABLISHED)
- 需要进行三次握手
接收数据
结束连接
请求报文
Request
请求行包括了请求方法,请求的url和http版本
请求方法
POST
向服务器提交数据 改GET
请求数据 查PUT
向服务器上传数据 增DELETE
请求服务器删除相应数据 删
请求头是key-value形式的,其中有以下属性
Host
请求资源的主机地址(必须存在)User-Agent
客户端的浏览器类型和版本Accept-Language
客户端申明自己接收的语言Accept
客户端申明自己想要接收数据的类型
- gzip
- deflate
Accept-Encoding
客户端申明自己接收的编码,通常指定压缩方法Cookie
发送cookie值Connection
连接方式
keep-alive
持久连接,当访问网页后连接不会关闭close
短连接,访问后就关闭,下次建立新的连接
Keep-Alive
保持连接的时间Referer
表示用户从该网页访问服务器Date
此报文产生的日期Authorization
客户端权限
空行,请求头和请求体之间必须有一个空行
请求体
响应报文
Response
响应行,包括了服务器软件的版本号,返回的状态码和相应短语
- 1XX 服务器已接收请求,继续处理
- 2XX 服务器请求已被处理
- 3XX 重定向
- 4XX 客户端错误
- 5XX 服务器错误
响应头
Location
重定向到另一个位置Server
服务器软件Date
此报文产生的日期Content-Length
响应体的长度Content-Type
声明发送响应体的类型和编码
空行
响应体,也就是服务器返回的请求资源
client
HTTP客户端模块模块HTTPConnection(host,port = None)
使用http协议连接服务器,返回一个HTTPConnection对象request(method,url,body = None,headers = {})
向服务器发送请求getresponse()
返回服务器相应set_debuglevel(1)
设置调试,将输出打印到控制台close()
关闭连接
HTTPSConnection(host,port = None)
使用https协议简介服务器,返回一个HTTPSConnection对象- 同上
HTTPResponse
对象read()
读取响应内容getheaders()
返回(属性,值)组成的元组列表getheader(name)
返回属性为name的值version
返回协议版本status
返回状态码
from http.client import * con = HTTPConnection('www.baidu.com') con.set_debuglevel(1) con.request('GET','http://www.baidu.com') response = con.getresponse() print(response.read()) response.close() con.close()
server
HTTP服务器模块服务器框架,此模块继承于socketserver.TCPServer
HTTPServer(server_address,RequestHandlerClass )
ThreadingHTTPServer(server_address,RequestHandlerClass )
处理HTTP请求类
BaseHTTPRequestHandler(request,client_address,server )
处理http请求的基本类command
请求形式path
请求的urlrequest_version
请求的http版本headers
请求报文的标头rfile
读取流wfile
写入流protocol_version
设置http版本协议默认为HTTP/1.0 短连接
HTTP/1.1 持久连接
send_response(code, message=None)
将响应状态码添加到缓冲区send_header(keyword, value)
将响应头添加到缓冲区end_headers()
发送空行version_string()
返回服务器软件的版本号date_time_string()
返回当前时间address_string()
返回呼叫的客户端地址
# 使用BaseHTTPRequestHandler创建HTTP服务器 from http.server import * html = ''' <html> <head> <title>Server</title> </head> <body> hello Python! </body> </html> ''' class myhttpserver(HTTPServer): pass class myhttphandler(BaseHTTPRequestHandler): def do_GET(self): url = self.path print(url) self.send_response(200) self.send_header('Hello','from server s welcome') self.end_headers() self.wfile.write(html.encode()) server = myhttpserver(('127.0.0.1',8888),myhttphandler) server.serve_forever() # 现在使用http://127.0.0.1:8888/打开网站吧
SimpleHTTPRequestHandler(request,client_address,server,directory = None )
普通HTTP请求处理类除了和
BaseHTTPRequestHandler
函数一致时,还另外定义了directory
属性,do_HEAD()
,do_GET()
方法directory
属性: 如果未指定,则代表提供的文件的目录是当前目录do_HEAD()
默认给定Server, Date, Connection, Content-Type, Content-Length, Last-Modified的标头do_GET()
如果代码所在位置有index.html,则会发送这个网页给客户端,否则将建立一个基于当前目录的类似于FTP的服务器
# 类似于FTP服务器 from http.server import * class myhttpserver(HTTPServer): pass class myhttphandler(SimpleHTTPRequestHandler): pass server = myhttpserver(('127.0.0.1',8888),myhttphandler) server.serve_forever() # 使用index.html # 在代码所在目录编写index.html文件 ''' <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My Page</title> </head> <body> <h1>Hello Python3!</h1> </body> </html> ''' # 再次运行服务器,使用浏览器访问,即可以查看结果
CGIHTTPRequestHandler(request,client_address,server )
调用CGI程序处理请求修改了
SimpleHTTPRequestHandler
定义的do_GET和do_HEAD方法,更改为使用CGI程序输出的方法 如果没有CGI程序,就会像SimpleHTTPRequestHandler
一样,建立一个类似于FTP的服务器cgi_directories
包含CGI程序的目录,默认为当前目录下的[‘/cgi-bin’, ‘/htbin’]do_POST()
定义了POST方法,允许客户端发送信息到服务器得CGI程序中,如果POST到非CGI程序上,会提示501错误
# 先定义一个能POST请求的html页面,命名为index.html ''' <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>请输入文字</h1> <form action="http://127.0.0.1:8888/cgi-bin/cc.py" method="post" id="myform"> <p><input type="text" name="text"></p> </form> <button type="submit" form="myform">发送</button> </body> </html> ''' # CGI服务器 from http.server import * class myhttpserver(HTTPServer): pass class myhttphandler(CGIHTTPRequestHandler): pass server = myhttpserver(('127.0.0.1',8888),myhttphandler) server.serve_forever() # 编写CGI程序,放入cgi-bin目录下 import cgi import cgitb cgitb.enable() html = ''' <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>你提交的是:</h1> <h2>{0}</h2> </body> </html> '''.format(cgi.FieldStorage().getfirst('text')) print(html) # 运行程序,会输出你在页面上输入的信息
cookies HTTP状态管理,主要用于服务器端创建cookie
Netscape形式的cookie :
Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
- NAME为该cookie的名称,为必填选项
- Expires为该cookie的终止日期 形式为
星期几,DD-MM-YY HH:MM:SS GMT
,GMT表示格林尼治时间,如不填该cookie不会保存在硬盘中,该cookie随着浏览器的关闭消失 - Domain为该cookie的作用域,如果不填,则为该服务器的域名
- Path为该服务器下那些页面可以获取该cookie,填’/’为该服务器下所有页面
- 加密协议,当前只有一种,即HTTPS
RFC2109中定义的cookie :
Set-Cookie: Name = Value; Comment = value; Domain = value; Max-Age = value; Path = Value;Secure; Version = 1 * DIGIT;
- NAME必须为一个JSESSIONID,必填
- Domain为该cookie的作用域,必须以
.
开始 - Max-Age为该cookie的生存时间,以秒为单位
- Secure必填
BaseCookie(input) 类
该类用于创建cookie,是一个类似于字典的对象,每向其中添加一个key,value值就会创建一个
Set-Cookie: key:value
的响应头信息,其中key必须是一个字符串,value是一个Morsel对象,可以向其中添加其他头信息操作定义的方法:
value_decode(val )
待续value_encode(val )
待续output(attrs = None,header ='Set-Cookie:',sep ='\ r \ n' )
将cookie对象作为符合html标准的字符串输出load(rawdata )
将接收到的字符串对象解析为cookie
SimpleCookie(input) 类
- BaseCookie的子类,重写实现了value_decode,value_encode方法
Morsel 类
常用常量
- expires
- path
- comment
- domain
- max-age
- secure
- version
- httponly
常用方法
set(key,value,coded_value )
添加key,设置value值output(attrs = None,header ='Set-Cookie:' )
作为html形式输出update(dict)
将字典中的key和value添加到对象中
# 创建一个cookie,使用了几种不同的方式
from http.cookies import *
cookie = SimpleCookie()
cookie["name"] = "mycookie"
dict = {'path':'/','comment':'这是cookie'}
cookie['name'].update(dict)
cookie['name']['domain'] = '127.0.0.1'
cookie['whatever'] = 'i dont know'
print(cookie)
cookiejar 持久化的cookie操作模块,主要用于客户端的操作,如爬虫
具体应用在urllib模块中
CookieJar 类,该类用来操作储存在内存中的cookie
MozillaCookieJar(filename,delayload = None,policy = None )
类 可以从加载和保存的Cookie到磁盘Mozilla的cookies.txtLWPCookieJar(filename,delayload = None,policy = None )
类 可以从加载和cookie保存到磁盘与的libwww-perl的库的兼容格式Set-Cookie3的文件格式add_cookie_header(requst)
向request对象中添加一个cookie对象extract_cookies(response,request)
从http的response中提取cookie并储存在cookieJar对象中make_cookies(response,request)
从http的response中提取cookieset_cookie(cookie)
向cookieJar添加cookie对象clear(keys)
清除名为key的cookie参数
# 获取cookie from urllib.request import * from http.cookiejar import * url = 'http://www.baidu.com' headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36' } request = Request(url=url, headers=headers) response = urlopen(request) cookie = CookieJar() cookie.extract_cookies(response,request) print(cookie)
FileCookieJar 类,该类主要用于操作已经持久到文件中的cookie
具有以上CookieJar的所有函数之外,额外添加了以下函数
filename
默认保存cookie的文件save(filename = None,ignore_discard = False,ignore_expires = False )
将cookie保存在文件中,方法未实现,但是在CookieJar的子类LWPCookieJar
,MozillaCookieJar
实现了其方法load(filename = None,ignore_discard = False,ignore_expires = False )
从文件中加载cookierevert(filename = None,ignore_discard = False,ignore_expires = False )
清除所有cookie,并从文件中重新加载cookie
# 以LWPCookieJar为例,将cookie保存到文件中 from urllib.request import * from http.cookiejar import LWPCookieJar url = 'http://www.baidu.com' headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36' } request = Request(url=url, headers=headers) response = urlopen(request) filecookie = LWPCookieJar() filecookie.extract_cookies(response,request) filecookie.save(r'a.txt')
cgi 通用网关接口支持模块、cgitb CGI脚本的回溯管理器模块
cgitb用于调试cgi脚本程序
- enable() 将错误信息输出到访问的html页面
cgi是服务器运行时调用的外部程序,通过通用CGI接口与服务器通信,默认放在[‘/cgi-bin’, ‘/htbin’]目录下
通常由两个部分组成
- 第一部分为响应头,用于告知客户端传输数据的类型,后面跟空行,用于将响应体分开
print("Content-Type: text/html") print()
- 第二部分为响应的数据
常用的头部信息
Content-type:
请求的资源类型对应的MIME信息Content-Disposition: disposition-type;filename-parm
响应请求资源的下载方式
- 其中disposition-type可以为
attachment
弹出对话框下载,或者inline
直接显示在页面上 - 其中filename-parm是
filename = xx
规定下载时的文件名,用于规定下载时的文件名称
- 其中disposition-type可以为
Expires:
响应过期的日期和时间Location:
重定向到url的新的资源Last-modified:
请求资源的最后修改日期Content-length:
请求的内容长度Set-Cookie:
设置cookie
常用方法
parse_header(str)
将MIME标头解析为字典parse_multipart(fp,pdict,encoding =“utf-8”,errors =“replace” )
返回{字段名称:数据}的字典print_form(form)
将接收的表格以html形式格式化print_directory()
以html形式格式化当前目录test()
将cgi作为主程序进行测试
FieldStorage(,key,filename,value)
类- 此类用于获取html中form输出的表单数据,仅需要实例化一次
FiledStorage()[key]
代表了表单中名为key的标签整体FiledStorage()[key].value
代表了表单中key标签的值getvalue(key)
当有多个值时,返回一个字符串列表getlist(key)
返回名key标签下的所有字符串组成的列表
编写一个最简单的CGI程序,如果需要访问该CGI程序,需要先启动一个CGI服务器,在前面已经学到过,在
HTTPServer
中导入CGIHTTPRequestHandler
创建一个最简单的CGI服务器
import cgi
import cgitb
cgitb.enable()
print("Content-Type: text/html")
print()
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>My First Python CGI</h1>
</body>
</html>
'''
print(html)
CGI获取客户端使用
GET
方式发送的数据请求的url为
http://127.0.0.1:8888/cgi-bin/cc.py?usr=jack&pwd=123456
,可以看出,传递的数据以明文方式出现在url中传递的数据包含在请求头中
在GET方式中,不应包含敏感数据,敏感数据应使用POST方式发送
# index.html中的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>请输入文字</h1>
<form action="http://127.0.0.1:8888/cgi-bin/cc.py" method="GET" id="myform">
<h2>账户:<input type="text" name="usr"></h2>
<h2>密码:<input type="text" name="pwd"></h2>
<button type="submit">发送</button>
</form>
</body>
</html>
# CGI程序
import cgi
import cgitb
cgitb.enable()
print("Content-Type: text/html")
print()
form = cgi.FieldStorage()
usr = form.getvalue('usr')
pwd = form.getvalue('pwd')
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>获取到的数据是:</h1>
<p>{0}</p>
<p>{1}</p>
</body>
</html>
'''.format(usr,pwd)
print(html)
POST方式在上面的
CGIHTTPRequestHandler
中,不多进行赘述- POST方式发送数据,不会明文出现在url中,会存在于请求体的form-data中,所以相对于GET方式,要安全的多
传递checkbox数据
# index.html内容是
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>请勾选</h1>
<form action="/cgi-bin/cc.py" method="POST" id="myform">
<input type="checkbox" name="usr" value="on" > usr
<input type="checkbox" name="pwd" value="on" > pwd
<button type="submit">发送</button>
</form>
</body>
</html>
# cgi程序
import cgi
import cgitb
cgitb.enable()
print("Content-Type: text/html")
print()
form = cgi.FieldStorage()
google = form.getvalue('google')
if google == on:
r1 = 'google被选中'
else:
r1 = 'google没有被选中'
baidu = form.getvalue('baidu')
if baidu == 'on':
r2 = 'baidu被选中'
else:
r2 = 'baidu没有被选中'
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>获取到的数据是:</h1>
<p>{0}</p>
<p>{1}</p>
</body>
</html>
'''.format(r1,r2)
print(html)
- 传递radio数据
# index.html中的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>请勾选</h1>
<form action="/cgi-bin/cc.py" method="POST" id="myform">
<input type="radio" name="site" value="google" > 谷歌
<input type="radio" name="site" value="baidu" > 百度
<button type="submit">发送</button>
</form>
</body>
# cgi程序内容
import cgi
import cgitb
cgitb.enable()
print("Content-Type: text/html")
print()
form = cgi.FieldStorage()
value = form.getvalue('site')
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>获取到的数据是:</h1>
<p>{0}</p>
</body>
</html>
'''.format(value)
print(html)
- 传递Textatrea数据
# index.html中的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>请输入内容</h1>
<form action="/cgi-bin/cc.py" method="post">
<Textarea name="text" cols="40" rows="4" >在这里输入内容....
</Textarea>
<input type="submit">
</form>
</body>
</html>
# cgi程序中的内容
import cgi
import cgitb
cgitb.enable()
print("Content-Type: text/html")
print()
form = cgi.FieldStorage()
value = form.getvalue('text')
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>获取到的数据是:</h1>
<p>{0}</p>
</body>
</html>
'''.format(value)
print(html)
- 传递下拉数据
# index.html中的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>请输入内容</h1>
<form action="/cgi-bin/cc.py" method="post">
<select name="sel" >
<option value="baidu">百度</option>
<option value="google">谷歌</option>
</select>
<input type="submit">
</form>
</body>
</html>
# cgi程序中的内容
import cgi
import cgitb
cgitb.enable()
print("Content-Type: text/html")
print()
form = cgi.FieldStorage()
value = form.getvalue('sel')
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>获取到的数据是:</h1>
<p>{0}</p>
</body>
</html>
'''.format(value)
print(html)
- 设置cookie
# 直接访问cgi程序,设置cookie,通过chrome即可查看设置的cookie
# cgi程序为:
import cgi,datetime
import cgitb
from http.cookies import *
cgitb.enable()
name = 'cgicookie'
now = datetime.datetime.utcnow()
time = (now + datetime.timedelta(days=10)).strftime('%a,%d-%m-%Y %H:%M:%S')
path = '/'
domain = '127.0.0.1'
cookie = SimpleCookie()
cookie['NAME'] = name
cookie['NAME']['expires'] = time
cookie['NAME']['path'] = path
cookie['NAME']['domain'] = domain
print("Content-Type: text/html")
print(cookie)
print()
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>cookie已经设置</h1>
</body>
</html>
'''
print(html)
- 文件上传
# index.html中的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>文件上传</h1>
<form action="/cgi-bin/cc.py" method="post" enctype="multipart/form-data">
<p><input type="file" name="upload"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
# cgi程序为:
import cgi
import cgitb
cgitb.enable()
form = cgi.FieldStorage()
file = form['upload']
open(file.filename,'wb').write(file.value)
print("Content-Type: text/html")
print()
html ='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>已接收上传文件</h1>
</body>
</html>
'''
print(html)
- 文件下载
# 直接访问cgi程序,以下为cgi程序的内容
import cgi
import cgitb
cgitb.enable()
data = open(r'chromedriver.exe','rb').read()
print("Content-Disposition: attachment;filename=chromedriver.exe")
print()
print(data)
urlib URL处理模块
url 同一资源定位符
格式:
prot_sch://net_loc/path;params?query#frag
prot_sch 网络协议
net_loc
服务器所在地址 可进一步分割为多个组件user
用户名passwd
用户密码host
服务器所在地址或服务器计算机名称(必须)port
端口号(默认80)
path
文件或者CGI应用的路径params
可选参数query
连接符(&)分割的键值对frag
文档中的特定锚点
request
请求url模块Request(url,data = None,headers = {})
类 构造一个复杂的请求对象,可以供urlopen函数使用full_url
返回请求的urlhost
返回请求的主机地址method
返回请求的方法add_header(key,value)
添加请求标头has_header(key)
检查是否有key请求标头remove_header(key)
删除名为key的请求标头get_header(key)
返回名为key的标头值header_items()
返回所有请求标头和值
urlopen(url,data)
打开一个url,发送data,返回HTTPResponse对象在HTTPResponse的基础上添加了以下方法
geturl()
返回请求资源的urlinfo()
返回页面的元信息getcode()
返回响应的状态码
build_opener([handler,])
返回一个OpenerDirector对象install_opener(OpenerDirector对象)
将OpenerDirector对象设置为全局默认OpenerDirector
类 该类主要用于通过添加相应的Handler构建一个自定义的urlopen对象,实现各种不同的访问需求add_handler(Handler)
添加一个处理行为给OpenerDirector对象open(url)
打开URL,返回HTTPResponse对象error(proto,* args)
给定相应的参数处理错误
HTTPRedirectHandler
类,重定向处理程序HTTPHandler
类,发送HTTP请求,可以为GET或POSTHTTPSHandler(debuglevel = 0,context = None,check_hostname = None )
类,发送HTTPS请求,可以为GET或者POSTHTTPBasicAuthHandler(password_mgr = None )
类,请求时需要账户密码登录ProxyHandler(proxies = None )
类,代理请求,需要在实例化的时候添加代理字典ProxyBasicAuthHandler(password_mgr = None )
类,需要用户名密码登录的代理HTTPCookieProcessor(cookiejar)
类,Cookie处理FileHandler
类,打开文件FTPHandler
类,打开FTP文件HTTPErrorProcessor
类,处理HTTP异常http_response(req,reply)
对于错误,返回reply对象https_response(req,reply)
对于错误,返回reply对象
密码管理器对象,用于添加到各项需要认证的处理程序中
HTTPPasswordMgr
类,映射数据库,通过(realm, uri) -> (user, password)add_password(realm, uri, user, passwd)
添加相应的账户,密码到管理器对象中,realm是指主机服务器的域信息一般为None,uri指服务器find_user_password(realm, authuri)
返回指定服务器域中是否有定义的账户,如果有返回(账户,密码)元组,没有返回None
HTTPPasswordMgrWithDefaultRealm
类- 同上两个方法
HTTPPasswordMgrWithPriorAuth
类同上两个方法
update_authenticated(self, uri, is_authenticated=False)
is_authenticated(self, authuri)
使用默认函数访问url
from urllib.request import *
url = 'http://www.baidu.com'
response = urlopen(url)
print(response.read().decode())
使用Request对象构造请求
from urllib.request import *
url = 'http://www.baidu.com'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
'Host': 'www.baidu.com'
}
request = Request(url,headers=headers)
response = urlopen(request)
print(response.read().decode())
构造自定的访问器,获取cookie
from urllib.request import *
from http.cookiejar import *
url = 'http://www.baidu.com'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
'Host': 'www.baidu.com'
}
cookie = CookieJar()
openr = build_opener(HTTPCookieProcessor(cookie))
request = Request(url,headers=headers)
response = openr.open(request)
print(cookie)
print(response.read().decode())
# 或者
from urllib.request import *
from http.cookiejar import *
url = 'http://www.baidu.com'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
'Host': 'www.baidu.com'
}
cookie = CookieJar()
openr = OpenerDirector()
openr.add_handler(HTTPCookieProcessor(cookie))
openr.add_handler(HTTPHandler())
request = Request(url,headers=headers)
response = openr.open(request)
print(cookie)
构造自定访问器,使用用户名、密码登录
from urllib.request import *
from urllib.parse import *
login_url = r'https://www.douban.com/accounts/login'
data = urlencode({
'source': 'index_nav',
'form_email': 'XXXXX',
'form_password': 'XXXXX'
}).encode()
request = Request(url=login_url,data=data)
response = urlopen(request)
print(response.read().decode('utf-8','ignore'))