对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。

import socket def f1(request): """ 处理用户请求,并返回相应的内容 :param request: 用户请求的所有信息 :return: """ f = open('index.fsw','rb') data = f.read() f.close() return data """ <body> <h1>用户登录</h1> <form> <p><input type="text" placeholder="用户名" /></p> <p><input type="password" placeholder="密码" /></p> </form> </body> """ def f2(request): f = open('aricle.tpl','rb') data = f.read() f.close() print(data) return data """ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用户名</th> <th>邮箱</th> </tr> </thead> <tbody> <tr> <th>1</th> <th>root</th> <th>root@qq.com</th> </tr> </tbody> </table> </body> """ routers = [ ('/xxx', f1), ('/ooo', f2), ]#手动写的网址 def run(): sock = socket.socket() sock.bind(('127.0.0.1',8080)) sock.listen(5) while True: conn,addr = sock.accept() # hang住 #print(conn)#获得的两个套接字,我去charm自己会发送请求一个/favicon.ico页面的报文 # print(addr) # 有人来连接了 # 获取用户发送的数据 data = conn.recv(8096) data = str(data,encoding='utf-8') #print(data)#get报文 headers,bodys = data.split('\r\n\r\n') #print("head:",headers) #print("body:",bodys) body是空的 temp_list = headers.split('\r\n') # print(temp_list) method,url,protocal = temp_list[0].split(' ') # print(method) GET # print(url) /ooo /favicon.ico # print(protocal) /HTTP/1.1 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None for item in routers: if item[0] == url: func_name = item[1] break if func_name: response = func_name(data) print(data) else: response = b"404" conn.send(response) conn.close() if __name__ == '__main__': run()
这种静态页面不能与数据库连接交互,所以也是非常的low。

