系列文章目录
[Python]目录
视频及资料和课件
链接:https://pan.baidu.com/s/1LCv_qyWslwB-MYw56fjbDg?pwd=1234
提取码:1234
文章目录
- 前言
- 1. web框架概述
- 1.1 web框架和web服务器的关系介绍
- 1.2 静态资源
- 1.3 动态资源
- 1.4 WSGI协议
- 2. 框架程序开发
- 2.1 框架职责介绍
- 2.2 静态web服务器
- 2.3 动态资源判断
- 3. 模板替换功能开发
- 4. 路由列表功能开发
- 4.1 请求的页面为center.html的处理函数
- 4.2 路由列表
- 4.3 请求处理函数
- 4.4 web框架代码
- 5. 装饰器方式的添加路由
- 5.1 装饰器的定义
- 5.2 web框架代码
- 6. 显示股票信息页面的开发
- 6.1 数据准备
- 6.2 根据sql语句查询股票信息 & 使用查询数据替换模板变量
- 7. 个人中心数据接口的开发
- 8. logging日志
- 8.1 logging日志的介绍
- 8.2 记录程序日志信息的目的是
- 8.3 logging日志级别介绍
- 8.4 logging日志的使用
- 8.4.1 日志信息输出到控制台的示例代码
- 8.4.2 logging日志等级和输出格式的设置
- 8.4.3 日志信息保存到日志文件
- 8.4.4 logging日志在mini-web项目中应用
- 程序入口模块设置logging日志的设置
- INFO级别的日志输出,示例代码
静态web服务器用于静态资源数据请求。
动态资源请求使用web框架专门负责处理,这个web框架其实就是一个为web服务器提供服务的应用程序,简称web框架。
关系说明:
- web服务器接收浏览器发起的请求,如果是动态资源请求找web框架来处理
- web框架负责处理浏览器的动态资源请求,把处理的结果发生给web服务器
- web服务器再把响应结果发生给浏览器
不需要经常变化的资源,这种资源web服务器可以提前准备好,比如: png/jpg/css/js等文件。
和静态资源相反, 这种资源会经常变化,比如: 我们在京东浏览商品时经常会根据条件进行筛选,选择不同条件, 浏览的商品就不同,这种资源web服务器无法提前准备好,需要web框架来帮web服务器进行准备,在这里web服务器可以把.html的资源请求认为是动态资源请求交由web框架进行处理。
它是web服务器和web框架之间进行协同工作的一个规则,WSGI协议规定web服务器把动态资源的请求信息传给web框架处理,web框架把处理好的结果返回给web服务器。
接收web服务器的动态资源请求,给web服务器提供处理动态资源请求的服务。
import socket
import sys
import threading
# 定义web服务器类
class HttpWebServer(object):
def __init__(self, port):
# 创建tcp服务端套接字对象
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(('', port))
# 服务端套接字监听
tcp_server_socket.listen(128)
# 将tcp服务端套接字对象作为属性
self.tcp_server_socket = tcp_server_socket
# 启动web服务器进行工作
def start(self):
# 循环等待客户端的连接
while True:
# 等待接受客户端的连接请求
new_socket, ip_port = self.tcp_server_socket.accept()
print(ip_port)
# 客户端与服务端建立连接后,创建子线程
sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,))
# 设置守护主线程
sub_thread.setDaemon(True)
# 启动线程
sub_thread.start()
# 处理客户端的请求
@staticmethod
def handle_client_request(new_socket):
# 接受客户端发送的数据
# 由于浏览器一般不会发送大量的数据,所以一次接受的数据大小不用指定很大
recv_client_data = new_socket.recv(4096)
# 判断客户端是否处于连接转态
if len(recv_client_data) == 0:
new_socket.close()
return
# 对二进制数据进行解码
recv_client_content = recv_client_data.decode('utf-8')
print(recv_client_content)
# 对字符串进行分割,分割两次,因为访问的资源路径在第二个位置
request_list = recv_client_content.split(' ', maxsplit=2)
# 获取请求资源地址
request_path = request_list[1]
print('请求的资源地址为:', request_path)
# 如果请求的资源地址为 '/', 默认为首页index.html
if request_path == '/':
request_path = '/index.html'
try:
# 打开读取需要发送给客户端的文件
with open('static' + request_path, 'rb') as f:
# 读取文件数据
file_data = f.read()
except Exception as e:
# 打开错误页面
with open('static/error.html', 'rb') as f:
# 读取文件数据
file_data = f.read()
# 将读取的数据封装为http格式的数据
# 响应行
response_line = 'HTTP/1.1 404 Not Found\r\n'
# 响应头
response_header = 'Server: PWS1.0\r\n'
# 响应体
response_body = file_data
# 拼接为响应报文
response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 发送数据给客户端
new_socket.send(response_data)
else:
# 将读取的数据封装为http格式的数据
# 响应行
response_line = 'HTTP/1.1 200 OK\r\n'
# 响应头
response_header = 'Server: PWS1.0\r\n'
# 响应体
response_body = file_data
# 拼接为响应报文
response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 发送数据给客户端
new_socket.send(response_data)
finally:
# 关闭与客户端的套接字
new_socket.close()
# 程序入口函数
def main():
print('服务器运行在9090端口...')
# 创建web服务器对象
# 服务器运行的端口为 9090
web_server = HttpWebServer(9090)
# 启动web服务器进行工作
web_server.start()
# 判断是否为主模块
if __name__ == '__main__':
main()
根据请求资源路径的后缀名进行判断:
(1)如果请求资源路径的后缀名是.html则是动态资源请求, 让web框架程序进行处理。
(2)否则是静态资源请求,让web服务器程序进行处理。
静态web服务器:
# 如果请求的资源地址为 '/', 默认为首页index.html
if request_path == '/':
request_path = '/index.html'
# 判断是动态资源请求还是静态资源请求, 后缀为.html的为动态资源请求
if request_path.endswith('.html'):
# 动态资源请求
# 动态资源请求找web框架处理,需要把请求参数给web框架
# 准备给web框架的参数信息
# env 环境变量,WSGI协议规定给web框架的参数信息最好使用env命名
env = {
'request_path': request_path
# 目前只传请求路径
# 需要传递其他的信息,可以在字典中添加
}
# 使用框架处理动态资源请求
# 1. web框架需要把处理结果返回web服务器
# 2. web服务器负责把返回的结果封装成响应报文发送给浏览器
# web框架需要返回 响应状态信息、响应头、响应体
status, headers, response_body = framework.handle_request(env)
# 将web框架处理的数据封装成响应报文
# 响应行
response_line = 'HTTP/1.1 %s\r\n' % status
# 响应头
response_header = ''
for header in headers:
# 对元组进行拆包,同时拼接进字符串
response_header += '%s: %s\r\n' % header
# 响应报文
response_data = (response_line + response_header + '\r\n' + response_body).encode('utf-8')
# 响应报文发送给浏览器
new_socket.send(response_data)
new_socket.close()
else:
# 静态资源请求
# 执行原先静态web服务器的代码
# 因为原先写的静态web服务器用于处理静态资源
try:
...
except Exception as e:
...
else:
...
finally:
...
import socket
import sys
import threading
# 导入web框架
import framework
# 定义web服务器类
class HttpWebServer(object):
def __init__(self, port):
# 创建tcp服务端套接字对象
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(('', port))
# 服务端套接字监听
tcp_server_socket.listen(128)
# 将tcp服务端套接字对象作为属性
self.tcp_server_socket = tcp_server_socket
# 启动web服务器进行工作
def start(self):
# 循环等待客户端的连接
while True:
# 等待接受客户端的连接请求
new_socket, ip_port = self.tcp_server_socket.accept()
print(ip_port)
# 客户端与服务端建立连接后,创建子线程
sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,))
# 设置守护主线程
sub_thread.setDaemon(True)
# 启动线程
sub_thread.start()
# 处理客户端的请求
@staticmethod
def handle_client_request(new_socket):
# 接受客户端发送的数据
# 由于浏览器一般不会发送大量的数据,所以一次接受的数据大小不用指定很大
recv_client_data = new_socket.recv(4096)
# 判断客户端是否处于连接转态
if len(recv_client_data) == 0:
new_socket.close()
return
# 对二进制数据进行解码
recv_client_content = recv_client_data.decode('utf-8')
print(recv_client_content)
# 对字符串进行分割,分割两次,因为访问的资源路径在第二个位置
request_list = recv_client_content.split(' ', maxsplit=2)
# 获取请求资源地址
request_path = request_list[1]
print('请求的资源地址为:', request_path)
# 如果请求的资源地址为 '/', 默认为首页index.html
if request_path == '/':
request_path = '/index.html'
# 判断是动态资源请求还是静态资源请求, 后缀为.html的为动态资源请求
if request_path.endswith('.html'):
# 动态资源请求
# 动态资源请求找web框架处理,需要把请求参数给web框架
# 准备给web框架的参数信息
# env 环境变量,WSGI协议规定给web框架的参数信息最好使用env命名
env = {
'request_path': request_path
# 目前只传请求路径
# 需要传递其他的信息,可以在字典中添加
}
# 使用框架处理动态资源请求
# 1. web框架需要把处理结果返回web服务器
# 2. web服务器负责把返回的结果封装成响应报文发送给浏览器
# web框架需要返回 响应状态信息、响应头、响应体
status, headers, response_body = framework.handle_request(env)
# 将web框架处理的数据封装成响应报文
# 响应行
response_line = 'HTTP/1.1 %s\r\n' % status
# 响应头
response_header = ''
for header in headers:
# 对元组进行拆包,同时拼接进字符串
response_header += '%s: %s\r\n' % header
# 响应报文
response_data = (response_line + response_header + '\r\n' + response_body).encode('utf-8')
# 响应报文发送给浏览器
new_socket.send(response_data)
new_socket.close()
else:
# 静态资源请求
# 执行原先静态web服务器的代码
# 因为原先写的静态web服务器用于处理静态资源
try:
# 打开读取需要发送给客户端的文件
with open('static' + request_path, 'rb') as f:
# 读取文件数据
file_data = f.read()
except Exception as e:
# 打开错误页面
with open('static/error.html', 'rb') as f:
# 读取文件数据
file_data = f.read()
# 将读取的数据封装为http格式的数据
# 响应行
response_line = 'HTTP/1.1 404 Not Found\r\n'
# 响应头
response_header = 'Server: PWS1.0\r\n'
# 响应体
response_body = file_data
# 拼接为响应报文
response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 发送数据给客户端
new_socket.send(response_data)
else:
# 将读取的数据封装为http格式的数据
# 响应行
response_line = 'HTTP/1.1 200 OK\r\n'
# 响应头
response_header = 'Server: PWS1.0\r\n'
# 响应体
response_body = file_data
# 拼接为响应报文
response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
# 发送数据给客户端
new_socket.send(response_data)
finally:
# 关闭与客户端的套接字
new_socket.close()
# 程序入口函数
def main():
print('服务器运行在9090端口...')
# 创建web服务器对象
# 服务器运行的端口为 9090
web_server = HttpWebServer(9090)
# 启动web服务器进行工作
web_server.start()
# 判断是否为主模块
if __name__ == '__main__':
main()
web框架:
"""web框架用于处理动态资源请求"""
import time
def index():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# web框架成立后的数据
# 获取当前时间
date_now = time.ctime()
# 返回信息
# 返回的是元组
return status, response_header, date_now
def not_found():
# 状态信息
status = '404 Not Found'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# web框架成立后的数据
date_now = 'Not Found'
# 返回信息
# 返回的是元组
return status, response_header, date_now
def handle_request(env):
# 获取动态资源请求路径
request_path = env['request_path']
print('动态资源请求路径:', request_path)
# 判断请求的动态资源路径,选中指定的函数处理对应的动态资源请求
if request_path == '/index.html':
# 返回处理数据
return index()
else:
# 没有动态资源信息,返回404
return not_found()
模板功能需要实现能够将数据库中查询出来的数据添加到需要返回给浏览器的页面中。
web框架代码:
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/index.html', 'r') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# web框架处理后的数据
# 获取当前时间,模拟从数据库中查询出来的数据
date_now = time.ctime()
response_body = f_data.replace('{%content%}', date_now)
# 返回信息
# 返回的是元组
return status, response_header, response_body
"""web框架用于处理动态资源请求"""
import time
def index():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/index.html', 'r') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# web框架处理后的数据
# 获取当前时间,模拟从数据库中查询出来的数据
date_now = time.ctime()
response_body = f_data.replace('{%content%}', date_now)
# 返回信息
# 返回的是元组
return status, response_header, response_body
def not_found():
# 状态信息
status = '404 Not Found'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# web框架成立后的数据
date_now = 'Not Found'
# 返回信息
# 返回的是元组
return status, response_header, date_now
def handle_request(env):
# 获取动态资源请求路径
request_path = env['request_path']
print('动态资源请求路径:', request_path)
# 判断请求的动态资源路径,选中指定的函数处理对应的动态资源请求
if request_path == '/index.html':
# 返回处理数据
return index()
else:
# 没有动态资源信息,返回404
return not_found()
按照原先的写法,每次请求不同的页面都需要重新写一个if判断,如果不同页面的数量较多,代码书写麻烦。
为了避免这个问题,我们可以使用使用路由。
路由就是请求的URL到处理函数的映射,也就是说提前把请求的URL和处理函数关联好。
路由的管理, 可以使用一个路由列表进行管理,通过路由列表保存每一个路由。
def center():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/center.html', 'r', encoding='utf-8') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# web框架处理后的数据
# 获取当前时间,模拟从数据库中查询出来的数据
date_now = time.ctime()
response_body = f_data.replace('{%content%}', date_now)
# 返回信息
# 返回的是元组
return status, response_header, response_body
route_list = [
('/index.html', index),
('/center.html', center)
]
def handle_request(env):
# 获取动态资源请求路径
request_path = env['request_path']
print('动态资源请求路径:', request_path)
# 判断请求的动态资源路径,选中指定的函数处理对应的动态资源请求
for path, func in route_list:
if request_path == path:
# 找到了对应的函数,调用与请求路径对应的函数
# 同时返回结果
return func()
else:
# 没有动态资源信息,返回404
return not_found()
"""web框架用于处理动态资源请求"""
import time
def index():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/index.html', 'r', encoding='utf-8') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# web框架处理后的数据
# 获取当前时间,模拟从数据库中查询出来的数据
date_now = time.ctime()
response_body = f_data.replace('{%content%}', date_now)
# 返回信息
# 返回的是元组
return status, response_header, response_body
def center():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/center.html', 'r', encoding='utf-8') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# web框架处理后的数据
# 获取当前时间,模拟从数据库中查询出来的数据
date_now = time.ctime()
response_body = f_data.replace('{%content%}', date_now)
# 返回信息
# 返回的是元组
return status, response_header, response_body
def not_found():
# 状态信息
status = '404 Not Found'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# web框架成立后的数据
date_now = 'Not Found'
# 返回信息
# 返回的是元组
return status, response_header, date_now
route_list = [
('/index.html', index),
('/center.html', center)
]
def handle_request(env):
# 获取动态资源请求路径
request_path = env['request_path']
print('动态资源请求路径:', request_path)
# 判断请求的动态资源路径,选中指定的函数处理对应的动态资源请求
for path, func in route_list:
if request_path == path:
# 找到了对应的函数,调用与请求路径对应的函数
# 同时返回结果
return func()
else:
# 没有动态资源信息,返回404
return not_found()
每次添加路由都需要手动添加来完成,接下来我们想要完成路由的自动添加,可以通过装饰器来实现。
在使用装饰器对处理函数进行装饰的时候我们需要知道装饰的函数和那个请求路径进行关联,也就是说装饰器需要接收一个url参数,这样我们定义的装饰器是一个带有参数的装饰器。
# 定义带有参数的装饰器
def route(path):
# 定义装饰器
def decorator(func):
# 将路由添加到路由列表中
route_list.append((path, func))
def inner():
return func()
# 返回装饰后的函数
return inner
# 返回装饰器
return decorator
"""web框架用于处理动态资源请求"""
import time
route_list = []
# 定义带有参数的装饰器
def route(path):
# 定义装饰器
def decorator(func):
# 将路由添加到路由列表中
# 如果在 inner 内添加,那么添加路由只能在调用装饰后的函数时才会添加路由
route_list.append((path, func))
def inner():
# 调用请求路径对应的处理函数
# 并返回结果
return func()
# 返回装饰后的函数
return inner
# 返回装饰器
return decorator
# 装饰函数
@route('/index.html')
def index():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/index.html', 'r', encoding='utf-8') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# web框架处理后的数据
# 获取当前时间,模拟从数据库中查询出来的数据
date_now = time.ctime()
response_body = f_data.replace('{%content%}', date_now)
# 返回信息
# 返回的是元组
return status, response_header, response_body
# 装饰函数
@route('/center.html')
def center():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/center.html', 'r', encoding='utf-8') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# web框架处理后的数据
# 获取当前时间,模拟从数据库中查询出来的数据
date_now = time.ctime()
response_body = f_data.replace('{%content%}', date_now)
# 返回信息
# 返回的是元组
return status, response_header, response_body
def not_found():
# 状态信息
status = '404 Not Found'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# web框架成立后的数据
date_now = 'Not Found'
# 返回信息
# 返回的是元组
return status, response_header, date_now
def handle_request(env):
# 获取动态资源请求路径
request_path = env['request_path']
print('动态资源请求路径:', request_path)
# 判断请求的动态资源路径,选中指定的函数处理对应的动态资源请求
for path, func in route_list:
if request_path == path:
# 找到了对应的函数,调用与请求路径对应的函数
# 同时返回结果
return func()
else:
# 没有动态资源信息,返回404
return not_found()
-- 创建数据库
create database stock_db charset=utf8;
-- 切换数据库
use stock_db;
-- 执行sql文件
source stock_db.sql;
stock_db.sql:
-- MySQL dump 10.13 Distrib 5.7.17, for Linux (x86_64)
--
-- Host: localhost Database: stock_db
-- ------------------------------------------------------
-- Server version 5.7.13-0ubuntu0.16.04.2
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `focus`
--
DROP TABLE IF EXISTS `focus`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `focus` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`note_info` varchar(200) DEFAULT '',
`info_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `info_id` (`info_id`),
CONSTRAINT `focus_ibfk_1` FOREIGN KEY (`info_id`) REFERENCES `info` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `focus`
--
LOCK TABLES `focus` WRITE;
/*!40000 ALTER TABLE `focus` DISABLE KEYS */;
INSERT INTO `focus` VALUES (2,'你确定要买这个?',36),(3,'利好',37),(9,'',88),(10,'',89),(13,'',1);
/*!40000 ALTER TABLE `focus` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `info`
--
DROP TABLE IF EXISTS `info`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `info` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`code` varchar(6) NOT NULL COMMENT '股票代码',
`short` varchar(10) NOT NULL COMMENT '股票简称',
`chg` varchar(10) NOT NULL COMMENT '涨跌幅',
`turnover` varchar(255) NOT NULL COMMENT '换手率',
`price` decimal(10,2) NOT NULL COMMENT '最新价',
`highs` decimal(10,2) NOT NULL COMMENT '前期高点',
`time` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `info`
--
LOCK TABLES `info` WRITE;
/*!40000 ALTER TABLE `info` DISABLE KEYS */;
INSERT INTO `info` VALUES (1,'000007','全新好','10.01%','4.40%',16.05,14.60,'2017-07-18'),(2,'000036','华联控股','10.04%','10.80%',11.29,10.26,'2017-07-20'),(3,'000039','中集集团','1.35%','1.78%',18.07,18.06,'2017-06-28'),(4,'000050','深天马A','4.38%','4.65%',22.86,22.02,'2017-07-19'),(5,'000056','皇庭国际','0.39%','0.65%',12.96,12.91,'2017-07-20'),(6,'000059','华锦股份','3.37%','7.16%',12.26,12.24,'2017-04-11'),(7,'000060','中金岭南','1.34%','3.39%',12.08,11.92,'2017-07-20'),(8,'000426','兴业矿业','0.41%','2.17%',9.71,9.67,'2017-07-20'),(9,'000488','晨鸣纸业','6.30%','5.50%',16.37,15.59,'2017-07-10'),(10,'000528','柳工','1.84%','3.03%',9.42,9.33,'2017-07-19'),(11,'000540','中天金融','0.37%','5.46%',8.11,8.08,'2017-07-20'),(12,'000581','威孚高科','3.49%','3.72%',27.00,26.86,'2017-06-26'),(13,'000627','天茂集团','5.81%','12.51%',10.93,10.33,'2017-07-20'),(14,'000683','远兴能源','6.42%','21.27%',3.48,3.29,'2017-07-19'),(15,'000703','恒逸石化','0.24%','1.65%',16.92,16.88,'2017-07-20'),(16,'000822','山东海化','6.60%','8.54%',9.05,8.75,'2017-07-06'),(17,'000830','鲁西化工','1.38%','4.80%',7.36,7.26,'2017-07-20'),(18,'000878','云南铜业','1.26%','3.23%',14.50,14.47,'2017-07-19'),(19,'000905','厦门港务','5.44%','10.85%',15.90,15.60,'2017-04-20'),(20,'000990','诚志股份','0.53%','1.00%',16.99,16.90,'2017-07-20'),(21,'002019','亿帆医药','1.19%','2.81%',17.05,16.85,'2017-07-20'),(22,'002078','太阳纸业','2.05%','1.90%',8.45,8.29,'2017-07-19'),(23,'002092','中泰化学','7.25%','6.20%',15.53,14.48,'2017-07-20'),(24,'002145','中核钛白','2.43%','7.68%',6.75,6.61,'2017-07-19'),(25,'002285','世联行','8.59%','5.66%',9.23,8.50,'2017-07-20'),(26,'002311','海大集团','1.13%','0.24%',18.81,18.63,'2017-07-19'),(27,'002460','赣锋锂业','9.41%','9.00%',63.70,58.22,'2017-07-20'),(28,'002466','天齐锂业','3.62%','3.66%',68.44,66.05,'2017-07-20'),(29,'002470','金正大','2.30%','0.99%',8.00,7.82,'2017-07-20'),(30,'002496','辉丰股份','3.15%','4.29%',5.24,5.08,'2017-04-10'),(31,'002497','雅化集团','0.38%','12.36%',13.10,13.05,'2017-07-20'),(32,'002500','山西证券','0.44%','3.70%',11.49,11.44,'2017-07-20'),(33,'002636','金安国纪','2.70%','11.59%',19.80,19.42,'2017-07-19'),(34,'300032','金龙机电','0.66%','0.72%',15.28,15.18,'2017-07-20'),(35,'300115','长盈精密','0.60%','0.59%',33.50,33.41,'2017-07-19'),(36,'300268','万福生科','-10.00%','0.27%',31.77,13.57,'2017-04-10'),(37,'300280','南通锻压','3.31%','0.66%',32.20,32.00,'2017-04-11'),(38,'300320','海达股份','0.28%','0.82%',18.26,18.21,'2017-07-20'),(39,'300408','三环集团','1.69%','0.81%',23.42,23.17,'2017-07-19'),(40,'300477','合纵科技','2.84%','5.12%',22.10,22.00,'2017-07-12'),(41,'600020','中原高速','5.46%','4.48%',5.60,5.31,'2017-07-20'),(42,'600033','福建高速','1.01%','1.77%',4.00,3.99,'2017-06-26'),(43,'600066','宇通客车','4.15%','1.49%',23.08,23.05,'2017-06-13'),(44,'600067','冠城大通','0.40%','2.97%',7.56,7.53,'2017-07-20'),(45,'600110','诺德股份','2.08%','4.26%',16.16,15.83,'2017-07-20'),(46,'600133','东湖高新','9.65%','21.74%',13.64,12.44,'2017-07-20'),(47,'600153','建发股份','3.65%','2.03%',13.35,13.21,'2017-07-10'),(48,'600180','瑞茂通','2.20%','1.07%',14.86,14.54,'2017-07-20'),(49,'600183','生益科技','6.94%','4.06%',14.94,14.12,'2017-07-19'),(50,'600188','兖州煤业','1.53%','0.99%',14.56,14.43,'2017-07-19'),(51,'600191','华资实业','10.03%','11.72%',15.80,14.36,'2017-07-20'),(52,'600210','紫江企业','6.03%','10.90%',6.68,6.30,'2017-07-20'),(53,'600212','江泉实业','1.39%','1.78%',10.20,10.15,'2017-07-19'),(54,'600225','*ST松江','4.96%','2.47%',5.71,5.61,'2017-04-13'),(55,'600230','沧州大化','5.74%','13.54%',43.26,40.91,'2017-07-20'),(56,'600231','凌钢股份','2.79%','3.77%',3.68,3.60,'2017-07-19'),(57,'600291','西水股份','10.02%','9.23%',34.71,31.55,'2017-07-20'),(58,'600295','鄂尔多斯','4.96%','12.62%',16.51,15.73,'2017-07-20'),(59,'600303','曙光股份','8.37%','14.53%',11.53,10.64,'2017-07-20'),(60,'600308','华泰股份','1.12%','2.66%',6.30,6.26,'2017-07-19'),(61,'600309','万华化学','0.03%','1.78%',31.81,31.80,'2017-07-20'),(62,'600352','浙江龙盛','0.39%','1.85%',10.32,10.28,'2017-07-20'),(63,'600354','敦煌种业','7.89%','18.74%',9.44,8.75,'2017-07-20'),(64,'600408','安泰集团','1.98%','3.38%',4.13,4.12,'2017-04-13'),(65,'600409','三友化工','0.62%','3.78%',11.36,11.29,'2017-07-20'),(66,'600499','科达洁能','0.46%','3.94%',8.84,8.80,'2017-07-20'),(67,'600508','上海能源','3.26%','2.99%',13.32,13.01,'2017-07-19'),(68,'600563','法拉电子','0.32%','1.36%',53.67,53.50,'2017-07-20'),(69,'600567','山鹰纸业','0.76%','2.85%',3.98,3.96,'2017-07-19'),(70,'600585','海螺水泥','0.45%','0.61%',24.51,24.44,'2017-07-19'),(71,'600668','尖峰集团','4.35%','6.43%',18.70,18.36,'2017-04-13'),(72,'600688','上海石化','2.72%','0.91%',6.80,6.74,'2017-06-01'),(73,'600729','重庆百货','5.70%','3.34%',27.45,27.13,'2017-06-29'),(74,'600739','辽宁成大','3.30%','3.50%',19.74,19.11,'2017-07-20'),(75,'600779','水井坊','3.85%','2.77%',29.39,28.30,'2017-07-20'),(76,'600781','辅仁药业','8.61%','4.16%',23.46,21.89,'2017-05-02'),(77,'600801','华新水泥','4.00%','10.15%',12.99,12.49,'2017-07-20'),(78,'600846','同济科技','2.06%','17.41%',9.39,9.26,'2017-04-13'),(79,'600884','杉杉股份','1.08%','3.53%',20.67,20.45,'2017-07-20'),(80,'600966','博汇纸业','2.89%','5.54%',6.41,6.28,'2017-07-19'),(81,'600971','恒源煤电','2.36%','8.81%',12.16,11.88,'2017-07-20'),(82,'601012','隆基股份','0.76%','1.30%',19.93,19.78,'2017-07-20'),(83,'601100','恒立液压','4.78%','0.92%',19.31,18.97,'2017-07-13'),(84,'601101','昊华能源','4.03%','6.06%',11.10,10.80,'2017-07-19'),(85,'601216','君正集团','2.16%','2.26%',5.20,5.10,'2017-04-17'),(86,'601666','平煤股份','2.81%','6.14%',6.96,6.77,'2017-07-20'),(87,'601668','中国建筑','2.39%','1.42%',10.70,10.45,'2017-07-20'),(88,'601678','滨化股份','0.13%','2.47%',7.92,7.91,'2017-07-20'),(89,'601918','新集能源','1.23%','3.11%',4.93,4.92,'2017-07-19'),(90,'603167','渤海轮渡','2.77%','3.34%',11.87,11.61,'2017-04-13'),(91,'603369','今世缘','3.34%','2.13%',14.24,13.78,'2017-07-20'),(92,'603589','口子窖','3.99%','1.84%',39.37,39.04,'2017-06-26'),(93,'603799','华友钴业','2.38%','7.19%',67.46,65.89,'2017-07-20'),(94,'603993','洛阳钼业','2.94%','2.50%',7.36,7.16,'2017-07-19');
/*!40000 ALTER TABLE `info` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2017-08-18 20:53:58
# 装饰函数
@route('/index.html')
def index():
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1')]
# 1. 打开指定模板文件,读取模板文件中的数据
with open('template/index.html', 'r', encoding='utf-8') as f:
f_data = f.read()
# 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
# 创建与数据库的连接对象
conn = pymysql.connect(
host='localhost',
port=3306,
user='root',
password='285013',
database='stock_db',
charset='utf8'
)
# 获取执行sql的游标对象
cur = conn.cursor()
# sql
sql = 'select * from info;'
# 执行sql
cur.execute(sql)
# 获取全部结果
result = cur.fetchall()
# 关闭对象
cur.close()
conn.close()
# print(result)
# web框架处理后的数据
data = ""
for row in result:
data += """
%s
%s
%s
%s
%s
%s
%s
%s
""" % row
response_body = f_data.replace('{%content%}', data)
# 返回信息
# 返回的是元组
return status, response_header, response_body
个人中心数据接口的开发,需要根据sql语句查询个人中心数据,然后将个人中心数据转成json字符串并返回。
python 数据类型与 json 类型的转化表:
来源:python中的元组与json类型转化问题
可以json.dumps
函数把字典转成json字符串。
json.dumps
用于将 Python 对象 编码成 JSON 字符串。import json
响应头添加Content-Type表示指定数据的编码格式,防止数据到浏览器乱码。
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [
('Server', 'PWS/1.1'),
# 设置数据的编码格式为 utf-8
('Content-Type', 'text/html;charset=utf-8')
]
# 个人中心数据接口
# 绑定请求资源路径
@route('/center_data.html')
def center_data():
# 从数据库中查询数据
# 创建与数据库的连接对象
conn = pymysql.connect(
host='localhost',
port=3306,
user='root',
password='285013',
database='stock_db',
charset='utf8'
)
# 获取执行sql的游标对象
cur = conn.cursor()
# sql
sql = '''select i.code, i.short, i.chg,
i.turnover, i.price, i.highs, f.note_info
from info as i inner join focus as f on i.id = f.info_id;'''
# 执行sql
cur.execute(sql)
# 获取全部结果
result = cur.fetchall()
# print(result)
# 将结果由元组转成列表(元组转成json字符串会报错),元组内的每个小元组转成字典
# 遍历元组中的每个元素,每个元素(每个小元组)取出值放到字典中
# 最后字典组成列表
center_data_list = [
{
'code': row[0],
'short': row[1],
'chg': row[2],
'turnover': row[3],
# Decimal不能转json,会报错
'price': str(row[4]),
'highs': str(row[5]),
'note_info': row[6],
} for row in result
]
# print(center_data_list)
# 列表转成json字符串
# ensure_ascii=False表示可以在控制台显示中文。
# indent:参数根据数据格式缩进显示,读起来更加清晰。
json_str = json.dumps(center_data_list, ensure_ascii=False, indent=True)
# print(json_str)
# 状态信息
status = '200 OK'
# 响应头信息
# 这里使用列表套元组的方法存放信息,每个元组为一个信息
response_header = [('Server', 'PWS/1.1'), ('Content-Type', 'text/html;charset=utf-8')]
# 关闭对象
cur.close()
conn.close()
# 向浏览器返回数据
return status, response_header, json_str
在现实生活中,记录日志非常重要,比如:银行转账时会有转账记录;飞机飞行过程中,会有个黑盒子(飞行数据记录器)记录着飞机的飞行过程,那在python程序中想要记录程序在运行时所产生的日志信息,可以使用 logging 这个包来完成。
日志等级可以分为5个,从低到高分别是:
默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL
在 logging 包中记录日志的方式有两种:
import logging
logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')
由于默认的是WARNING等级,在WARNING或WARNING之上等级的才记录日志信息,所以WARNING以下的两个等级DEBUG、INFO都不显示。
设置logging的配置信息,使用:
logging.basicConfig()
改变logging日志等级:
# 导入包
import logging
# 设置logging日志等级为DEBUG
logging.basicConfig(level=logging.DEBUG)
logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')
设置logging的输出格式:
# 导入包
import logging
# level 表示设置的日志等级
# format 表示日志的输出格式
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')
日志信息保存到日志文件,使用:
logging.basicConfig()
# 导入包
import logging
# level 表示设置的日志等级
# format 表示日志的输出格式
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
filename='log.txt',
filemode='a',
encoding='utf-8'
)
logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')
logging日志配置信息在程序入口模块设置一次,整个程序都可以生效。
import socket
import threading
import sys
import framework
import logging
# logging日志的配置
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
filename="log.txt",
filemode="w",
encoding='utf-8'
)
if request_path.endswith('.html'):
logging.info('动态资源请求:'+ request_path)
...
else:
# 静态资源请求
logging.info('静态资源请求:' + request_path)
...