服務端與瀏覽器收發信息: socket 套接字 是應用層和傳輸層之間一個虛擬層,是一個介面. 列印一下收到的消息是什麼>??? 那瀏覽器收到的消息是什麼? 通過以上對比,發現收發消息的格式都是一樣的 即為 HTTP協議格式 每個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Bod ...
服務端與瀏覽器收發信息:
socket 套接字 是應用層和傳輸層之間一個虛擬層,是一個介面.
import socket sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() while True: conn, addr = sk.accept() data = conn.recv(8096) conn.send(b"OK") conn.close()
列印一下收到的消息是什麼>???
將\r\n替換成換行看得更清晰點: GET / HTTP/1.1 Host: 127.0.0.1:8080 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8
那瀏覽器收到的消息是什麼?
通過以上對比,發現收發消息的格式都是一樣的---即為 HTTP協議格式
每個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。
HTTP響應的Header中有一個 Content-Type
表明響應的內容格式。它的值如text/html; charset=utf-8。
text/html則表示是網頁,charset=utf-8則表示編碼為utf-8。
mport socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8000)) sock.listen() while True: conn, addr = sock.accept() data = conn.recv(8096) # 給回覆的消息加上響應狀態行 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") 不加這行代碼返回不了數據 conn.send(b"OK") conn.close()自定義web框架
""" 根據URL中不同的路徑返回不同的內容 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和埠 sk.listen() # 監聽 while True: # 等待連接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的位元組類型的數據轉換成字元串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因為要遵循HTTP協議,所以回覆的消息也要加狀態行 # 根據不同的路徑返回不同內容 if url == "/index/": response = b"index" elif url == "/home/": response = b"home" else: response = b"404 not found!" conn.send(response) conn.close()根據不同的路徑返回不同的內容
""" 根據URL中不同的路徑返回不同的內容--函數版 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和埠 sk.listen() # 監聽 # 將返回不同的內容部分封裝成函數 def func(url): s = "這是{}頁面!".format(url) return bytes(s, encoding="utf8") while True: # 等待連接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的位元組類型的數據轉換成字元串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因為要遵循HTTP協議,所以回覆的消息也要加狀態行 # 根據不同的路徑返回不同內容,response是具體的響應體 if url == "/index/": response = func(url) elif url == "/home/": response = func(url) else: response = b"404 not found!" conn.send(response) conn.close()根據不同的路徑返回不同的內容--函數版
""" 根據URL中不同的路徑返回不同的內容--函數進階版 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和埠 sk.listen() # 監聽 # 將返回不同的內容部分封裝成不同的函數 def index(url): s = "這是{}頁面XX!".format(url) return bytes(s, encoding="utf8") def home(url): s = "這是{}頁面。。!".format(url) return bytes(s, encoding="utf8") # 定義一個url和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ] while True: # 等待連接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的位元組類型的數據轉換成字元串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因為要遵循HTTP協議,所以回覆的消息也要加狀態行 # 根據不同的路徑返回不同內容 func = None # 定義一個保存將要執行的函數名的變數 for item in list1: if item[0] == url: func = item[1] break if func: response = func(url) else: response = b"404 not found!" # 返回具體的響應消息 conn.send(response) conn.close()根據不同的路徑返回不同的內容--函數進階版
""" 根據URL中不同的路徑返回不同的內容--函數進階版 返回獨立的HTML頁面 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和埠 sk.listen() # 監聽 # 將返回不同的內容部分封裝成不同的函數 def index(url): # 讀取index.html頁面的內容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回位元組數據 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") # 定義一個url和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ] while True: # 等待連接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的位元組類型的數據轉換成字元串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因為要遵循HTTP協議,所以回覆的消息也要加狀態行 # 根據不同的路徑返回不同內容 func = None # 定義一個保存將要執行的函數名的變數 for item in list1: if item[0] == url: func = item[1] break if func: response = func(url) else: response = b"404 not found!" # 返回具體的響應消息 conn.send(response) conn.close()返回具體的HTML文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>index</title> </head> <body> <div>這是index頁面</div> </body> </html>index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>index</title> </head> <body> <div>這是home頁面</div> </body> </html>home.html
""" 根據URL中不同的路徑返回不同的內容--函數進階版 返回獨立的HTML頁面 """ import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) # 綁定IP和埠 sk.listen() # 監聽 # 將返回不同的內容部分封裝成不同的函數 def index(url): # 讀取index.html頁面的內容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回位元組數據 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") def timer(url): import time with open("time.html", "r", encoding="utf8") as f: s = f.read() s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S")) return bytes(s, encoding="utf8") # 定義一個url和實際要執行的函數的對應關係 list1 = [ ("/index/", index), ("/home/", home), ("/time/", timer), ] while True: # 等待連接 conn, add = sk.accept() data = conn.recv(8096) # 接收客戶端發來的消息 # 從data中取到路徑 data = str(data, encoding="utf8") # 把收到的位元組類型的數據轉換成字元串 # 按\r\n分割 data1 = data.split("\r\n")[0] url = data1.split()[1] # url是我們從瀏覽器發過來的消息中分離出的訪問路徑 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因為要遵循HTTP協議,所以回覆的消息也要加狀態行 # 根據不同的路徑返回不同內容 func = None # 定義一個保存將要執行的函數名的變數 for item in list1: if item[0] == url: func = item[1] break if func: response = func(url) else: response = b"404 not found!" # 返回具體的響應消息 conn.send(response) conn.close()讓網頁動態起來
以下內容目前隨便看看就行了,反正我沒看懂
============>>>>>>...伺服器程式和應用程式<<<<<<<<<=================
對於真實開發中的python web程式來說,一般會分為兩部分:伺服器程式和應用程式。
伺服器程式負責對socket服務端進行封裝,併在請求到來時,對請求的各種數據進行整理。
應用程式則負責具體的邏輯處理。為了方便應用程式的開發,就出現了眾多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的開發方式,但是無論如何,開發出的應用程式都要和伺服器程式配合,才能為用戶提供服務。
這樣,伺服器程式就需要為不同的框架提供不同的支持。這樣混亂的局面無論對於伺服器還是框架,都是不好的。對伺服器來說,需要支持各種不同框架,對框架來說,只有支持它的伺服器才能被開發出的應用使用。
這時候,標準化就變得尤為重要。我們可以設立一個標準,只要伺服器程式支持這個標準,框架也支持這個標準,那麼他們就可以配合使用。一旦標準確定,雙方各自實現。這樣,伺服器可以支持更多支持標準的框架,框架也可以使用更多支持標準的伺服器。
WSGI(Web Server Gateway Interface)就是一種規範,它定義了使用Python編寫的web應用程式與web伺服器程式之間的介面格式,實現web應用程式與web伺服器程式間的解耦。
常用的WSGI伺服器有uwsgi、Gunicorn。而Python標準庫提供的獨立WSGI伺服器叫wsgiref,Django開發環境用的就是這個模塊來做伺服器。
wsgiref
我們利用wsgiref模塊來替換我們自己寫的web框架的socket server部分:
- """
- 根據URL中不同的路徑返回不同的內容--函數進階版
- 返回HTML頁面
- 讓網頁動態起來
- wsgiref模塊版
- """
- from wsgiref.simple_server import make_server
- # 將返回不同的內容部分封裝成函數
- def index(url):
- # 讀取index.html頁面的內容
- with open("index.html", "r", encoding="utf8") as f:
- s = f.read()
- # 返回位元組數據
- return bytes(s, encoding="utf8")
- def home(url):
- with open("home.html", "r", encoding="utf8") as f:
- s = f.read()
- return bytes(s, encoding="utf8")
- def timer(url):
- import time
- with open("time.html", "r", encoding="utf8") as f:
- s = f.read()
- s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S"))
- return bytes(s, encoding="utf8")
- # 定義一個url和實際要執行的函數的對應關係
- list1 = [
- ("/index/", index),
- ("/home/", home),
- ("/time/", timer),
- ]
- def run_server(environ, start_response):
- start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 設置HTTP響應的狀態碼和頭信息
- url = environ['PATH_INFO'] # 取到用戶輸入的url
- func = None
- for i in list1:
- if i[0] == url:
- func = i[1]
- break
- if func:
- response = func(url)
- else:
- response = b"404 not found!"
- return [response, ]
- if __name__ == '__main__':
- httpd = make_server('127.0.0.1', 8090, run_server)
- print("我在8090等你哦...")
- httpd.serve_forever()
jinja2
上面的代碼實現了一個簡單的動態,我完全可以從資料庫中查詢數據,然後去替換我html中的對應內容,然後再發送給瀏覽器完成渲染。 這個過程就相當於HTML模板渲染數據。 本質上就是HTML內容中利用一些特殊的符號來替換要展示的數據。 我這裡用的特殊符號是我定義的,其實模板渲染有個現成的工具: jinja2
下載jinja2:
pip install jinja2
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>姓名:{{name}}</h1> <h1>愛好:</h1> <ul> {% for hobby in hobby_list %} <li>{{hobby}}</li> {% endfor %} </ul> </body> </html>index2.html文件
使用jinja2渲染index2.html文件:
- from wsgiref.simple_server import make_server
- from jinja2 import Template
- def index(url):
- # 讀取HTML文件內容
- with open("index2.html", "r", encoding="utf8") as f:
- data = f.read()
- template = Template(data) # 生成模板文件
- ret = template.render({'name': 'alex', 'hobby_list': ['抽煙', '喝酒', '燙頭']}) # 把數據填充到模板中
- return bytes(ret, encoding="utf8")
- def home(url):
- with open("home.html", "r", encoding="utf8") as f:
- s = f.read()
- return bytes(s, encoding="utf8")
- # 定義一個url和實際要執行的函數的對應關係
- list1 = [
- ("/index/", index),
- ("/home/", home),
- ]
- def run_server(environ, start_response):
- start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 設置HTTP響應的狀態碼和頭信息
- url = environ['PATH_INFO'] # 取到用戶輸入的url
- func = None
- for i in list1:
- if i[0] == url:
- func = i[1]
- break
- if func:
- response = func(url)
- else:
- response = b"404 not found!"
- return [response, ]
- if __name__ == '__main__':
- httpd = make_server('127.0.0.1', 8090, run_server)
- print("我在8090等你哦...")
- httpd.serve_forever()
現在的數據是我們自己手寫的,那可不可以從資料庫中查詢數據,來填充頁面呢?
使用pymysql連接資料庫:
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8") cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select name, age, department_id from userinfo") user_list = cursor.fetchall() cursor.close() conn.close()
創建一個測試的user表:
CREATE TABLE user( id int auto_increment PRIMARY KEY, name CHAR(10) NOT NULL, hobby CHAR(20) NOT NULL )engine=innodb DEFAULT charset=UTF8;
模板的原理就是字元串替換,我們只要在HTML頁面中遵循jinja2的語法規則寫上,其內部就會按照指定的語法進行相應的替換,從而達到動態的返回內容。
以下內容留存,一些配置信息修改: 重要
模板文件配置:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "template")], # template文件夾位置 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
靜態文件配置:
STATIC_URL = '/static/' # HTML中使用的靜態文件夾首碼 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), # 靜態文件存放位置 ]
剛開始學習時可在配置文件中暫時禁用csrf中間件,方便表單提交測試。
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
1. HTTP協議
1. 請求(瀏覽器發送給伺服器的消息-request)
格式:
請求方式 URL 協議版本
k1: v1
k2: v2
請求數據(請求體)
2. 響應(伺服器返回給瀏覽器的消息-response)
格式:
協議版本 狀態碼 狀態描述符
k1: v1
k2: v2
響應體(HTML)
2. web框架
本質: socket服務端
功能:
a. socket收發消息
b. URL和函數的對應關係,根據不同的URL執行不同的函數,返回函數的結果
c. 讀取HTML文件,進行了一個字元替換(模板渲染)
分類:
Django flask tornado
完成了a,b,c三個功能的 ——》 tornado
完成了b,c 兩個功能 ——》 Django
完成了b 一個功能 ——》 flask
另一種分類:
1. Django 大而全
2. 其他 短小精悍
3. 安裝Django
1. pycharm
file settings project 點加號 輸入django 選擇版本 下載
2. 命令行
pip install django==1.11.15
4. 創建Django項目
1. 命令行