目录
视频在线学习地址:https://www.bilibili.com/video/BV1Yp4y117BA
————————————————
——Python全套:BV1jZ4y1p7zQ
学习目标
- 能够说出什么是HTTP协议
- 能够手写代码搭建一个HTTP服务器
- 了解WSGI接口
- 能够看懂自定义WSGI服务器
- 能够使用requests插件发送请求
1. 使用谷歌/火狐浏览器分析
在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP,所以:
- HTML是一种用来定义网页的文本,会HTML,就可以编写网页;
- HTTP是在网络上传输HTML的协议,用于浏览器和服务器的通信。
Chrome浏览器提供了一套完整地调试工具,非常适合Web开发。
安装好Chrome浏览器后,打开Chrome,在菜单中选择“视图”,“开发者”,“开发者工具”,就可以显示开发者工具:
说明
- Elements显示网页的结构
- Network显示浏览器和服务器的通信
我们点Network,确保第一个小红灯亮着,Chrome就会记录所有浏览器和服务器之间的通信:
2. http协议的分析
当我们在地址栏输入www.sina.com时,浏览器将显示新浪的首页。在这个过程中,浏览器都干了哪些事情呢?通过Network的记录,我们就可以知道。在Network中,找到www.sina.com那条记录,点击,右侧将显示Request Headers,点击右侧的view source,我们就可以看到浏览器发给新浪服务器的请求:
2.1 浏览器请求
说明
最主要的头两行分析如下,第一行:
GET / HTTP/1.1
CopyGET表示一个读取请求,将从服务器获得网页数据,/表示URL的路径,URL总是以/开头,/就表示首页,最后的HTTP/1.1指示采用的HTTP协议版本是1.1。目前HTTP协议的版本就是1.1,但是大部分服务器也支持1.0版本,主要区别在于1.1版本允许多个HTTP请求复用一个TCP连接,以加快传输速度。
从第二行开始,每一行都类似于Xxx: abcdefg:
Host: www.sina.com
Copy表示请求的域名是www.sina.com。如果一台服务器有多个网站,服务器就需要通过Host来区分浏览器请求的是哪个网站。
2.2 服务器响应
继续往下找到Response Headers,点击view source,显示服务器返回的原始响应数据:
HTTP响应分为Header和Body两部分(Body是可选项),我们在Network中看到的Header最重要的几行如下:
HTTP/1.1 200 OK
Copy200表示一个成功的响应,后面的OK是说明。
如果返回的不是200,那么往往有其他的功能,例如
- 失败的响应有404 Not Found:网页不存在
- 500 Internal Server Error:服务器内部出错
- ...等等...
Content-Type: text/html
CopyContent-Type指示响应的内容,这里是text/html表示HTML网页。
请注意,浏览器就是依靠Content-Type来判断响应的内容是网页还是图片,是视频还是音乐。浏览器并不靠URL来判断响应的内容,所以,即使URL是
http://www.baidu.com/meimei.jpg
,它也不一定就是图片。HTTP响应的Body就是HTML源码,我们在菜单栏选择“视图”,“开发者”,“查看网页源码”就可以在浏览器中直接查看HTML源码:
浏览器解析过程
当浏览器读取到新浪首页的HTML源码后,它会解析HTML,显示页面,然后,根据HTML里面的各种链接,再发送HTTP请求给新浪服务器,拿到相应的图片、视频、Flash、JavaScript脚本、CSS等各种资源,最终显示出一个完整的页面。所以我们在Network下面能看到很多额外的HTTP请求。
3. 总结
3.1 HTTP请求
跟踪了新浪的首页,我们来总结一下HTTP请求的流程:
3.1.1 步骤1:浏览器首先向服务器发送HTTP请求,请求包括:
方法:GET还是POST,GET仅请求资源,POST会附带用户数据;
路径:/full/url/path;
域名:由Host头指定:Host: www.sina.com
以及其他相关的Header;
如果是POST,那么请求还包括一个Body,包含用户数据
3.1.1 步骤2:服务器向浏览器返回HTTP响应,响应包括:
响应代码:200表示成功,3xx表示重定向,4xx表示客户端发送的请求有错误,5xx表示服务器端处理时发生了错误;
响应类型:由Content-Type指定;
以及其他相关的Header;
通常服务器的HTTP响应会携带内容,也就是有一个Body,包含响应的内容,网页的HTML源码就在Body中。
3.1.1 步骤3:如果浏览器还需要继续向服务器请求其他资源,比如图片,就再次发出HTTP请求,重复步骤1、2。
Web采用的HTTP协议采用了非常简单的请求-响应模式,从而大大简化了开发。当我们编写一个页面时,我们只需要在HTTP请求中把HTML发送出去,不需要考虑如何附带图片、视频等,浏览器如果需要请求图片和视频,它会发送另一个HTTP请求,因此,一个HTTP请求只处理一个资源(此时就可以理解为TCP协议中的短连接,每个链接只获取一个资源,如需要多个就需要建立多个链接)
HTTP协议同时具备极强的扩展性,虽然浏览器请求的是
http://www.sina.com
的首页,但是新浪在HTML中可以链入其他服务器的资源,比如<img src="http://i1.sinaimg.cn/home/2013/1008/U8455P30DT20131008135420.png">
,从而将请求压力分散到各个服务器上,并且,一个站点可以链接到其他站点,无数个站点互相链接起来,就形成了World Wide Web,简称WWW。
3.2 HTTP格式
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。
HTTP协议是一种文本协议,所以,它的格式也非常简单。
3.2.1 HTTP GET请求的格式:
GET /path HTTP/1.1 Header1: Value1 Header2: Value2 Header3: Value3
Copy每个Header一行一个,换行符是\r\n。
3.2.2 HTTP POST请求的格式:
POST /path HTTP/1.1 Header1: Value1 Header2: Value2 Header3: Value3 body data goes here...
Copy当遇到连续两个\r\n时,Header部分结束,后面的数据全部是Body。
3.2.3 HTTP响应的格式:
200 OK Header1: Value1 Header2: Value2 Header3: Value3 body data goes here...
CopyHTTP响应如果包含body,也是通过\r\n\r\n来分隔的。
请再次注意,Body的数据类型由Content-Type头来确定,如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。
当存在Content-Encoding时,Body数据是被压缩的,最常见的压缩方式是gzip,所以,看到Content-Encoding: gzip时,需要将Body数据先解压缩,才能得到真正的数据。压缩的目的在于减少Body的大小,加快网络传输。
手动搭建HTTP服务器
import re import socket from multiprocessing import Process class WSGIServer(): def __init__(self, server, port, root): self.server = server self.port = port self.root = root self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server_socket.bind((self.server, self.port)) self.server_socket.listen(128) def handle_socket(self, socket): data = socket.recv(1024).decode('utf-8').splitlines()[0] file_name = re.match(r'[^/]+(/[^ ]*)', data)[1] # print(file_name) if file_name == '/': file_name = self.root + '/index.html' else: file_name = self.root + file_name try: file = open(file_name, 'rb') except IOError: response_header = 'HTTP/1.1 404 NOT FOUND \r\n' response_header += '\r\n' response_body = '========Sorry,file not found======='.encode('utf-8') else: response_header = 'HTTP/1.1 200 OK \r\n' response_header += '\r\n' response_body = file.read() finally: socket.send(response_header.encode('utf-8')) socket.send(response_body) def forever_run(self): while True: client_socket, client_addr = self.server_socket.accept() # self.handle_socket(client_socket) p = Process(target=self.handle_socket, args=(client_socket,)) p.start() client_socket.close() if __name__ == '__main__': ip = '0.0.0.0' port = 8899 server = WSGIServer(ip, port, './pages') print('server is running at {}:{}'.format(ip, port)) server.forever_run()
WSGI接口
WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return '<h1>Hello, web!</h1>'
上面的
application()
函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
- environ:一个包含所有HTTP请求信息的
dict
对象;- start_response:一个发送HTTP响应的函数。
在
application()
函数中,调用:start_response('200 OK', [('Content-Type', 'text/html')])
Copy就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次
start_response()
函数。start_response()
函数接收两个参数,一个是HTTP响应码,一个是一组list
表示的HTTP Header,每个Header用一个包含两个str
的tuple
表示。通常情况下,都应该把
Content-Type
头发送给浏览器。其他很多常用的HTTP Header也应该发送。然后,函数的返回值
'<h1>Hello, web!</h1>'
将作为HTTP响应的Body发送给浏览器。有了WSGI,我们关心的就是如何从
environ
这个dict
对象拿到HTTP请求信息,然后构造HTML,通过start_response()
发送Header,最后返回Body。整个
application()
函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。不过,等等,这个
application()
函数怎么调用?如果我们自己调用,两个参数environ
和start_response
我们没法提供,返回的str
也没法发给浏览器。所以
application()
函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。但是现在,我们只想尽快测试一下我们编写的application()
函数真的可以把HTML输出到浏览器,所以,要赶紧找一个最简单的WSGI服务器,把我们的Web应用程序跑起来。好消息是Python内置了一个WSGI服务器,这个模块叫wsgiref,它是用纯Python编写的WSGI服务器的参考实现。所谓“参考实现”是指该实现完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用。
新建WSGI服务器
- 创建
hello.py
文件,用来实现WSGI应用的处理函数。def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) print(environ) return ['<h1>Hello, web!</h1>'.encode('utf-8'),'hello'.encode('utf-8')]
- 创建
server.py
文件,用来启动WSGI服务器,加载appliction
函数# 从wsgiref模块导入: from wsgiref.simple_server import make_server # 导入我们自己编写的application函数: from hello import application # 创建一个服务器,IP地址为空,端口是8000,处理函数是application: httpd = make_server('', 8000, application) print("Serving HTTP on port 8000...") # 开始监听HTTP请求: httpd.serve_forever()
使用if管理请求路径
文件结构:
├── server.py ├── utils.py ├── pages └── index.html └── templates └── info.html --------------------------------- Copy utlis.py文件 ---------------------------------- PAGE_ROOT = './pages' TEMPLATE_ROOT = './templates' def load_html(file_name, start_response, root=PAGE_ROOT): """ 加载HTML文件时调用的方法 :param file_name: 需要加载的HTML文件 :param start_response: 函数,用来设置响应头。如果找到文件,请求头设置为200,否则设置为410 :param root: HTML文件所在的目录。默认PAGE_ROOT表示静态HTML文件,TEMPLATE_ROOT表示的是模板文件 :return: 读取HTML文件成功的话,返回HTML文件内容;读取失败提示资源被删除 """ file_name = root + file_name try: file = open(file_name, 'rb') except IOError: start_response('410 GONE', [('Content-Type', "text/html;charset=utf-8")]) return ['资源被删除了'.encode('utf-8')] else: start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")]) content = file.read() return [content] def load_template(file_name, start_respone, **kwargs): """ 加载模板文件 :param file_name: 需要加载的模板文件名 :param start_respone: 函数,用来设置响应头。如果找到文件,请求头设置为200,否则设置为410 :param kwargs: 用来设置模板里的变量 :return: 读取HTML文件成功的话,返回HTML文件内容;读取失败提示资源被删除 """ content = load_html(file_name, start_respone, root=TEMPLATE_ROOT) html = content[0].decode('utf-8') if html.startswith('<!DOCTYPE html>'): return [html.format(**kwargs).encode('utf-8')] else: return content ----------------------------- Copy service.py文件 ------------------------------ from wsgiref.simple_server import make_server from utils import load_html, load_template def show_home(start_response): return load_html('/index.html', start_response) def show_test(start_response): start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")]) return ['我是一段普通的文字'.encode('utf-8')] def show_info(start_response): return load_template('/info.html', start_response, name='张三',age=18}) def application(environ, start_response): path = environ.get('PATH_INFO') # 处理首页请求(加载一个HTML文件) if path == '/' or path == '/index.html': result = show_home(start_response) return result # 处理test.html请求(返回一个普通的字符串) elif path == '/test.html': return show_test(start_response) # 处理info.html请求(加载一个模板并且返回) elif path == '/info.html': return show_info(start_response) # 其它请求暂时无法处理,返回404 else: start_response('400 NOT FOUND', [('Content-Type', "text/html;charset=utf-8")]) return ['页面未找到'.encode('utf-8')] httpd = make_server('', 8000, application) print("Serving HTTP on port 8000...") httpd.serve_forever()
使用字典管理请求路径
文件结构:
├── server.py ├── utils.py ├── urls.py ├── pages └── index.html └── templates └── info.html --------------------------------------------- Copy urls.py文件:该文件里只有一个字典对象,用来保存请求路径和处理函数之间的对应关系。 --------------------------------------------- urls = { '/': 'show_home', '/index.html': 'show_home', '/test.html': 'show_test', '/info.html': 'show_info' } -------------------------------------- Copy server.py文件: -------------------------------------- from wsgiref.simple_server import make_server from urls import urls from utils import load_html, load_template def show_home(start_response): return load_html('/index.html', start_response) def show_test(start_response): start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")]) return ['我是一段普通的文字'.encode('utf-8')] def show_info(start_response): return load_template('/info.html', start_response, name='张三',age=18}) def application(environ, start_response): path = environ.get('PATH_INFO') # 这里不再是一大堆的if...elif语句了,而是从urls字典里获取到对应的函数 func = urls.get(path) if func: return eval(func)(start_response) # 其它请求暂时无法处理,返回404 else: start_response('400 NOT FOUND', [('Content-Type', "text/html;charset=utf-8")]) return ['页面未找到'.encode('utf-8')] httpd = make_server('', 8000, application) print("Serving HTTP on port 8000...") httpd.serve_forever()
使用装饰器管理请求路径
from wsgiref.simple_server import make_server from utils import load_html, load_template g_url_route = {} def route(url): def handle_action(action): g_url_route[url] = action def do_action(start_response): return action(start_response) return do_action return handle_action @route('/index.html') @route('/') def show_home(start_response): return load_html('/index.html', start_response) @route('/test.html') def show_test(start_response): start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")]) return ['我是一段普通的文字'.encode('utf-8')] @route('/info.html') def show_info(start_response): return load_template('/info.html', start_response, name='张三', age=18) def application(environ, start_response): file_name = environ.get('PATH_INFO') try: return g_url_route[file_name](start_response) except Exception: start_response('404 NOT FOUND', [('Content-Type', 'text/html;charset=utf-8')]) return ['对不起,界面未找到'.encode('utf-8')] if __name__ == '__main__': httpd = make_server('', 8000, application) print("Serving HTTP on port 8000...") httpd.serve_forever()
requests模块
除了使用浏览器给服务器发送请求以外,我们还可以使用第三方模块requests用代码来给服务器发送器请求,并获取结果。
url = 'https://www.apiopen.top/satinApi' params = {'type': 1, 'page': 2} response = requests.get(url, params) print(response) # 方法二: 只能用于get请求 url = 'https://www.apiopen.top/satinApi?type=1&page=1' response = requests.get(url) # print(response) # 2.获取请求结果 print(response.headers) # 2)响应体(数据) # a.获取二进制对应的原数据(数据本身是图片、压缩文件、视频等文件数据) content = response.content print(type(content)) # b.获取字符类型的数据 text = response.text print(type(text)) # c.获取json数据(json转换成python对应的数据) json = response.json() print(type(json)) print(json)
01-HTTP服务器
import socket
# HTTP 服务器都是基于TCP的socket链接
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('192.168.31.199', 9090))
server_socket.listen(128)
# 获取的数据是一个元组,元组里有两个元素
# 第 0 个元素是 客户端的socket链接
# 第 1 个元素是 客户端的ip地址和端口号
while True:
client_socket, client_addr = server_socket.accept()
# 从客户端的 socket 里获取数据
data = client_socket.recv(1024).decode('utf8')
print('接收到{}的数据{}'.format(client_addr[0], data))
# 返回内容之前,需要先设置HTTP响应头
# 设置一个响应头就换一行
client_socket.send('HTTP/1.1 200 OK\n'.encode('utf8'))
client_socket.send('content-type:text/html\n'.encode('utf8'))
# 所有的响应头设置完成以后,再换行
client_socket.send('\n'.encode('utf8'))
# 发送内容
client_socket.send('<h1 style="color:red">hello world</h1>'.encode('utf8'))
02-HTTP请求头
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('192.168.31.199', 8090))
server_socket.listen(128)
while True:
client_socket, client_addr = server_socket.accept()
data = client_socket.recv(1024).decode('utf8')
print('接收到{}的数据{}'.format(client_addr[0], data))
"""
# GET 请求方式,GET/POST/PUT/DELETE... ...
# /index.html?name=jack&age=18 请求的路径以及请求参数
# HTTP/1.1 HTTP版本号
GET /index.html?name=jack&age=18 HTTP/1.1
Host: 192.168.31.199:8090 # 请求的服务器地址
Upgrade-Insecure-Requests: 1
# UA 用户代理,最开始设计的目的,是为了能从请求头里辨识浏览器的类型
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
"""
client_socket.send('HTTP/1.1 200 OK\n'.encode('utf8'))
client_socket.send('content-type:text/html\n'.encode('utf8'))
client_socket.send('\n'.encode('utf8'))
client_socket.send('<h1 style="color:red">hello world</h1>'.encode('utf8'))
03-ip地址绑定
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ip地址只能通过ip地址访问
# server_socket.bind(('192.168.31.199', 8090))
# 能够通过 127.0.0.1 和 localhost 来访问
# server_socket.bind(('127.0.0.1', 8090))
# 127.0.0.1 和 0.0.0.0 都表示本机
# 0.0.0.0 表示所有的可用的地址
server_socket.bind(('0.0.0.0', 8090))
server_socket.listen(128)
while True:
client_socket, client_addr = server_socket.accept()
data = client_socket.recv(1024).decode('utf8')
print('接收到{}的数据{}'.format(client_addr[0], data))
client_socket.send('HTTP/1.1 200 OK\n'.encode('utf8'))
client_socket.send('content-type:text/html\n'.encode('utf8'))
client_socket.send('\n'.encode('utf8'))
client_socket.send('<h1 style="color:red">hello world</h1>'.encode('utf8'))
04-根据不同的请求返回不同的内容
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 8090))
server_socket.listen(128)
while True:
client_socket, client_addr = server_socket.accept()
data = client_socket.recv(1024).decode('utf8')
path = ''
if data: # # 浏览器发送过来的数据有可能是空的
path = data.splitlines()[0].split(' ')[1]
print('请求的路径是{}'.format(path))
response_body = 'hello world'
# 响应头
response_header = 'HTTP/1.1 200 OK\n' # 200 OK 成功了
if path == '/login':
response_body = '欢迎来到登录页面'
elif path == '/register':
response_body = '欢迎来到注册页面'
elif path == '/':
response_body = '欢迎来到首页'
else:
# 页面未找到 404 Page Not Found
response_header = 'HTTP/1.1 404 Page Not Found\n'
response_body = '对不起,您要查看的页面不存在!!!'
response_header += 'content-type:text/html;charset=utf8\n'
response_header += '\n'
# 响应
response = response_header + response_body
# 发送给客户端
client_socket.send(response.encode('utf8'))
05-面对对象的封装
import socket
class MyServer(object):
def __init__(self, ip, port):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((ip, port))
self.socket.listen(128)
def run_forever(self):
while True:
client_socket, client_addr = self.socket.accept()
data = client_socket.recv(1024).decode('utf8')
path = ''
if data:
path = data.splitlines()[0].split(' ')[1]
response_header = 'HTTP/1.1 200 OK\n'
if path == '/login':
response_body = '欢迎来到登录页面'
elif path == '/register':
response_body = '欢迎来到注册页面'
elif path == '/':
response_body = '欢迎来到首页'
else:
response_header = 'HTTP/1.1 404 Page Not Found\n'
response_body = '对不起,您要查看的页面不存在!!!'
response_header += 'content-type:text/html;charset=utf8\n'
response_header += '\n'
response = response_header + response_body
client_socket.send(response.encode('utf8'))
server = MyServer('0.0.0.0', 9090)
server.run_forever()
06-wsgi服务器
from wsgiref.simple_server import make_server
# demo_app 需要两个参数
# 第 0 个参数,表示环境(电脑的环境;请求路径相关的环境)
# 第 1 个参数,是一个函数,用来返回响应头
# 这个函数需要一个返回值,返回值是一个列表
# 列表里只有一个元素,是一个二进制,表示返回给浏览器的数据
def demo_app(environ, start_response):
# environ是一个字典,保存了很多的数据
# 其中重要的一个是 PATH_INFO能够获取到用户的访问路径
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')])
return ['hello'.encode('utf8')] # 浏览器显示的内容
if __name__ == '__main__':
# demo_app 是一个函数,用来处理用户的请求
httpd = make_server('', 8000, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
# 代码的作用是打开电脑的浏览器,并在浏览器里输入 http://localhost:8000/xyz?abc
# import webbrowser
# webbrowser.open('http://localhost:8000/xyz?abc')
# 处理一次请求
# httpd.handle_request()
httpd.serve_forever() # 服务器在后台一致运行
07-自定义WSGI服务器
from wsgiref.simple_server import make_server
def demo_app(environ, start_response):
path = environ['PATH_INFO']
print('path= {}'.format(path))
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ('sss', 'dddd')])
return ['你好'.encode('utf8')]
if __name__ == '__main__':
httpd = make_server('', 8080, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
08-根据请求路径返回不同内容
from wsgiref.simple_server import make_server
def demo_app(environ, start_response):
path = environ['PATH_INFO']
# 状态码: RESTFUL ==> 前后端分离
# 2XX: 请求响应成功
# 3XX: 重定向
# 4XX: 客户端的错误。 404 客户端访问了一个不存在的地址 405:请求方式不被允许
# 5XX: 服务器的错误。
status_code = '200 OK' # 默认状态码是 200
if path == '/':
response = '欢迎来到我的首页'
elif path == '/test':
response = '欢迎阿里到test页面'
elif path == '/demo':
response = '欢迎来到demo页面'
else:
status_code = '404 Not Found' # 如果页面没有配置,返回404
response = '页面走丢了'
start_response(status_code, [('Content-Type', 'text/html;charset=utf8')])
return [response.encode('utf8')]
if __name__ == '__main__':
httpd = make_server('', 8080, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
# 状态码: RESTFUL ==> 前后端分离 # 2XX: 请求响应成功 # 3XX: 重定向 # 4XX: 客户端的错误。 404 客户端访问了一个不存在的地址 405:请求方式不被允许 # 5XX: 服务器的错误。
09-返回不同类型
import json
from wsgiref.simple_server import make_server
def demo_app(environ, start_response):
path = environ['PATH_INFO']
# print(environ.get('QUERY_STRING')) # QUERY_STRING ==> 获取到客户端GET请求方式传递的参数
# POST 请求数据的方式后面再说
status_code = '200 OK'
if path == '/':
response = '欢迎来到我的首页'
elif path == '/test':
response = json.dumps({'name': 'zhangsan', 'age': 18})
elif path == '/demo':
with open('pages/xxxx.txt', 'r', encoding='utf8') as file:
response = file.read()
elif path == '/hello':
with open('pages/hello.html', 'r', encoding='utf8') as file:
response = file.read()
elif path == '/info':
# 查询数据库,获取到用户名
name = 'jack'
with open('pages/info.html', 'r', encoding='utf8') as file:
# '{username}, 欢迎回来'.format(username=name)
# flask django 模板,渲染引擎
response = file.read().format(username=name, age=18, gender='男')
else:
status_code = '404 Not Found'
response = '页面走丢了'
start_response(status_code, [('Content-Type', 'text/html;charset=utf8')])
return [response.encode('utf8')]
if __name__ == '__main__':
httpd = make_server('', 8080, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
10-HTTP服务器优化
import json
from wsgiref.simple_server import make_server
def load_file(file_name, **kwargs):
try:
with open('pages/' + file_name, 'r', encoding='utf8') as file:
content = file.read()
if kwargs: # kwargs = {'username':'zhangsan','age':19,'gender':'male'}
content = content.format(**kwargs)
# {username},欢迎回来,你今年{age}岁了,你的性别是{gender}.format(**kwargs)
return content
except FileNotFoundError:
print('文件未找到')
def demo_app(environ, start_response):
path = environ['PATH_INFO']
status_code = '200 OK'
if path == '/':
response = '欢迎来到我的首页'
elif path == '/test':
response = json.dumps({'name': 'zhangsan', 'age': 18})
elif path == '/demo':
response = load_file('xxxx.txt')
elif path == '/hello':
response = load_file('hello.html')
elif path == '/info':
response = load_file('info.html', username='zhangsan', age=19, gender='male')
else:
status_code = '404 Not Found'
response = '页面走丢了'
start_response(status_code, [('Content-Type', 'text/html;charset=utf8')])
return [response.encode('utf8')]
if __name__ == '__main__':
httpd = make_server('', 8080, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
11-HTTP服务器函数封装
import json
from wsgiref.simple_server import make_server
def load_file(file_name, **kwargs):
try:
with open('pages/' + file_name, 'r', encoding='utf8') as file:
content = file.read()
if kwargs: # kwargs = {'username':'zhangsan','age':19,'gender':'male'}
content = content.format(**kwargs)
# {username},欢迎回来,你今年{age}岁了,你的性别是{gender}.format(**kwargs)
return content
except FileNotFoundError:
print('文件未找到')
def index():
return '欢迎来到我的首页'
def show_test():
return json.dumps({'name': 'zhangsan', 'age': 18})
def show_demo():
return load_file('xxxx.txt')
def show_hello():
return load_file('hello.html')
def show_info():
return load_file('info.html', username='zhangsan', age=19, gender='male')
def demo_app(environ, start_response):
path = environ['PATH_INFO']
status_code = '200 OK'
if path == '/':
response = index()
elif path == '/test':
response = show_test()
elif path == '/demo':
response = show_demo()
elif path == '/hello':
response = show_hello()
elif path == '/info':
response = show_info()
else:
status_code = '404 Not Found'
response = '页面走丢了'
start_response(status_code, [('Content-Type', 'text/html;charset=utf8')])
return [response.encode('utf8')]
if __name__ == '__main__':
httpd = make_server('', 8080, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
12-使用字典管理路径
import json
from wsgiref.simple_server import make_server
def load_file(file_name, **kwargs):
try:
with open('pages/' + file_name, 'r', encoding='utf8') as file:
content = file.read()
if kwargs: # kwargs = {'username':'zhangsan','age':19,'gender':'male'}
content = content.format(**kwargs)
# {username},欢迎回来,你今年{age}岁了,你的性别是{gender}.format(**kwargs)
return content
except FileNotFoundError:
print('文件未找到')
def index():
return '欢迎来到我的首页'
def show_test():
# return json.dumps({'name': 'zhangsan', 'age': 18})
return json.dumps(['zhangsan', 'lisi', 'wangwu', 'jack'])
def show_demo():
return load_file('xxxx.txt')
def show_hello():
return load_file('hello.html')
def show_info():
return load_file('info.html', username='zhangsan', age=19, gender='male')
def show_foo():
return '我是foo'
url = {
'/': index,
'/test': show_test,
'/demo': show_demo,
'/hello': show_hello,
'/info': show_info,
'/foo': show_foo
}
def demo_app(environ, start_response):
path = environ['PATH_INFO']
status_code = '200 OK'
method = url.get(path)
if method:
response = method()
else:
status_code = '404 Not Found'
response = '页面走丢了'
start_response(status_code, [('Content-Type', 'text/html;charset=utf8')])
return [response.encode('utf8')]
if __name__ == '__main__':
httpd = make_server('', 8090, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
13-requests模块的介绍
# requests 模块是第三方的模块,可以用来发送网络连接
# pip install requests
import requests
response = requests.get('http://127.0.0.1:8090')
# print(response) 结果是一个Response对象
# content 指的是返回的结果,是一个二进制,可以用来传递图片
# print(response.content.decode('utf8')) # 将二进制解码成为字符串
# 获取到的结果就是一个文本
print(response.text)
print(response.status_code) # 200
# 如果返回的结果是一个 json 字符串,可以解析json字符串
# print(response.json())
r = requests.get('http://127.0.0.1:8090/test')
t = r.text # 获取到 json 字符串
print(t, type(t)) # {"name": "zhangsan", "age": 18} <class 'str'>
j = r.json() # 把 json 字符串解析成为python里对应的数据类型
print(j, type(j)) # {'name': 'zhangsan', 'age': 18} <class 'dict'>
HTTP协议:HyperText Transfer Protocol 超文本传输协议 协议的作用就是用来传输超文本 HTML(HyperTextMarkupLanguage) HTML:超文本标记语言 HTTP:用来传输超文本的一个协议 GET /login HTTP/1.1 Host: localhost:8090 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36 Sec-Fetch-Dest: document Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8