import socket def f1(request): """ 处理用户请求,并返回相应的内容 :param request: 用户请求的所有信息 :return: """ f = open('index.fsw','rb') data = f.read() f.close() return data """ <body> <h1>用户登录</h1> <form> <p><input type="text" placeholder="用户名" /></p> <p><input type="password" placeholder="密码" /></p> </form> </body> """ def f2(request): f = open('aricle.tpl','r',encoding='utf-8') data = f.read() f.close() import time ctime = time.time() data = data.replace('@@sw@@',str(ctime)) return bytes(data,encoding='utf-8') """ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用户名</th> <th>邮箱</th> </tr> </thead> <tbody> <tr> <th>1</th> <th>@@sw@@</th> <th>root@qq.com</th> </tr> </tbody> </table> </body> """ def f3(request): import pymysql # 创建连接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id,username,password from userinfo") user_list = cursor.fetchall() #print(user_list) cursor.close() conn.close() content_list = [] for row in user_list: tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password']) content_list.append(tp) content = "".join(content_list) f = open('userlist.html','r',encoding='utf-8') template = f.read() f.close() # 模板渲染(模板+数据) data = template.replace('@@sdfsdffd@@',content) return bytes(data,encoding='utf-8') """ mysql> select * from userinfo; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | alex | 123 | +----+----------+----------+ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用户名</th> <th>邮箱</th> </tr> </thead> <tbody> @@sdfsdffd@@ </tbody> </table> </body> """ def f4(request): import pymysql # 创建连接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id,username,password from userinfo") user_list = cursor.fetchall() cursor.close() conn.close() f = open('hostlist.html','r',encoding='utf-8') data = f.read() f.close() # 基于第三方工具实现的模板渲染 from jinja2 import Template template = Template(data) data = template.render(xxxxx=user_list,user='sdfsdfsdf') return data.encode('utf-8') """ {% for row in xxxxx %} <tr> <td>{{row.id}}</td> <td>{{row.username}}</td> <td>{{row.password}}</td> </tr> {% endfor %} </tbody> </table> {{user}} """ routers = [ ('/xxx', f1), ('/ooo', f2), ('/userlist.html', f3), ('/host.html', f4), ] def run(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1',8080)) sock.listen(5) while True: conn,addr = sock.accept() # hang住 # 有人来连接了 # 获取用户发送的数据 data = conn.recv(8096) data = str(data,encoding='utf-8') headers,bodys = data.split('\r\n\r\n') temp_list = headers.split('\r\n') method,url,protocal = temp_list[0].split(' ') conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None for item in routers: if item[0] == url: func_name = item[1] break if func_name: response = func_name(data) else: response = b"404" conn.send(response) conn.close() if __name__ == '__main__': run()
这里要说两点,首先这里使用了jinjia2模块,所以要简单的介绍一下这个模块。
渲染模板(使用render_template方法)
@app.route('/about/') def about(): # return render_template('about.html',user='username') return render_template('about.html',**{'user':'username'})
渲染模版时有两种传递参数的方式:用 var='value' 传递一个参数;使用字典组织多个参数,并且加两个*
号转换成关键字参数传入。
在jinja2模板中:
{{ ... }}
:装载一个变量,模板渲染的时候,会使用传进来的同名参数这个变量代表的值替换掉。
{% ... %}
:装载一个控制语句。
{# ... #}
:装载一个注释,模板渲染的时候会忽视这中间的值。
变量:
设置全局变量:{% set name='xx' %},之后就可以使用此变量了。
设置局部变量:
{% with foo = 42 %}
{{ foo }}
{% endwith %}
这里的foo变量只能在with标签中使用。

{% if kenny.sick %} Kenny is sick. {% elif kenny.dead %} You killed Kenny! You bastard!!! {% else %} Kenny looks okay --- so far {% endif %}

#一般循环 <ul> {% for user in users %} <li>{{ user.username|e }}</li> {% endfor %} </ul> #遍历字典 {% for key, value in my_dict.iteritems() %} <dt>{{ key|e }}</dt> <dd>{{ value|e }}</dd> {% endfor %}
jinja2模块最重要的部分是宏,宏相当于一个搭建好的页面一部分,可以被引入,可以往宏传递参数。可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量,在使用宏时传递参数,从而将宏渲染成为页面的一部分。
更多关于此模块的操作,可以查看博客https://www.cnblogs.com/ygj0930/p/7170621.html。
要说的第二点就是这种方法还是太low了。

import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8")) client.send("<h1 style='color:red'>Hello, yuan</h1>".encode("utf8")) def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8001)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。

# from wsgiref.simple_server import make_server # # # def application(environ, start_response): # start_response('200 OK', [('Content-Type', 'text/html')]) # return [b'<h1>Hello, web!</h1><h2>Hello, py!</h2>'] # # # httpd = make_server('127.0.0.2', 8080, application)#(ip,pork,func) # # print('Serving HTTP on port 8080...') # # 开始监听HTTP请求: # httpd.serve_forever()
django入门
django是一个基于python的高级web开发框架,因为他的高度集成,将会在今后的web开发里给予我们很大的帮助。
首先创建一个django工程(加不加.py都可以):
django-admin.py startproject project_name #django-admin.py startproject myblog
工程下面有几个核心测文件:
manage.py Django项目里面的管理工具,通过它可以调用django shell和数据库等。
settings.py 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
urls.py 负责把URL模式映射到应用程序,路由(就是url与函数的对应关系)。
wsgi.py 调用python内置的wsgiref模块,web服务网关接口。他定义django用什么socket实现,默认就是wsgiref模块。
注:除了命令的方式pycharm也可以进行django工程的搭建。
HttpResponse模块
from django.conf.urls import url from django.shortcuts import HttpResponse def index(request):#request用户请求相关的所有信息 return HttpResponse('whatever') urlpatterns = [ url(r'^index/', index), ]
启动django自带的服务器,
python manage.py runserver 8080
在浏览器访问127.0.0.1:8080/index即可查看到django渲染后的网页。
render模块
from django.conf.urls import url from django.shortcuts import render def index(request):#request用户请求相关的所有信息 return render(request,"a.html")#默认要加request参数 urlpatterns = [ url(r'^index/', index), ]
接下来在浏览器访问127.0.0.1:8080/index即可查看到django渲染后的网页(服务器在改变了代码的情况下会自动重启)。还有,访问的前提是在templates目录下有一个a.html的文件。那么django是如何找到这个路径的呢,因为在settings.py下有一个TEMPLATES列表,其中'DIRS': [os.path.join(BASE_DIR, 'templates')]指明了render需要从这个目录下拿到。
静态文件的配置
在工程文件夹下创建一个static文件夹里面存放静态文件,并将路径写入settings.py下。
STATIC_URL = '/static/' STATICFILES_DIRS=(os.path.join(BASE_DIR,'static'),)
然后在导入文件时一律使用/static引入。
request相关
request.method获得当前请求的方法。request.GET与request.POST可以取到用户提交的数据。
from django.conf.urls import url from django.shortcuts import render,redirect def index(request):#request用户请求相关的所有信息 if request.method =='GET':#浏览器默认传get,区分返回来的信息 return render(request,"a.html") else: u=request.POST.get('user')#取出post方式传回来的字典的值 p=request.POST.get('pwd')#get取不到会转化为none if u=='jeff' and p=='123': return redirect('http://www.baidu.com')#当然也可以重定向到自己的目录 else: return render(request, "a.html") urlpatterns = [ url(r'^index/', index), ]
a.html中的修改:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/static/as.css"> <title>Title</title> </head> <body> <h1>用户登录</h1> <form method="post" action="/index/">{# 发送以post方式发到index下 #} <input type="text" name="user"/> <input type="password" name="pwd"/> <input type="submit" value="login"/> </form> </body> </html>
这样用户访问127.0.0.1:8080/index会使用get方法返回a.html页面,输入用户名和密码提交会用post方法返回给index页面经判断是重定向还是重新输入。
django的渲染模板
django基本的html的模板与jinja2很相似,我们可以在form表单里加入一个{{ msg }}的模板,然后在render里添加一个msg:value用于自动传入。
django的模板取序列的值也是简单粗暴,比如取列表就是{{ list.index }}例如{{ s.0 }}{{ s.1 }},字典就是{{ dict.key }}例如{{row.id}}{{ row.name }}。

from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse,render,redirect
def index(request):#request用户请求相关的所有信息
if request.method =='GET':
return render(request,"a.html")
else:
u=request.POST.get('user')
p=request.POST.get('pwd')
print(u)
print(p)
if u=='jeff' and p=='123':
return render(request, "b.html",{'user':[{'id':1,'name':'jeff','age':0},
{'id': 2, 'name': 'frank', 'age': 1},
{'id': 3, 'name': 'xixi', 'age': 2}]})
else:
return render(request, "a.html")
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', index),
]

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/static/as.css"> <title>Title</title> </head> <body> <h1>用户登录</h1> <form method="post" action="/index/">{# 发送以post方式发到index下 #} <input type="text" name="user"/> <input type="password" name="pwd"/> <input type="submit" value="login"/> </form> </body> </html>

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1"> {% for item in user %} <tr> <td>{{ item.id }}</td> <td>{{ item.name }}</td> <td>{{ item.age }}</td> <td><a href="/del/?nid=={{ item.id }}"></a></td>{# 跳转到专门的del页面 #} </tr> {% endfor %}{#循环结束 #} </table> </body> </html>
这里render传入的字典可以使用pymsql导入,这样就与数据库紧密的连接到一起了。