47、 Web应用、手写web框架、借助wsgiref模块、动静态网页、Django框架

252 阅读11分钟

web应用

1.python就业方向
    1.web方向----就是通过浏览器访问应用程序(网站)
    2.爬虫岗位----爬取数据
    3.数据分析----工作岗位不多
    4.人工智能----具备数学知识---Autogpt(python写的)

2.web方向:通过浏览器访问的应用程序
    1.两种模式:B/S C/S
    2.web应用程序优点
        1.只需要一个浏览器即可
        2.节省资源
        3.他们不需要更新,他们在服务端更新(所有新的特性都在服务器上执行)
    3.web应用程序缺点
        1.特别依赖服务端程序的健壮性,一旦服务端宕机,客户端立马宕机
    4.web框架
        1.定义:别人写好的模版,我们只需要在模版中固定的位置书写代码即可
            Django框架就是一款专门用来写web应用的框架

手写web框架

import socket

server = socket.socket()
server.bind(('127.0.0.1',8081))
server.listen(3)

while True:
    conn,addr = server.accept()
    data = conn.recv(1024)
    print(data)  # 返回的是请求数据
    """
    b'GET / HTTP/1.1\r\n
    Host: 127.0.0.1:8080\r\n
    Connection: keep-alive\r\n
    Cache-Control: max-age=0\r\n
    sec-ch-ua: "Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"\r\n
    sec-ch-ua-mobile: ?0\r\n
    sec-ch-ua-platform: "macOS"\r\n
    Upgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\n
    Sec-Fetch-Site: none\r\n
    Sec-Fetch-Mode: navigate\r\n
    Sec-Fetch-User: ?1\r\n
    Sec-Fetch-Dest: document\r\n
    Accept-Encoding: gzip, deflate, br\r\n
    Accept-Language: zh-CN,zh;q=0.9\r\n
    \r\n'
    """
    conn.send(b'HTTP/1.1 200 \r\n\r\n')  # 每一个服务端都应该准许HTTP协议
    conn.send(b'hello word')  # http流式协议
    conn.close()

需求:浏览器端地址写什么就返回在浏览器端

import socket

server = socket.socket()
server.bind(('127.0.0.1',8081))
server.listen(3)

while True:
    conn,addr = server.accept()
    data = conn.recv(1024)
    conn.send(b'HTTP/1.1 200 \r\n\r\n')
    data = data.decode('utf-8')
    current_path = data.split()[1]
    # print(current_path)   /index  /login /xxx
    if current_path =='/index':
        conn.send(b'index')
    elif current_path =='/func':
        conn.send(b'func')
    else:
        conn.send(b'404 not found')
    conn.close()
    
ps:代码的缺陷
	1.socket代码重复编写造轮子
	2.针对请求数据格式的处理复杂且重复
 	3.针对不同网址后缀的匹配方式过于lowB 

借助于wsgiref模块

wsgiref内部封装了socket代码和对请求数据的处理

1.优点:
  	1.socket服务端哪些代码不用我们自己写了
		2.HTTP的数据不用我们自己处理了,它帮助我门处理

2.用法
	1.导入模块:from wsgiref.simple_server import make_server
  2.定义函数
  	def run():
    	pass
	3.启动文件
    if __name__ == '__main__':
      make_server('127.0.0.1',8080,run)
    ps:
        1.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler):第一个参数ip地址,第二个参数端口,第三个参数是函数地址
    		2.上面这句话写完,就代码实时监听127.0.0.1:8080这个地址,只要有客户端开访问了,请求都交给run来处理
    	  3.flask框架的源码启动:
        	make_server('127.0.0.1', 8080, obj)
					对象调用,obj()---自动调用__call__方法----flask框架启动入口
	4.启动服务端:server.serve_forever()
5.run(env,response)
	def run(env,response):
    """
    :param env: 所有请求相关的数据都在env中
    :param respomse: 所有的响应数据都在response中
    :return: 返回给浏览器的数据
    """
    print(env)
    response('200 OK',[])
    return [b'hello']
