pythonwebget
⑴ 純 python 寫一個 Web 框架,就是這么簡單
造輪子是最好的一種學習方式,本文嘗試從0開始造個Python Web框架的輪子,我稱它為 ToyWebF 。
本文操作環境為:MacOS,文中涉及的命令,請根據自己的系統進行替換。
ToyWebF的簡單特性:
下面我們來實現這些特性。
首先,我們需要安裝gunicorn,回憶一下Flask框架,該框架有內置的Web伺服器,但不穩定,所以上線時通常會替換成uWSGI或gunicorn,這里不搞這個內置Web服務,直接使用gunicorn。
我們創建新的目錄與Python虛擬環境,在該虛擬環境中安裝gunicorn
在啥都沒有的情況下,構建最簡單的Web服務,在ToyWebF目錄下,創建app.py與api.py文件,寫入下面代碼。
運行 gunicorn app:app 訪問 http://127.0.0.1:8000 ,可以看見 Hello, World! ,但現在請求體中的參數在environ變數中,難以解析,我們返回的response也是bytes形式。
我們可以使用webob庫,將environ中的數據轉為Request對象,將需要返回的數據轉為Response對象,處理起來更加直觀方便,直接通過pip安裝一下。
然後修改一下API類的 __call__方法 ,代碼如下。
上述代碼中,通過webob庫的Request類將environ對象(請求的環境信息)轉為容易處理的request,隨後調用handle_request方法對request進行處理,處理的結果,通過response對象返回。
handle_request方法在ToyWebF中非常重要,它會匹配出某個路由對應的處理方法,然後調用該方法處理請求並將處理的結果返回,在解析handle_request前,需要先討論路由注冊實現,代碼如下。
其實就是將路由和方法存到self.routes字典中,可以通過route裝飾器的形式將路由和方法關聯,也可以通過add_route方法關聯,在app.py中使用一下。
因為url中可以存在變數,如 @app.route("/hello/{name}") ,所以在匹配時,需要進行解析,可以使用正則匹配的方式進行匹配,parse這個第三方庫已經幫我們實現了相應的正則匹配邏輯,pip安裝使用一下則可。
這里定義find_handler方法來實現對self.routes的遍歷。
了解了路由與方法關聯的原理後,就可以實現handle_request方法,該方法主要的路徑就是根據路由調度對應的方法,代碼如下。
在該方法中,首先實例化webob庫的Response對象,然後通過self.find_handler方法獲取此次請求路由對應的方法和對應的參數,比如。
它將返回hello方法對象和name參數,如果是 /hello/二兩 ,那麼name就是二兩。
因為route裝飾器可能裝飾器的類對象,比如。
此時self.find_handler方法返回的hanler就是個類,但我們希望調用的是類中的get、post、delete等方法,所以需要一個簡單的判斷邏輯,通過inspect.isclass方法判斷handler如果是類對象,那麼就通過getattr方法獲取類對象實例的中對應的請求方法。
如果類對象中沒有該方法屬性,則拋出該請求類型不被允許的錯誤,如果不是類對象或類對象中存在該方法屬性,則直接調用則可。
此外,如果方法的路由並沒有注冊到self.routes中,即404的情況,定義了defalut_response方法返回其中內容,代碼如下。
如果handle_request方法中調度的過程出現問題,則直接raise將錯誤拋出。
至此,一個最簡單的web服務就編寫完成了。
回顧Flask,Flask可以支持HTML、CSS、javaScript等靜態文件,利用模板語言,可以構建出簡單但美觀的Web應用,我們讓TopWebF也支持這一功能,最終實現圖中的網站,完美兼容靜態文件。
Flask使用了jinja2作為其html模板引擎,ToyWebF同樣使用jinja2,jinja2其實實現一種簡單的DSL(領域內語言),讓我們可以在HTML中通過特殊的語法改變HTML的結構,該項目非常值得研究學習。
首先 pip install jinja2 ,然後就可以使用它了,在ToyWebF項目目錄中創建templates目錄,以該目錄作為默認的HTML文件根目錄,代碼如下。
首先利用jinja2的FileSystemLoader類將file system中的某個文件夾作為loader,然後初始化Environment。
在使用的過程中(即調用template方法),通過get_template方法獲得具體的某個模板並通過render方法將對應的內容傳遞給模板中的變數。
這里我們不寫前端代碼,直接去互聯網中下載模板,這里下載了Bootstrap提供的免費模板,可以自行去 https://startbootstrap.com/themes/freelancer/ 下載,下載完後,你會獲得index.html以及對應的css、jss、img等文件,將index.html移動到ToyWebF/templates中並簡單修改了一下,添加一些變數。
然後在app.py文件中為index.html定義路由以及需要的參數。
至此html文件的支持就完成了,但此時的html無法正常載入css和js,導致頁面布局非常醜陋且交互無法使用。
接著就讓ToyWebF支持css、js,首先在ToyWebF目錄下創建static文件夾用於存放css、js或img等靜態文件,隨後直接將前面下載的模板,其中的靜態文件復制到static中則可。
通過whitenoise第三方庫,可以通過簡單的幾行代碼讓web框架支持css和js,不需要依賴nginx等服務,首先 pip install whitenoise ,隨後修改API類的 __init__ 方法,代碼如下。
其實就是通過WhiteNoise將self.wsgi_app方法包裹起來,在調用API的 __call__ 方法時,直接調用self.whitenoise。
此時,如果請求web服務獲取css、js等靜態資源,WhiteNoise會獲取其內容並返回給client,它在背後會匹配靜態資源在系統中對應的文件並將其讀取返回。
至此,一開始的網頁效果就實現好了。
web服務如果出現500時,默認會返回 internal server error ,這顯得比較丑,為了讓框架使用者可以自定義500時返回的錯誤,需要添加一些代碼。
首先API初始化時,初始self.exception_handler對象並定義對應的方法添加自定義的錯誤
在handler_request方法進行請求調度時,調度的方法執行邏輯時報500,此時不再默認將錯誤拋出,而是先判斷是否有自定義錯誤處理。
在app.py中,自定義錯誤返回方法,如下。
custom_exception_handler方法只返回自定義的一段話,你完全可以替換成美觀的template。
我們可以實驗性定義一個路由來看效果。
Web服務的中間件也可以理解成鉤子,即在請求前可以對請求做一些處理或者返回Response前對Response做一下處理。
為了支持中間件,在TopWebF目錄下創建middleware.py文件,在編寫代碼前,思考一下如何實現?
回顧一下現在請求的調度邏輯。
1.通過routes裝飾器關聯路由和方法 2.通過API.whitenoise處理 3.如果是請求API介面,那麼會將參數傳遞給API.wsgi_app 4.API.wsgi_app最終會調用API.handle_request方法獲取路由對應的方法並調用該方法執行相應的邏輯
如果希望在request前以及response後做相應的操作,那麼其實就需要讓邏輯在API.handle_request前後執行,看一下代碼。
其中add方法會實例化Middleware對象,該對象會將當前的API類實例包裹起來。
Middleware.handle_request方法其實就是在self.app.handle_request前調用self.process_request方法處理request前的數據以及調用self.process_response處理response後的數據,而核心的調度邏輯,依舊交由API.handle_request方法進行處理。
這里的代碼可能會讓人感到疑惑, __call__ 方法和handle_request方法中都有self.app.handle_request(request),但其調用對象似乎不同?這個問題暫時放一下,先繼續完善代碼,然後再回來解釋。
接著在api.py中為API創建middleware屬性以及添加新中間件的方法。
隨後,在app.py中,自定義一個簡單的中間件,然後調用add_middleware方法將其添加。
定義好中間件後,在請求調度時,就需要使用中間件,為了兼容靜態文件的情況,需要對css、js、ing文件的請求路徑做一下兼容,在其路徑中加上/static前綴
緊接著,修改API的 __call__ ,兼容中間件和靜態文件,代碼如下。
至此,中間件的邏輯就完成了。
但代碼中依舊有疑惑,Middleware類中的 __call__ 方法和handle_request方法其調用的self.app到底是誰?
為了方便理解,這里一步步拆解。
如果沒有添加新的中間件,那麼請求的調度邏輯如下。
在沒有添加中間件的情況下,self.app其實就是API本身,所以 middleware.__call__ 中的self.app.handle_request就是調用API.handle_request。
如果添加了新的中間件,如上述代碼中添加了名為SimpleCustomMiddleware的中間件,此時的請求調度邏輯如下。
因為注冊中間件時,Middleware.add方法替換了原始Middleware實例中的app對象,將其替換成了SimpleCustomMiddleware,而SimpleCustomMiddleware也有app對象,SimpleCustomMiddleware中的app對象,才是API類實例。
在請求調度的過程中,就會觸發Middleware類的handle_request方法,該方法就會執行中間件相應的邏輯去處理request和response中的數據。
當然,你可以通過Middleware.add方法添加多個中間件,這就會構成棧式調用的效果,代碼如下。
啟動web服務後,其執行效果如下。
⑵ python 怎麼搭建簡單的web伺服器
利用Python自帶的包可以建立簡單的web伺服器。在DOS里cd到准備做伺服器根目錄的路徑下,輸入命令:x0dx0apython -m Web伺服器模塊 [埠號,默認8000]x0dx0a例如:x0dx0apython -m SimpleHTTPServer 8080x0dx0a然後就可以在瀏覽器中輸入x0dx0ah ttp://loca lhost:埠號/路徑x0dx0a來訪問伺服器資源。 x0dx0a例如:x0dx0ah ttp://local host:808 0/index.h tm(當然index.htm文件得自己創建)x0dx0a其他機器也可以通過伺服器的IP地址來訪問。x0dx0ax0dx0a這里的「Web伺服器模塊」有如下三種:x0dx0ax0dx0aBaseHTTPServer: 提供基本的Web服務和處理器類,分別是HTTPServer和BaseHTTPRequestHandler。x0dx0aSimpleHTTPServer: 包含執行GET和HEAD請求的SimpleHTTPRequestHandler類。x0dx0aCGIHTTPServer: 包含處理POST請求和執行CGIHTTPRequestHandler類。
⑶ Python 有哪些好的 Web 框架
python的web框架很多
django (大而全,模板,orm都自帶)
flask (pocoo出品,比屬精品,自帶jinja2模板,可以替換)
web.py (這個我沒用過,作者自殺,白瞎了一個高手)
bottle (只有一個文件的框架,需要自己構建整個開發體系)
uliweb (中國人開發的,也很不錯)
Tornado (非同步框架,適合長連接,比如在線聊天之類的)
Python框架雖然說是百花齊放,但仍然有那麼一家是最大的,它就是Django。Django為人所稱道的地方主要有:
①完美的文檔,Django的成功,我覺得很大一部分原因要歸功於Django近乎完美的官方文檔(包括Django book)。
②
全套的解決方案,Django象Rails一樣,提供全套的解決方案(full-stack framework + batteries
included),基本要什麼有什麼(比如:cache、session、feed、orm、geo、auth),而且全部Django自己造,開發網
站應手的工具Django基本都給你做好了,因此開發效率是不用說的,出了問題也算好找,不在你的代碼里就在Django的源碼里。
③強大的URL路由配置,Django讓你可以設計出非常優雅的URL,在Django里你基本可以跟醜陋的GET參數說拜拜。
④自助管理後台,admin interface是Django里比較吸引眼球的一項contrib,讓你幾乎不用寫一行代碼就擁有一個完整的後台管理界面。
⑷ 請教,python web.py查詢資料庫返回的結果是列表還是字典
我常用pymssql連接M$SQL伺服器, 沒用過pyodbc, 但按理應該都差不多。
若用pymssql, 從資料庫中取數據的過程如下:
import pymssql
connect_setting = {
'host': '127.0.0.1',
'user': 'sa',
'password': '',
'database': 'master',
'charset': 'gb18030'
}
conn = pymssql.connect(**connect_setting)
curr = conn.cursor()
curr.execute("select list, name from nlist")
result = [(lst, name) for lst, name in curr]
curr.close()
conn.close()
關於"如何查詢,list的每個值在資料庫中對應的Name「
若庫表中的數據像下面的樣子:
id, name:
1, 'python'
2, 'perl'
3, 'c'
4, 'java'
且每行id是唯一值, 將從資料庫表中拿到的數據放到一個字典中:
curr.execute("select id, name from nlist")
dd = dict([(id, name) for id, name in curr])
lst = [1,2,3,4,5,6]
用dd進行轉換
[(i, dd.get(i)) for i in lst]
==>
[(1, 'python'), (2, 'perl'), (3, 'c'), (4, 'java'), (5, None), (6, None)]
or
[dd.get(i, i) for i in lst]
==>
['python', 'perl', 'c', 'java', 5, 6]
⑸ 如何在 virtualenv 環境下搭建 Python Web
安裝組件庫
第一步安裝所需要的存儲庫,因為打算用到虛擬環境,用到 pip 安裝和管理 Python 組件,所以先更新本地包,然後安裝組件:
sudo apt-get update
sudo apt-get install python-pip python-dev nginx
創建虛擬環境 virtualenv
在一個系統中創建不同的 Python 隔離環境,相互之間還不會影響,為了使系統保持干凈,遂決定用 virtualenv 跑應用程序,創建一個容易識別的目錄,開始安裝,再創建項目目錄 super,然後激活環境:
sudo pip install virtualenv
mkdir ~/supervisor && cd ~/supervisor
virtualenv super
source super/bin/activate
安裝 Flask 框架
好了,現在在虛擬環境裡面,開始安裝 Flask 框架,flask 依賴兩個庫 werkzeug 和 jinjia2, 採用 pip 方式安裝即可, pip 是一個重要的工具,Python 用它來管理包:
pip install flask
先用 Flask 寫一個簡單的 Web 服務 myweb.py ,因為後面要做一些測試,所以設置了幾個請求:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello world supervisor gunicorn '
@app.route('/1')
def index1():
return 'hello world supervisor gunicorn ffffff'
@app.route('/qw/1')
def indexqw():
return 'hello world supervisor gunicorn fdfdfbdfbfb '
if __name__ == '__main__':
app.debug = True
app.run()
啟動 Flask 看看!
python myweb.py
在瀏覽器中訪問 http://127.0.0.1:5000 就可以看到了「幾個路徑都試一試」
用 Gunicorn 部署 Python Web
現在我們使用 Flask 自帶的伺服器,完成了 Web 服務的啟動。生產環境下,Flask 自帶的伺服器,無法滿足性能要求。所以我們這里採用 Gunicorn 做 wsgi 容器,用來部署 Python,首先還是安裝 Gunicorn:
pip install gunicorn
當我們安裝好 Gunicorn 之後,需要用 Gunicorn 啟動 Flask,Flask 用自帶的伺服器啟動時,Flask 裡面的
name 裡面的代碼啟動了 app.run()。而這里我們使用 Gunicorn,myweb.py 就等同於一個庫文件,被 Gunicorn
調用,這樣啟動:
gunicorn -w 4 -b 0.0.0.0:8000 myweb:app
其中 myweb 就是指 myweb.py,app 就是那個 wsgifunc 的名字,這樣運行監聽 8000 埠,原先的 5000 埠並沒有啟用,-w 表示開啟多少個 worker,-b 表示 Gunicorn 開發的訪問地址。
想要結束 Gunicorn 只需執行 pkill Gunicorn,但有時候還要 ps 找到 pid 進程號才能
kill。可是這對於一個開發來說,太過於繁瑣,因此出現了另外一個神器
---supervisor,一個專門用來管理進程的工具,還可以管理系統的工具進程。
安裝 Supervisor
pip install supervisor
echo_supervisord_conf > supervisor.conf # 生成 supervisor 默認配置文件
gedit supervisor.conf # 修改 supervisor 配置文件,添加 gunicorn 進程管理
在 supervisor.conf 底部下添加 myweb.py 的配置 `/home/wang/supervisor/super` 是我的項目目錄」
[program:myweb]
command=/home/wang/supervisor/super/bin/gunicorn -w 4 -b 0.0.0.0:8000 myweb:app
directory=/home/wang/supervisor
startsecs=0
stopwaitsecs=0
autostart=false
autorestart=false
user=wang
stdout_logfile=/home/wang/supervisor/log/gunicorn.log
stderr_logfile=/home/wang/supervisor/log/gunicorn.err
supervisor 的基本使用命令:
supervisord -c supervisor.conf
supervisorctl -c supervisor.conf status 查看supervisor的狀態
supervisorctl -c supervisor.conf reload 重新載入 配置文件
supervisorctl -c supervisor.conf start [all]|[appname] 啟動指定/所有 supervisor 管理的程序進程
supervisorctl -c supervisor.conf stop [all]|[appname] 關閉指定/所有 supervisor 管理的程序進程
supervisor 還有一個 web 的管理界面,可以激活。更改下配置:
[inet_http_server] ; inet (TCP) server disabled by default
port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for alliface)
username=wang ; (default is no username (open server)
password=123 ; (default is no password (open server))
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
username=wang ; should be same as http_username if set
password=123 ; should be same as http_password if set
;prompt=mysupervisor ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history ; use readline history if available
現在可以使用 supervsior 啟動 gunicorn 啦。運行命令:
supervisord -c supervisor.conf
瀏覽器訪問 http://127.0.0.1:9001 可以得到 supervisor 的 web 管理界面,訪問 http://127.0.0.1:8000 可以看見 gunicorn 啟動的返回的頁面。
配置 Nginx
前面我們已經在系統環境下安裝了 Nginx, 安裝好的 Nginx 二進制文件放在 /usr/sbin/ 文件夾下,接下來使用
Supervisor 來管理 Nginx。這里需要注意一個問題,許可權問題。Nginx 是 sudo 的方式安裝,啟動的適合也是 root
用戶,那麼我們現在也需要用 root 用戶啟動 supervisor。在 supervisor.conf 下添加配置文件:
[program:nginx]
command=/usr/sbin/nginx
startsecs=0
stopwaitsecs=0
autostart=false
autorestart=false
stdout_logfile=/home/wang/supervisor/log/nginx.log
stderr_logfile=/home/wang/supervisor/log/nginx.err
好了,都配置完之後,啟動 supervisor:
supervisord -c supervisor.conf
訪問頁面,也可以用 ab 進行壓力測試:
ab -c 100 -n 100 http://127.0.0.1:8000/qw/1
-c 用於指定壓力測試的並發數, -n 用於指定壓力測試總共的執行次數。
安裝 Python 探針
搭建好了 web,想實時監控應用數據,有什麼好的工具,用 OneAPM 的 Python 探針試試,
首先也是安裝 Python 探針:
pip install -i http://pypi.oneapm.com/simple --upgrade blueware
根據 License Key 生成配置文件:
blueware-admin generate-config (License Key) = blueware.ini
由於是在虛擬環境下,所以要特別注意路徑,修改 supervisor.conf 裡面兩項:
[program:myapp]
command = /home/wang/supervisor/super/bin/blueware-admin run-program /home/wang/supervisor/super/bin/gunicorn -w 4 -b 0.0.0.0:8000 myapp:app
environment = BLUEWARE_CONFIG_FILE=blueware.ini
重啟應用
supervisorctl # 進入命令行
supervisor> reload # 重新載入
向上面一樣訪問頁面,也可以用 ab 進行壓力測試
幾分鍾後有下圖,可以看到頁面載入時間,web 事物,頁面吞吐量,其中後面是設置自定義事物「Business Transaction」。