網絡
-
理論模型,分為七層
- 物理層
- 數據鏈路層
- 傳輸層
- 會話層
- 表示層
- 應用層
-
實際應用,分為四層
- 鏈路層
- 網絡層
- 傳輸層
- 應用層
-
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查詢
- 遞歸查詢
image- 迭代查詢
image -
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)
image -
-
擴展功能的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) 郵件獲取代理,收件人郵箱所在的服務器將郵件獲取到收件人的郵箱地址中去
image -
郵件傳輸使用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
[圖片上傳失敗...(image-49e11e-1536041654038)]
-
請求行包括了請求方法,請求的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
[圖片上傳失敗...(image-8a1169-1536041654038)]
-
響應行,包括了服務器軟件的版本號,返回的狀態碼和相應短語
- 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'))