6.wsgiref做的事:--web服务网关接口
	1.请求来了,处理请求过来的http格式的数据,封装成方便我们取值的数据格式
  2.响应走的时候,又把一些数据封装成http格式的数据,发给浏览器

简易版

from wsgiref.simple_server import make_server

def run(env,response):
    print(env) # 大的字典,里面保存了http请求的相关数据
    response('200 OK',[])
    return [b'hello']

if __name__ == '__main__':
    server = make_server('127.0.0.1',8080,run)
    server.serve_forever()

ps:wsgiref模块解决了两个问题
      1.socket代码重复编写造轮子
      2.针对请求数据格式的处理复杂且重复

需求:浏览器端地址写什么就返回在浏览器端

from wsgiref.simple_server import make_server


def run(env,response):
    """
    :param env: 所有请求相关的数据都在env中
    :param respomse: 所有的响应数据都在response中
    :return: 返回给浏览器的数据
    """
    print(env)  # 在浏览器端口输入的index、func ,都在env的大字典里的'PATH_INFO'键内
    response('200 OK',[])
    current_path = env.get('PATH_INFO') # /index   /func
    if current_path == '/index':
        with open('my1.html','rb') as f:
            data = f.read()
        return [data]
    elif current_path == '/func':
        return [b'func']
    else:
        return [b'404 not found']

if __name__ == '__main__':
    server = make_server('127.0.0.1',8081,run)
    server.serve_forever()

优化:优化代码--

# 网关接口
from wsgiref.simple_server import make_server

def index():
    return 'index'

def func():
    return 'func'
def error():
    return '404 not found'

urls =[('/index',index),('/func',func)]

def run(env,response):
    response('200 OK',[])
    current_path = env.get('PATH_INFO') # /index   /func

    func = None
    for url in urls:
        if url[0] == current_path:
           func = url[1]
    if func:
        res = func()
    else:
        res = error()
    return [res.encode('utf8')]

if __name__ == '__main__':
    server = make_server('127.0.0.1',8080,run)
    server.serve_forever()

优化:分文件存储

1.urls.py -------- 后缀与视图函数的对应关系 -------- 路由文件
	eg:urls =[('/index',index),('/func',func)]
2.views.py -------- 视图函数,以后专门用来写逻辑功能的
	eg:里面存很多的函数
3.templates文件夹 -------- 专门用来存储html文件

ps:以后想增加新功能,只需要在urls.py中写路由,然后在视图函数中写功能即可
# wsgiref模块.py文件
from wsgiref.simple_server import make_server
from urls import urls
from views import *

def run(env,response):
    response('200 OK',[])
    current_path = env.get('PATH_INFO') # /index   /func
    func = None
    for url in urls:
        if url[0] == current_path:
           func = url[1]
    if func:
        res = func()
    else:
        res = error()
    return [res.encode('utf8')]
if __name__ == '__main__':
    server = make_server('127.0.0.1',8080,run)
    server.serve_forever()

# urls.py文件
from views import *
urls =[('/index',index),('/func',func)]

# views文件
  def index():
      return 'index'

  def func():
      return 'func'

  def error():
      return '404 not found'

动静态网页

1.动态网页:页面中的数据是从数据库查询出来的
2.静态网页:如html文件,数据写死的

制作动态网页

1.后端代码回去当前时间 然后让前端页面展示

def get_time():
    import time
    c_time = time.strftime('%Y-%m-%d %X')
    with open(r'templates/time1.html','r',encoding ='utf8')as f:
        data = f.read()
    data =data.replace('aaaaa',c_time)
    return data

2.将字典数据传递给html页面并且想要在页面上操作字典数据

