Web框架本质
Web应用本质是一个socket套接字服务端,用户浏览器相当于socket客户端。
自定义服务端
import socket server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(("127.0.0.1",8080)) server.listen(5) while True: conn,client_addr=server.accept() data=conn.recv(1024) conn.send(b"hello") conn.close()
利用浏览器访问自定义的服务端("127.0.0.1",8080)出现下面结果
响应无效分析
响应无效,即服务端回应的内容浏览器无法识别。浏览器访问网站需遵循HTTP协议,则服务端发送的信息需遵循HTTP协议的规则。
HTTP协议主要规定了客户端和服务端之间的通信规则。
HTTP协议GET方法的请求格式
HTTP协议的响应格式
自定义服务端修改
import socket server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('127.0.0.1',8090)) server.listen(5) while True: conn,client_addr=server.accept() data=conn.recv(1024) #按照HTTP协议响应格式,给回复信息加上响应状态行 conn.send(b"HTTP/1.1 200 OK ") #响应头部信息为空 conn.send(b"hello") conn.close() #浏览器显示结果hello
自定义服务端功能完善
1、根据不同路径返回不同内容
分析客户端请求的URL,根据不同的URL给出不同的响应
利用if判断回应不同请求
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" 根据浏览器访问的路径的不同,返回不同的内容 """ import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 8090)) server.listen() while 1: conn, addr = server.accept() data = conn.recv(8096) # 收消息 # print(data) # 从浏览器发送消息中,拿到用户访问的路径 data_str = str(data, encoding="utf8") # 收到的消息格式bytes类型,需要转换成字符串进行下面的分割操作, # 根据回车符换行符( )将请求信息分割得到包含请求信息的列表, # 第一个元素为请求状态行,再按空格分割状态行,取出第二个元素,即URL url = data_str.split(" ")[0].split(" ")[1] print(url) #根据不同的URL返回不同的信息 if url == "/home/": msg = b'<h1>home page</h1>' elif url == "/index/": msg = b'<h1>index page</h1>' else: msg = b'<h1>404</h1>' conn.send(b'HTTP/1.1 200 OK ') conn.send(msg) conn.close()
包装成函数回应不同请求
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" 根据浏览器访问的路径的不同,返回不同的内容 将不同页面的处理代码封装到函数中 """ import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 8090)) server.listen() def home(url): return b'<h1>home page</h1>' def index(url): return b'<h1>index page</h1>' while 1: conn, addr = server.accept() data = conn.recv(1024) # 收消息 # print(data) # 从浏览器发送消息中,拿到用户访问的路径 data_str = str(data, encoding="utf8") # print(data_str) url = data_str.split(" ")[0].split(" ")[1] print(url) #路径不同执行不同的函数,即返回不同的信息 if url == "/home/": msg = home(url) elif url == "/index/": msg = index(url) else: msg = b'<h1>404</h1>' conn.send(b'HTTP/1.1 200 OK ') conn.send(msg) conn.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
"" 根据浏览器访问的路径的不同,返回不同的内容 将不同页面的处理代码封装到函数中 优化频繁的if判断 """ import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 8090)) server.listen() def home(url): s = "this is {} page!".format(url) return bytes(s, encoding="utf8") def index(url): return b'<h1>index page</h1>' def user(url): return b'hehe' while 1: conn, addr = server.accept() data = conn.recv(8096) # 收消息 # print(data) # 从浏览器发送消息中,拿到用户访问的路径 data_str = str(data, encoding="utf8") # print(data_str) url = data_str.split(" ")[0].split(" ")[1] print(url) url2func = [ ("/index/", index), ("/home/", home), ("/user/", user), ] func = None for i in url2func: if url == i[0]: func = i[1] # 拿到将要执行的函数 break else: func = None if func: msg = func(url) # 执行对应的函数 else: msg = b'<h1>404</h1>' # 找不到要执行的函数就返回404 conn.send(b'HTTP/1.1 200 OK ') conn.send(msg) conn.close()
2、返回具体的HTML文件
将HTML文件打开,以bytes类型发给客户端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 8090)) server.listen() def home(url): s = "this is {} page!".format(url) return bytes(s, encoding="utf8") def index(url): return b'<h1>index page</h1>' def user(url): return b'hehe' # 请求URL为login时,返回具体的HTML文件 # 打开相应的HTML文件,发给客户端浏览器 # 浏览器根据HTML文件渲染响应信息 def login(url): with open("login.html", "rb") as f: return f.read() # url和将要执行的函数的对应关系 url2func = [ ("/index/", index), ("/home/", home), ("/user/", user), ("/login/", login), ] while 1: conn, addr = server.accept() data = conn.recv(8096) # 收消息 # print(data) # 从浏览器发送消息中,拿到用户访问的路径 data_str = str(data, encoding="utf8") # print(data_str) url = data_str.split(" ")[0].split(" ")[1] print(url) func = None for i in url2func: if url == i[0]: func = i[1] # 拿到将要执行的函数 break else: func = None if func: msg = func(url) # 执行对应的函数 else: msg = b'<h1>404</h1>' # 找不到要执行的函数就返回404 # 按照HTTP协议的格式要求 回复消息 conn.send(b'HTTP/1.1 200 OK ') # 发送状态行 conn.send(msg) # 发送响应体 conn.close()
3、返回动态的HTML文件
在网页中定义特殊符号,用动态的数据去替换其,从而使不同访问者得到不同的数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket import time server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 8090)) server.listen() def home(url): s = "this is {} page!".format(url) return bytes(s, encoding="utf8") def index(url): return b'<h1>index page</h1>' def user(url): # 不同的用户得到的页面上显示不同的时间 # 用time模块得到当前的时间戳 c_time = str(time.time()) with open("user.html", "r") as f: data_s = f.read() data_s = data_s.replace("@@xx@@", c_time) # 替换后的字符串数据 return bytes(data_s, encoding="utf8") def login(url): with open("login.html", "rb") as f: return f.read() # url和将要执行的函数的对应关系 url2func = [ ("/index/", index), ("/home/", home), ("/user/", user), ("/login/", login), ] while 1: conn, addr = server.accept() data = conn.recv(8096) # 收消息 # print(data) # 从浏览器发送消息中,拿到用户访问的路径 data_str = str(data, encoding="utf8") # print(data_str) url = data_str.split(" ")[0].split(" ")[1] print(url) func = None for i in url2func: if url == i[0]: func = i[1] # 拿到将要执行的函数 break else: func = None if func: msg = func(url) # 执行对应的函数 else: msg = b'<h1>404</h1>' # 找不到要执行的函数就返回404 # 按照HTTP协议的格式要求 回复消息 conn.send(b'HTTP/1.1 200 OK ') # 发送状态行 conn.send(msg) # 发送响应体 conn.close()
服务器程序
真实开发中的Python web程序图解
web程序分为:服务器程序和web应用程序
服务器程序主要处理浏览器与服务端之间的通信。
服务器程序负责对socket服务器进行封装,以及对请求的各种数据进行整理。
web应用程序主要负责具体的业务逻辑:1、处理不同的访问,返回不同的页面;2、在HTML页面上展现从数据库拿到的数据。
WSGI服务器
WSGI(Web Server Gateway Interface)定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。
常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
wsgiref代替socket实现客户端与服务端的通信
import time from wsgiref.simple_server import make_server def home(url): s = "this is {} page!".format(url) return bytes(s, encoding="utf8") def index(url): return b'<h1>index page</h1>' def user(url): # 不同的用户得到的页面上显示不同的时间 c_time = str(time.time()) with open("user.html", "r") as f: data_s = f.read() data_s = data_s.replace("@@xx@@", c_time) # 替换后的字符串数据 return bytes(data_s, encoding="utf8") def login(url): with open("login.html", "rb") as f: return f.read() # url和将要执行的函数的对应关系 url2func = [ ("/index/", index), ("/home/", home), ("/user/", user), ("/login/", login), ] # 按照wsgiref的要求定义一个run_server函数 def run_server(environ, start_response): """ :param environ: 跟请求相关的参数 :param start_response: :return: """ # 设置HTTP响应的状态码和头信息 start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # text/html说明返回的是一个HTML的文本文件 url = environ['PATH_INFO'] # 取到用户输入的url func = None for i in url2func: if url == i[0]: func = i[1] # 拿到将要执行的函数 break else: func = None if func: msg = func(url) # 执行对应的函数 else: msg = b'<h1>404</h1>' # 找不到要执行的函数就返回404 return [msg, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
模板渲染工具jinja2
下载
pip3 install jinja2
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>hello</h1> <table border="1"> <thead> <tr> <th>id</th> <th>姓名</th> <th>爱好</th> </tr> </thead> <tbody> {% for user in user_list %} <tr> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.hobby}}</td> </tr> {% endfor %} </tbody> </table> </body> </html>
使用jinja2渲染user.html文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from wsgiref.simple_server import make_server from jinja2 import Template with open("user.html", "r", encoding="utf8") as f: data_s = f.read() template = Template(data_s) # 生成一个模板文件实例 msg = template.render({"id": 1,"name":"chris","hobby":"book"}) # 把数据填充到模板里面 return bytes(msg, encoding="utf8") # url和将要执行的函数的对应关系 url2func = [ ("/user/", user), ] # 按照wsgiref的要求定义一个run_server函数 def run_server(environ, start_response): """ :param environ: 跟请求相关的参数 :param start_response: :return: """ # 设置HTTP响应的状态码和头信息 start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) url = environ['PATH_INFO'] # 取到用户输入的url func = None for i in url2func: if url == i[0]: func = i[1] # 拿到将要执行的函数 break else: func = None if func: msg = func(url) # 执行对应的函数 else: msg = b'<h1>404</h1>' # 找不到要执行的函数就返回404 return [msg, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.server_forever()
jinja2渲染HTML文件的本质是字符串替换,即将HTML中的特殊字符替换成要展示的数据。
利用数据库里查询的数据来填充user.html文件
1、使用pymysql模块连接数据库
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='xx', database='xx', charset='utf8' ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute('select * from user') ret = cursor.fetchall() cursor.close() conn.close() #conn建立的链接,cursor数据库邮标 #host数据库服务器IP,port数据库端口, user数据库用户名,数据库密码password,要使用的数据库库名database,数据库字符编码charset
2、创建user表,以及插入信息
3、利用数据库查出的信息填充user.html文档,并利用jinja2渲染user.thml文件。
from wsgiref.simple_server import make_server from jinja2 import Template import pymysql def user(url): # 从数据库里面去到所有的用户信息, conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='xx', database='xx', charset='utf8' ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute('select * from user') ret = cursor.fetchall() print(ret) # 在页面上显示出来 with open("user.html", "r", encoding="utf8") as f: data_s = f.read() template = Template(data_s) # 生成一个模板文件实例 msg = template.render({"user_list": ret}) # 把数据填充到模板里面 return bytes(msg, encoding="utf8") # url和将要执行的函数的对应关系 url2func = [ ("/user/", user), ] # 按照wsgiref的要求定义一个run_server函数 def run_server(environ, start_response): """ :param environ: 跟请求相关的参数 :param start_response: :return: """ # 设置HTTP响应的状态码和头信息 start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) url = environ['PATH_INFO'] # 取到用户输入的url func = None for i in url2func: if url == i[0]: func = i[1] # 拿到将要执行的函数 break else: func = None if func: msg = func(url) # 执行对应的函数 else: msg = b'<h1>404</h1>' # 找不到要执行的函数就返回404 return [msg, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
Django
官网地址https://www.djangoproject.com/download/
安装
pip3 install django==1.11.11 #设置下载版本
创建django项目
#命令行输入命令,创建一个名为"mydjg"的Django项目 django-admin startproject mydjg
目录介绍
#创建一个Django项目,项目中会默认生成以下文件 mysite/ ├── manage.py # 管理文件 └── mysite # 项目目录 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函数的对应关系 └── wsgi.py # runserver命令就使用wsgiref模块做简单的web server
运行项目命令
#命令行输入命令 python manage.py runserver 127.0.0.1:8090
pycharm运行方式
TEMPLATES文件配置
TEMPLATES文件夹存放项目相关HTML文件
#在setting文件中查找templates文件夹路径的配置是否完成 #templates文件夹中存放项目相关的HTML文件,Django会去该文件夹的路径下查找相关HTML文件 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #template文件夹位置,告诉Django取该目录下找HTML文件 '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', ], }, }, ]
静态文件配置
# setting文件中的静态文件配置信息 # 静态文件的引用路径(HTML文件中通过什么来找静态文件) STATIC_URL = '/static/' # 别名 # 告诉Django去这个目录下找我的静态文件(CSS和JS文件 图片等) STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), ]
Django基础三件套
1、导入模块
from django.shortcuts import HttpResponse,render,redirect
2、使用该模块回应请求
HttpResponse
#使用该模块给浏览器回应具体消息 #传入字符串参数,返回给浏览器 def home(request): #此处可添加业务逻辑代码 return HttpResponse("hello")
render
#返回给浏览器一个HTML文件,浏览器会根据HTML标签显示器页面 def login(request): #此处可添加业务逻辑代码 return render(request, "login.html") 除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。
将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2) 如下: def index(request): # 业务逻辑代码 return render(request, "index.html", {"name": "chris", "hobby": ["read", "music"]})
redirect
#传一个URL参数,让浏览器跳转到该地址页面 def login(request): #业务逻辑代码 return redirect("https://www.baidu.com/") #URL为绝对地址 def login(request): #业务逻辑代码 return redirect("/home/") #跳转的地址是自己的网页,URL为相对路径
页面跳转重定向
浏览器向服务端发请求时,服务端回应的是一个跳转信息(redirect),则浏览器会拿着跳转信息的网址,重新给该网址发送请求,跳转到新网址的页面。
图片说明如下:
对应关系
#在urls文件中存放URL和函数的对应关系 例如: urlpatterns = [ url(r'^admin/', admin.site.urls), #默认生成 url(r'^home/',home), url(r'^login/',login), ]
启动Django报错
Django 启动时报错 “UnicodeEncodeError ...”
报错原因:通常是因为计算机名为中文,改成英文的计算机名重启电脑。
Django 启动报错“SyntaxError: Generator expression must be parenthesized”
报错原因:可能是因为使用了Python3.7.0,而目前(2018-06-12)Python3.7.0和Django还有点兼容性问题,换回Python3.6的环境即可。