Web基础(三)Python Web
文章目錄
- Python Web基礎(chǔ)
- 1. WSGI
- 1.1 概述
- 1.2 實(shí)現(xiàn)原理
- 1、WSGI Server/gateway
- 2、WSGI Application
- 3、WSGI MiddleWare
- 1.3 測(cè)試 WSGI服務(wù)器
- 代碼簡(jiǎn)析
- 1.4 實(shí)現(xiàn)WSGI服務(wù)器
- 1.5 生產(chǎn)環(huán)境中的Web服務(wù)器
- [Gunicorn](https://github.com/benoitc/gunicorn "Gunicorn")
- [uWSGI ](https://github.com/unbit/uwsgi-docs "uWSGI ")
- [bjoern](https://github.com/jonashaag/bjoern#libev "bjoern")
- 2. Web應(yīng)用開(kāi)發(fā)
- 2.1 服務(wù)器架構(gòu)
- 2.1.1 Nginx
- 反向代理
- Nginx的優(yōu)勢(shì)
- 附錄
Python Web基礎(chǔ)
Web應(yīng)用的本質(zhì):
1. 瀏覽器發(fā)送一個(gè)HTTP請(qǐng)求
2. 服務(wù)器收到請(qǐng)求,生成一個(gè)HTML文檔
3. 服務(wù)器把HTML文檔作為HTTP響應(yīng)的Body發(fā)送給瀏覽器
4. 瀏覽器收到HTTP響應(yīng),從HTTP Body取出HTML文檔并顯示
所以,最簡(jiǎn)單的Web應(yīng)用就是先把HTML用文件保存好,用一個(gè)現(xiàn)成的HTTP服務(wù)器軟件,接收用戶請(qǐng)求,從文件中讀取HTML,返回。我們上兩篇博客已經(jīng)詳細(xì)講解并實(shí)現(xiàn)了這樣的HTTP服務(wù)器zjhttp,除此外Apache、Nginx、Lighttpd等這些常見(jiàn)的靜態(tài)服務(wù)器就是干這件事情的。
如果要?jiǎng)討B(tài)生成HTML,就需要把上述步驟自己來(lái)實(shí)現(xiàn)。不過(guò),接受HTTP請(qǐng)求、解析HTTP請(qǐng)求、發(fā)送HTTP響應(yīng)都是苦力活,如果我們自己來(lái)寫(xiě)這些底層代碼,還沒(méi)開(kāi)始寫(xiě)動(dòng)態(tài)HTML呢,就得花個(gè)把月去讀HTTP規(guī)范。正確的做法是底層代碼由專門(mén)的服務(wù)器軟件實(shí)現(xiàn),我們用Python專注于生成HTML文檔。
1. WSGI
Web服務(wù)器網(wǎng)關(guān)接口(Python Web Server Gateway Interface,縮寫(xiě)為WSGI)是為Python語(yǔ)言定義的Web服務(wù)器和Web應(yīng)用程序或框架之間的一種簡(jiǎn)單而通用的接口。自從WSGI被開(kāi)發(fā)出來(lái)以后,許多其它語(yǔ)言中也出現(xiàn)了類似接口。
以前,如何選擇合適的Web應(yīng)用程序框架成為困擾Python初學(xué)者的一個(gè)問(wèn)題,這是因?yàn)?#xff0c;一般而言,Web應(yīng)用框架的選擇將限制可用的Web服務(wù)器的選擇,反之亦然。那時(shí)的Python應(yīng)用程序通常是為CGI,FastCGI,mod_python中的一個(gè)而設(shè)計(jì),甚至是為特定Web服務(wù)器的自定義的API接口而設(shè)計(jì)的。
WSGI(有時(shí)發(fā)音作’wiz-gee’)是作為Web服務(wù)器與Web應(yīng)用程序或應(yīng)用框架之間的一種低級(jí)別的接口,以提升可移植Web應(yīng)用開(kāi)發(fā)的共同點(diǎn)。WSGI是基于現(xiàn)存的CGI標(biāo)準(zhǔn)而設(shè)計(jì)的。WSGI沒(méi)有官方的實(shí)現(xiàn), 因?yàn)閃SGI更像一個(gè)協(xié)議。只要遵照這些協(xié)議,WSGI應(yīng)用(Application)都可以在任何服務(wù)器(Server)上運(yùn)行, 反之亦然。WSGI就是Python的CGI包裝,相對(duì)于Fastcgi是PHP的CGI包裝
1.1 概述
WSGI區(qū)分為兩個(gè)部分
1. 為“服務(wù)器”或“網(wǎng)關(guān)”。它用于接收、整理客戶端發(fā)送的請(qǐng)求
2. 為“應(yīng)用程序”或“應(yīng)用框架”。處理服務(wù)器程序傳遞過(guò)來(lái)的請(qǐng)求
如上圖,Web服務(wù)器即第一部分,接收、整理客戶端發(fā)送的請(qǐng)求,咱們前兩篇博客使用C語(yǔ)言實(shí)現(xiàn)的zjhttp就是屬于Web服務(wù)器部分;Web框架即為第二部分,即所謂的Web應(yīng)用程序。開(kāi)發(fā)Web應(yīng)用程序的時(shí)候,通常會(huì)把常用的功能封裝起來(lái),成為各種框架,比如Flask,Django,Tornado(使用某框架進(jìn)行web開(kāi)發(fā),相當(dāng)于開(kāi)發(fā)服務(wù)端的應(yīng)用程序,處理后臺(tái)邏輯)。但是,服務(wù)器程序和應(yīng)用程序互相配合才能給用戶提供服務(wù),而不同應(yīng)用程序(不同框架)會(huì)有不同的函數(shù)、功能。 此時(shí),我們就需要一個(gè)標(biāo)準(zhǔn),讓服務(wù)器程序和應(yīng)用程序都支持這個(gè)標(biāo)準(zhǔn),那么,二者就能很好的配合了,這個(gè)標(biāo)準(zhǔn)就是 WSGI。
在處理一個(gè)WSGI請(qǐng)求時(shí),服務(wù)器會(huì)為應(yīng)用程序提供環(huán)境信息及一個(gè)回調(diào)函數(shù)(Callback Function)。當(dāng)應(yīng)用程序完成處理請(qǐng)求后,透過(guò)前述的回調(diào)函數(shù),將結(jié)果回傳給服務(wù)器。
所謂的 WSGI 中間件同時(shí)實(shí)現(xiàn)了API的兩方,因此可以在WSGI服務(wù)器和WSGI應(yīng)用之間起調(diào)解作用。從Web服務(wù)器的角度來(lái)說(shuō),中間件扮演應(yīng)用程序,而從應(yīng)用程序的角度來(lái)說(shuō),中間件扮演服務(wù)器。“中間件”組件可以執(zhí)行以下功能:
1. 重寫(xiě)環(huán)境變量后,根據(jù)目標(biāo)URL,將請(qǐng)求消息路由到不同的應(yīng)用對(duì)象。
2. 允許在一個(gè)進(jìn)程中同時(shí)運(yùn)行多個(gè)應(yīng)用程序或應(yīng)用框架。
3. 負(fù)載均衡和遠(yuǎn)程處理,通過(guò)在網(wǎng)絡(luò)上轉(zhuǎn)發(fā)請(qǐng)求和響應(yīng)消息。
4. 進(jìn)行內(nèi)容后處理,例如應(yīng)用XSLT樣式表。
1.2 實(shí)現(xiàn)原理
WSGI 將 Web 組件分為三類
- web服務(wù)器
- web中間件
- web應(yīng)用程序
wsgi基本處理模式為:
WSGI Server -> WSGI Middleware -> WSGI Application
1、WSGI Server/gateway
wsgi server可以理解為一個(gè)符合wsgi規(guī)范的web server,接收request請(qǐng)求,封裝一系列環(huán)境變量,按照wsgi規(guī)范調(diào)用注冊(cè)的wsgi app,最后將response返回給客戶端。以python自帶的wsgiref為例,wsgiref是按照wsgi規(guī)范實(shí)現(xiàn)的一個(gè)簡(jiǎn)單wsgi server。它的代碼不復(fù)雜。
2、WSGI Application
wsgi application就是一個(gè)普通的callable對(duì)象,當(dāng)有請(qǐng)求到來(lái)時(shí),wsgi server會(huì)調(diào)用這個(gè)wsgi app。這個(gè)對(duì)象接收兩個(gè)參數(shù),通常為environ,start_response。environ就像前面介紹的,可以理解為環(huán)境變量,跟一次請(qǐng)求相關(guān)的所有信息都保存在了這個(gè)環(huán)境變量中,包括服務(wù)器信息,客戶端信息,請(qǐng)求信息。start_response是一個(gè)callback函數(shù),wsgi application通過(guò)調(diào)用start_response,將response headers/status 返回給wsgi server。此外這個(gè)wsgi app會(huì)return 一個(gè)iterator對(duì)象 ,這個(gè)iterator就是response body。這么空講感覺(jué)很虛,對(duì)著下面這個(gè)簡(jiǎn)單的例子看就明白很多了。
3、WSGI MiddleWare
有些功能可能介于服務(wù)器程序和應(yīng)用程序之間,例如,服務(wù)器拿到了客戶端請(qǐng)求的URL, 不同的URL需要交由不同的函數(shù)處理,這個(gè)功能叫做 URL Routing,這個(gè)功能就可以放在二者中間實(shí)現(xiàn),這個(gè)中間層就是 middleware。middleware對(duì)服務(wù)器程序和應(yīng)用是透明的,也就是說(shuō),服務(wù)器程序以為它就是應(yīng)用程序,而應(yīng)用程序以為它就是服務(wù)器。這就告訴我們,middleware需要把自己偽裝成一個(gè)服務(wù)器,接受應(yīng)用程序,調(diào)用它,同時(shí)middleware還需要把自己偽裝成一個(gè)應(yīng)用程序,傳給服務(wù)器程序。
論是服務(wù)器程序、middleware 還是應(yīng)用程序,都在服務(wù)端,為客戶端提供服務(wù),之所以把他們抽象成不同層,就是為了控制復(fù)雜度,使得每一次都不太復(fù)雜,各司其職。
1.3 測(cè)試 WSGI服務(wù)器
原理說(shuō)得太多未免過(guò)于抽象,現(xiàn)在使用Python內(nèi)置的純Python代碼編寫(xiě)的wsgiref服務(wù)器來(lái)體驗(yàn)一把WSGI服務(wù)器是如何工作的
-
編寫(xiě)hello.py 作為一個(gè)Web應(yīng)用程序
def application(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])return [b'<h1>Hello, World!</h1>'] -
編寫(xiě)server.py作為一個(gè)WSGI服務(wù)器
from wsgiref.simple_server import make_server # 導(dǎo)入編寫(xiě)的application函數(shù) from hello import application# 創(chuàng)建一個(gè)服務(wù)器,IP地址為空,端口是8000,傳入函數(shù)application httpd = make_server('', 8000, application) print('Serving HTTP on port 8000...') # 開(kāi)始監(jiān)聽(tīng)HTTP請(qǐng)求: httpd.serve_forever() -
啟動(dòng)WSGI服務(wù)器
python server.py -
使用客戶端訪問(wèn)
打開(kāi)瀏覽器,輸入http://localhost:8000/ ,在瀏覽器正常顯示“Hello, World!”
代碼簡(jiǎn)析
上面的application()函數(shù)就是符合WSGI標(biāo)準(zhǔn)的一個(gè)HTTP處理函數(shù),它接收兩個(gè)參數(shù):
- environ:一個(gè)包含所有HTTP請(qǐng)求信息的dict對(duì)象
- start_response:一個(gè)發(fā)送HTTP響應(yīng)的函數(shù)
而在application()函數(shù)中又調(diào)用了start_response函數(shù)
該函數(shù)發(fā)送了HTTP響應(yīng)的Header,注意Header只能發(fā)送一次,也就是只能調(diào)用一次start_response()函數(shù)。start_response()函數(shù)接收兩個(gè)參數(shù),一個(gè)是HTTP響應(yīng)碼,一個(gè)是一組list表示的HTTP Header,每個(gè)Header用一個(gè)包含兩個(gè)str的tuple表示。
通常情況下,都應(yīng)該把Content-Type頭發(fā)送給瀏覽器。其他很多常用的HTTP Header也應(yīng)該發(fā)送。然后,函數(shù)的返回值b'<h1>Hello, web!</h1>'將作為HTTP響應(yīng)的Body發(fā)送給瀏覽器。
有了WSGI,我們關(guān)心的就是如何從environ這個(gè)dict對(duì)象拿到HTTP請(qǐng)求信息,然后構(gòu)造HTML,通過(guò)start_response()發(fā)送Header,最后返回Body。
整個(gè)application()函數(shù)本身沒(méi)有涉及到任何解析HTTP的部分,也就是說(shuō),底層代碼不需要我們自己編寫(xiě),我們只負(fù)責(zé)在更高層次上考慮如何響應(yīng)請(qǐng)求就可以了。
需要注意的是,application()函數(shù)必須由WSGI服務(wù)器來(lái)調(diào)用。有很多符合WSGI規(guī)范的服務(wù)器,我們可以挑選一個(gè)來(lái)用。但是我們僅將內(nèi)置的wsgiref服務(wù)器用于測(cè)試,使我們編寫(xiě)的Web應(yīng)用程序立馬跑起來(lái)。
1.4 實(shí)現(xiàn)WSGI服務(wù)器
為了了解wsgi的工作原理,我們可以參照wsgiref源碼,使用Python簡(jiǎn)單實(shí)現(xiàn)一個(gè)WSGI服務(wù)器
import socket import StringIO import sysclass WSGIServer(object):address_family = socket.AF_INETsocket_type = socket.SOCK_STREAMrequest_queue_size = 1def __init__(self, server_address):# 創(chuàng)建socket,利用socket獲取客戶端的請(qǐng)求self.listen_socket = listen_socket = socket.socket(self.address_family, self.socket_type)# 設(shè)置socket的工作模式listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 綁定socket地址listen_socket.bind(server_address)# socket active, 監(jiān)聽(tīng)文件描述符listen_socket.listen(self.request_queue_size)# 獲得serve的host name和porthost, port = self.listen_socket.getsockname()[:2]self.server_name = socket.getfqdn(host)self.server_port = portself.headers_set = []def set_app(self, application):self.application = application #啟動(dòng)WSGI server服務(wù),不停的監(jiān)聽(tīng)并獲取socket數(shù)據(jù)。def serve_forever(self):listen_socket = self.listen_socketwhile True:self.client_connection, client_address = listen_socket.accept() #接受客戶端請(qǐng)求#處理請(qǐng)求self.handle_one_request()def handle_one_request(self):self.request_data = request_data = self.client_connection.recv(1024)self.parse_request(request_data)# Construct environment dictionary using request dataenv = self.get_environ()#給flask\tornado傳遞兩個(gè)參數(shù),environ,start_responseresult = self.application(env, self.start_response)self.finish_response(result)#處理socket的http協(xié)議def parse_request(self, data):format_data = data.splitlines()if len(format_data):request_line = data.splitlines()[0]request_line = request_line.rstrip('\r\n')(self.request_method, self.path, self.request_version) = request_line.split() ## ['GET', '/', 'HTTP/1.1']# 獲取environ數(shù)據(jù)并設(shè)置當(dāng)前server的工作模式def get_environ(self):env = {}env['wsgi.version'] = (1, 0)env['wsgi.url_scheme'] = 'http'env['wsgi.input'] = StringIO.StringIO(self.request_data)env['wsgi.errors'] = sys.stderrenv['wsgi.multithread'] = Falseenv['wsgi.multiprocess'] = Falseenv['wsgi.run_once'] = False# Required CGI variablesenv['REQUEST_METHOD'] = self.request_method # GETenv['PATH_INFO'] = self.path # /helloenv['SERVER_NAME'] = self.server_name # localhostenv['SERVER_PORT'] = str(self.server_port) # 8888return envdef start_response(self, status, response_headers, exc_info=None):server_headers = [('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'), ('Server', 'WSGIServer 0.2')]self.headers_set = [status, response_headers + server_headers]#把a(bǔ)pplication返回給WSGI的數(shù)據(jù)返回給客戶端。def finish_response(self, result):try:status, response_headers = self.headers_setresponse = 'HTTP/1.1 {status}\r\n'.format(status=status)for header in response_headers:response += '{0}: {1}\r\n'.format(*header)response += '\r\n'for data in result:response += dataself.client_connection.sendall(response)print(''.join(['> {line}\n'.format(line=line) for line in response.splitlines()]))finally:self.client_connection.close()SERVER_ADDRESS = (HOST, PORT) = '', 8888def make_server(server_address, application):server = WSGIServer(server_address)server.set_app(application)return serverif __name__ == '__main__':if len(sys.argv) < 2:sys.exit('Provide a WSGI application object as module:callable')app_path = sys.argv[1]module, application = app_path.split(':') # 第一個(gè)參數(shù)是文件名,第二個(gè)參數(shù)時(shí)長(zhǎng)文件內(nèi)app的命名module = __import__(module)application = getattr(module, application) # getattr(object, name[, default]) -> valuehttpd = make_server(SERVER_ADDRESS, application)print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))httpd.serve_forever()1.5 生產(chǎn)環(huán)境中的Web服務(wù)器
每個(gè)web框架都不是專注于實(shí)現(xiàn)服務(wù)器方面的,因此,在生產(chǎn)環(huán)境部署的時(shí)候,使用的服務(wù)器也不會(huì)簡(jiǎn)單的使用web框架自帶的服務(wù)器,那么用于生產(chǎn)環(huán)境的服務(wù)器有哪些呢?
Gunicorn
Gunicorn(從Ruby下面的Unicorn得到的啟發(fā))應(yīng)運(yùn)而生:依賴Nginx的代理行為,同Nginx進(jìn)行功能上的分離。由于不需要直接處理用戶來(lái)的請(qǐng)求(都被Nginx先處理),Gunicorn不需要完成相關(guān)的功能,其內(nèi)部邏輯非常簡(jiǎn)單:接受從Nginx來(lái)的動(dòng)態(tài)請(qǐng)求,處理完之后返回給Nginx,由后者返回給用戶。
由于功能定位很明確,Gunicorn得以用純Python開(kāi)發(fā):大大縮短了開(kāi)發(fā)時(shí)間的同時(shí),性能上也不會(huì)很掉鏈子。同時(shí),它也可以配合Nginx的代理之外的別的Proxy模塊工作,其配置也相應(yīng)比較簡(jiǎn)單
uWSGI
使用C語(yǔ)言開(kāi)發(fā),和底層接觸的更好,配置也比較方便,目前和gunicorn兩個(gè)算是部署時(shí)的唯二之選。由于其可擴(kuò)展的架構(gòu),它能夠被無(wú)限制的擴(kuò)展用來(lái)支持更多的平臺(tái)和語(yǔ)言。目前,可以使用C,C++和Objective-C來(lái)編寫(xiě)插件
uWSGI 既不使用wsgi協(xié)議也不用FastCGI協(xié)議,而是自創(chuàng)了一個(gè)uwsgi的協(xié)議,uwsgi協(xié)議是一個(gè)uWSGI服務(wù)器自有的協(xié)議,它用于定義傳輸信息的類型(type of information),每一個(gè)uwsgi packet前4byte為傳輸信息類型描述,它與WSGI相比是兩樣?xùn)|西。據(jù)說(shuō)該協(xié)議大約是fcgi協(xié)議的10倍那么快
主要特點(diǎn)如下:
- 超快的性能
- 低內(nèi)存占用(實(shí)測(cè)為apache2的mod_wsgi的一半左右)
- 多app管理
- 詳盡的日志功能(可以用來(lái)分析app性能和瓶頸)
- 高度可定制(內(nèi)存大小限制,服務(wù)一定次數(shù)后重啟等)
uWSGI 服務(wù)器自己實(shí)現(xiàn)了基于uwsgi協(xié)議的server部分,因此我們只需要在uwsgi的配置文件中指定application的地址,uWSGI 就能直接和應(yīng)用框架中的WSGI application通信
bjoern
是一個(gè)用C語(yǔ)言編寫(xiě)的,快速超輕量級(jí)的 Python WSGI服務(wù)器。
它是最快速的,最小的并且是最輕量級(jí)的WSGI服務(wù)器。有以下特性:
- 1000 行的C代碼
- 占用內(nèi)存 600KB
- 單線程沒(méi)有其他協(xié)同程序
- 可以綁定到TCP主機(jī):端口地址和Unix套接字
- 支持HTTP1.0/1.1,包含支持HTTP1.1的分塊響應(yīng)
如果單純追求性能,那uWSGI會(huì)更好一點(diǎn),而Gunicorn則會(huì)更易安裝和結(jié)合gevent。在阻塞響應(yīng)較多的情況下,Gunicorn的gevent模式無(wú)疑性能會(huì)更加強(qiáng)大。功能實(shí)現(xiàn)方面,uWSGI會(huì)更多一些,配置也會(huì)更加復(fù)雜一些。
2. Web應(yīng)用開(kāi)發(fā)
常見(jiàn)的Python Web應(yīng)用框架:
- Django:全能型Web框架
- Flask:一個(gè)使用Python編寫(xiě)的輕量級(jí)Web框架
- web.py:一個(gè)小巧的Web框架
- Bottle:和Flask類似的Web框架
- Tornado:Facebook的開(kāi)源異步Web框架
2.1 服務(wù)器架構(gòu)
2.1.1 Nginx
Nginx(發(fā)音同engine x)是一個(gè)異步框架的 Web服務(wù)器,也可以用作反向代理,負(fù)載平衡器 和 HTTP緩存。該軟件由 Igor Sysoev 創(chuàng)建,并于2004年首次公開(kāi)發(fā)布。同名公司成立于2011年,以提供支持。
Nginx是一款免費(fèi)的開(kāi)源軟件,根據(jù)類BSD許可證的條款發(fā)布。一大部分Web服務(wù)器使用Nginx,通常作為負(fù)載均衡器。
Nginx是一款面向性能設(shè)計(jì)的HTTP服務(wù)器,相較于Apache、lighttpd具有占有內(nèi)存少,穩(wěn)定性高等優(yōu)勢(shì)。與舊版本(<=2.2)的Apache不同,Nginx不采用每客戶機(jī)一線程的設(shè)計(jì)模型,而是充分使用異步邏輯從而削減了上下文調(diào)度開(kāi)銷,所以并發(fā)服務(wù)能力更強(qiáng)。整體采用模塊化設(shè)計(jì),有豐富的模塊庫(kù)和第三方模塊庫(kù),配置靈活。 在Linux操作系統(tǒng)下,Nginx使用epoll事件模型,得益于此,Nginx在Linux操作系統(tǒng)下效率相當(dāng)高。同時(shí)Nginx在OpenBSD或FreeBSD操作系統(tǒng)上采用類似于epoll的高效事件模型kqueue。
Nginx在官方測(cè)試的結(jié)果中,能夠支持五萬(wàn)個(gè)并行連接,而在實(shí)際的運(yùn)作中,可以支持二萬(wàn)至四萬(wàn)個(gè)并行連接
反向代理
正向代理是指瀏覽器主動(dòng)請(qǐng)求代理服務(wù)器,代理服務(wù)器轉(zhuǎn)發(fā)請(qǐng)求到對(duì)應(yīng)的目標(biāo)服務(wù)器。而反向代理則部署在Web服務(wù)器上,代理所有外部網(wǎng)絡(luò)對(duì)內(nèi)部網(wǎng)絡(luò)的訪問(wèn)。瀏覽器訪問(wèn)服務(wù)器,必須經(jīng)過(guò)這個(gè)代理,是被動(dòng)的。正向代理的主動(dòng)方是客戶端,反向代理的主動(dòng)方是Web服務(wù)器
在Python的Web開(kāi)發(fā)中,較為成熟穩(wěn)定的服務(wù)器架構(gòu)一般是Nginx + uWSGI + Django。而實(shí)際上Nginx服務(wù)器并不是必須的,直接使用uWSGI + Djang完全是可以的,但這樣一來(lái),直接將uWSGI服務(wù)器暴露給了瀏覽器客戶端,由此會(huì)導(dǎo)致諸多隱患。
Nginx的優(yōu)勢(shì)
附錄
支持WSGI的Web應(yīng)用框架有很多
- BlueBream
- bobo
- Bottle
- CherryPy
- Django
- Flask
- Google App Engine’s webapp2
- Gunicorn
- prestans
- Pylons
- Pyramid
- restlite
- Tornado
- Trac
- TurboGears
- Uliweb
- web.py
- web2py
- weblayer
- Werkzeug
參考
[參考資料1]
[參考資料2]
[參考資料3]
[參考資料4]
[參考資料5]
總結(jié)
以上是生活随笔為你收集整理的Web基础(三)Python Web的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 第4章第11节:图表:使用柱形图表制作学
- 下一篇: python随机森林特征重要性原理_随机