def get_dic():
    user_dict = {'name': 'jason', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
    with open(r'templates/time1.html','r',encoding ='utf8')as f:
        data = f.read()
  
  我们无法自己实现>>>:在html页面上使用类似于后端的语法操作数据

jinja2模块

jinja2能够让我们在html文件内使用类似于后端的语法来操作各种数据类型

#views.py文件
from jinja2 import Template
def get_dic():
    user_dict = {'name': 'jason', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
    with open(r'templates/time1.html','r',encoding ='utf8')as f:
        data = f.read()
    temp = Template(data)
    res = temp.render(data=user_dict) # 将字典传递给html页面 页面上通过data即可获取(data仅仅是一个变量名)
    return res
  
templates/time1.html文件
  <h1>aaaaa</h1>
  <h1>{{data}}</h1>
  <h1>{{data['name']}}</h1>
  <h1>{{data.get('pwd')}}</h1>
  <h1>{{data.hobby}}</h1>

前端、后端、数据库三者联动

python中得三大主流web框架

1.Django
    1.特点:大而全(重量级的框架),里面自带了很多的功能和模块,里面也带了很多的文件
  	2.缺陷:开发小项目的时候使用该框架有点笨重(大材小用)
2.flask
    1.特点:小而精(轻量级的框架),自身并没有多少的文件,它也一样可以做django能做的事情,严重依赖第三方模块,需要不停的安装模块----->依赖第三方
     2.当在flask中安装的模块足够多,也差不多类似于Django
3.tornado
    1.特点:异步非阻塞,并且支持高并发,该框架快到可以作为游戏服务器
4.sanic、fastapi...

Django框架

1.版本:
    1.x  (使用)--同步
  	2.x  (学1.x的时候,比较于2.x的区别)--同步
    3.x(不学)--异步
    
2.安装Django   
		pip install Django==1.11
  	ps:以后我们自己装的第三方模块都在:site-packages文件夹下面
    
3.验证django是否安装成功
    在cmd中,输入django-admin,看一些是否有反应,如果有反应就说明安装成功了
  	ps:出现的问题
    	1.在cmd输入:django-admin--找不到:
      	如果django安装成功,它会在scripts文件夹会有一个文件
        解决:将python/scripts加入环境变量
      2.在cmd输入:django-admin--出现一堆错误
      	在cmd黑窗口不会出现,在pycharm中的终端会出现问题---(pycharm中的终端可以用cmd,也可以用powershell,他们两个环境有区别)
        
4.启动注意事项
	1.计算机名称尽量不要有中文
 	2.项目中所有的py文件名尽量不要用中文
 	3.不同版本的python解释器配合不同版本的django 会有一些报错
    	仔细查找一下报错信息 里面会提示你是哪个py文件里面的代码出错
      找到那一行代码 把逗号删除即可
    		widgets.py  152
  4.一个pycharm窗口只允许有一个项目 不要做项目的嵌套

如何使用

1.创建项目

1.命令行创建

可提前切换文件夹路径

django-admin startproject 项目名
eg:jdango-admin startproject myfirst
  
ps:一个pycharm窗口只打开一个django项目

创建项目2.png

2.pycharm创建

创建项目1.png

2.启动django项目

1.cmd启动

先切换路径到manage.py这一层下面
python3 manage.py runserver # 默认启动在本地电脑上
python3 manage.py runserver ip:端口
ps:反应有东西
浏览器端:
It worked!
Next, start your first app by running python manage.py startapp [app_label].

2.pycharm启动

 在导航栏点击绿色按钮-项目名

3.创建应用

1.应用:Django框架就是一款专门用来开发web应用的
2.Django这个框架就类似于:一所大学(空壳)
  应用就相当于一所大学里的二级学院(多个)
  eg:
    我们使用django开发一个淘宝出来
    用户相关的功能----------->user
    订单相关的功能------------>order
    客服相关的功能------------>kefu
    发货相关的功能------------>fahuo
3.每一个应用都是一个独立的功能
  每一个Django项目至少有一个应用

1.cmd创建

python3 manage.py startapp 应用名
python3 manage.py startapp app01 

2.pycharm创建应用

新建项目的时候,一块创建应用

1.创建项目的时候,Application name(只能创一个,后面用cmd)
2.tools--->run manage.py task
   在这个里面命令前面的python3 manage.py省略不写,及:startapp 应用名

4.去配置文件settings.py中去注册应用

当应用创建出来以后,第一件事就去配置文件settings.py中去注册应用,以后每创建一个应用,都要去这里面去注册,如果不注册,创建出来的应用就不生效

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app1.apps.App1Config',  # 全称,这个是pycharm创建Django项目的时候,就创建了应用,自动添加了一个应用,(‘app1’)
    'app01',
    'app02'
]

命令行创建和pycharm创建的区别

1.templates文件夹是否存在
  1.命令行创建的项目没有templates文件夹
  2.pycharm创建的项目有templates文件夹

2.配置文件settings.py:
	1.在命令行创建的项目里的配置文件中,TEMPLATES列表字典中,DIRS键对应的值是空列表
    TEMPLATES = [
      {
          'BACKEND': 'django.template.backends.django.DjangoTemplates',
          'DIRS': [],# 默认没有东西
          ....
      },
      ]
  2.在pycharm创建的项目里的配置文件中,TEMPLATES列表字典中,DIRS对应的值是[BASE_DIR/'templates'],需要改:[os.path.join(BASE_DIR,'templates')]
    TEMPLATES = [
      {
          'BACKEND': 'django.template.backends.django.DjangoTemplates',
          'DIRS': [os.path.join(BASE_DIR,'templates')],
      },
  		]

主要文件介绍

myfirst	-------- 项目名称
	app01	-------- 应用名称
        migrations	-------- 它是数据库迁移记录	-------- 跟数据库相关的时候才会用到
        __init__.py
        admin.py	-------- 跟后台相关的
        apps.py	-------- 跟注册一些东西相关的
        models.py	-------- 这个是跟数据库相关的文件(重要)
        tests.py	-------- 这个是测试文件
        views.py 	-------- 视图函数	-------- 主要写一些功能逻辑
	app02
  myfirst
        __init__.py
          settings.py	-------- django框架的配置文件
          urls.py	-------- 路由文件:后缀和视图函数的对应关系
          wsgi.py	-------- 是一个服务器	-------- wsgiref(支持的并发量小)	-------- uwsgi服务器(支持的并发量高)
  templates	-------- 里面存html文件
  db.sqlite3	-------- django自带的数据库,方便我们测试使用
  manage.py	-------- 这个是django框架的启动文件,入口文件

Django三板斧

遇见的问题

1.index() takes 0 positional arguments but 1 was given
	views.py中,函数里必须要有一个形参----request
	def index(request):
    	# request--------->env----------->请求相关的数据
    	pass
    
2.The view app1.views.index didn't return an HttpResponse object. It returned None instead.
	views.py中,函数里必须要返回http响应---->使用Django三板斧

1.HttpRespons

主要用于直接返回字符串类型的数据

1.class HttpResponse(HttpResponseBase):
				__init__(self, content=b'', *args, **kwargs):
      				pass
	ps:1.HttpRespons是一个类,HttpRespons()实例化对象,自动调用__init__方法。
  	 2.括号里面的参数直接写二进制的字符串
    
2.例子
		def index(request):
    		return HttpResponse('OK!!!') # 里面写的都是字符串,在浏览器端打印了OK!!!

2.render

主要用于返回html页面 并且支持模板语法

1.render(request, template_name, context=None, content_type=None, status=None, using=None)
	ps:render()是一个函数,第一个参数写函数的形参(request),第二个参数写templates文件夹里的html文件的名字.html。
  
2.例子
    def func(request):
        return render(request,'index.html') #加载html文件

3.redirect

主要用于页面重定向

1.redirect(to, *args, **kwargs)----重定向
	ps:redirect()是一个函数,里面的参数可以是函数,也可以是函数名:/函数名/
  
2.例子
def home(request):
    # return redirect('http://www.baidu.com') # 重定向,浏览器端跳转到百度页面
    return redirect('/func/')  # 重定向 调用了func函数,浏览器端加载了html文件
#urls文件:
from app1 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index',views.index),
    url(r'^func/', views.func),
    url(r'^home/', views.home),
]

# views文件
from django.shortcuts import HttpResponse,render,redirect

# Create your views here.
def index(request):
    print('adsdf')
    return HttpResponse('OK!!!') # 里面写的都是字符串,在浏览器端打印了OK!!!

def func(request):
    return render(request,'index.html') #加载html文件


def home(request):
    # return redirect('http://www.baidu.com') # 重定向
    return redirect('/func/')  # 重定向 调用了func函数,浏览器端加载了html文件