用python写一个服务器并且实现数据库和网页交互

  • 本文简介
    • 1.需要实现的功能逻辑
    • 2.设计想法
    • 3.数据库设计
    • 4.代码实现:
      • 服务器类代码
      • 应用程序框架代码
      • 商城类代码(实现数据库交互)
    • 5.运行

本文简介

开发环境是Ubuntu系统下python3.5,数据库为mysql,与数据库交互的包为pymysql。

1.需要实现的功能逻辑

2.设计想法

先写一个动态资源服务器,详情见
动态资源服务器初级
然后更新应用程序框架用装饰器实现(模拟FLASK框架),再设计一个类shsp,类功能实现与数据库交互并返回想要的结果
之后是代码实现。

3.数据库设计


数据库设计完然后写入要展示的商品数据,这里不展示。已打包上传

4.代码实现:

中间的网页内容具体不显示,具体已打包上传

服务器类代码

import gevent
from gevent import monkey
import sys
import socket
import chardet'''这个程序实现的是一个动态资源服务器'''
gevent.monkey.patch_all()  # 给所有耗时操作打补丁class Server(object):def __init__(self, port, app):'''初始化,实现逻辑'''self.app = app# 创建服务器的套接字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(('', port))self.response_data = None  # 这个是响应报文的数据(除了响应体之外)def start(self):# 服务器开始运行self.server_socket.listen()while True:handler_socket, address = self.server_socket.accept()# handler_prc = threading.Thread(#     target=self.handler_client, args=(handler_socket,))# handler_prc.start()gv = gevent.spawn(self.handler_client, handler_socket)gv.join()def start_response(self, status, header_list):'''这个函数实现的是拼接响应报文,遵循的还是响应报文的格式,请求动态资源调用:param status: 状态码,例如'200 ok':param header_list: 列表 例如[(server,wsgisever),(name,xiaoming)]:return:HTTP/1.1 状态码 说明\r\nHeadername1:header_value\r\nHeadername2:header_value\r\nHeadername3:header_value\r\n\r\nResponse_body 响应体,就是数据,网页、图片、文本'''response_header_firstline = 'HTTP/1.1 %s\r\n' % statusresponse_header = ''# 这是键值 直接拆包for header_key, header_value in header_list:response_header += ('%s:%s\r\n' % (header_key, header_value))self.response_data = response_header_firstline + response_header + '\r\n'def handler_client(self, handler_socket):# 人工客服处理客户端的请求data = handler_socket.recv(1024).decode('utf-8')  # 字节流文件--字符串request_datas_list = data.split('\r\n')for obj in request_datas_list:print(obj)# todo:开始拼接的响应报文# todo:截取请求的路径,获取请求的内容,根据路径不同返回不同数据first_line = request_datas_list[0]# ret = re.search('GET (/.*?) HTTP/1.1', first_line) #这个是正则# 获取第一行,然后路径firstline_list = first_line.split(' ')# 判断是否有ret值,如果没有,说明是一个非法请求if not firstline_list:handler_socket.close()return# 尝试没有就给他一个默认值try:request_path = firstline_list[1]except BaseException:request_path = '/'# request_path = ret.group(1)print('**********************')'''HTTP/1.1 状态码 说明\r\nHeadername1:header_value\r\nHeadername2:header_value\r\nHeadername3:header_value\r\n\r\nResponse_body 响应体,就是数据,网页、图片、文本'''environ = {'path': request_path}if request_path.endswith('.py'):# 交给动态资源处理框架取处理print('此时进入动态资源处理')user_info = request_datas_list[len(request_datas_list)-1]responsebody = self.app(environ, self.start_response, user_info)data = self.response_data + responsebodyhandler_socket.send(data.encode('UTF-8'))handler_socket.close()else:# 这里交给静态资源处理if request_path == '/':status_code = '200 ok'responsebody = 'hello world!'# 暂时不写静态
#             elif request_path == '/index.html':
#                 status_code = '200 ok'
#                 with open('index.html') as f:
#                     response_data = f.read()
#                 responsebody = response_data
#             elif request_path == '/login.html':
#                 status_code = '200 ok'
#                 with open('login.html') as f:
#                     responsebody = f.read()%('这是登录页面', '''<form method="post" action="signin.py">
#     <input type="submit" value="跳转注册">
# </form>''')else:status_code = '404 not found'with open('err.html') as f:responsebody = f.read()responseHeader = 'HTTP/1.1' + status_code + ' 123\r\n'responseHeader += '\r\n'# 判断responsebody是否是二进制文件# 需要知道发送数据大小,已经发送了多少try:chardet.detect(responsebody)sent_count = 0  # 已经发送数据大小reponse = responseHeader.encode('utf-8') + responsebodywhile sent_count < len(reponse):# send_size = int(sent_count + len(reponse) / 2)  # 一次发送数据大小加上当前位置# 现在发送的数据大小now_size = handler_socket.send(reponse[sent_count:])sent_count += now_size# handler_socket.send(#     responseHeader.encode('utf-8') + responsebody)except BaseException:reponse = responseHeader + responsebodysent_count = 0  # 已经发送数据大小while sent_count < len(reponse):# send_size = int(sent_count + len(reponse) / 2)  # 一次发送数据大小加上当前位置# 现在发送的数据大小now_size = handler_socket.send(reponse[sent_count:].encode('utf-8'))  # send有可能发送不完sent_count += now_size# ret = handler_socket.send(reponse.encode('utf-8'))# print(ret)finally:# 最后关闭handler_socket.close()def main():'''实现主要逻辑'''try:port = sys.argv[1]frame_name = sys.argv[2]  # 用来表示哪个框架的哪个接口except:# 获取不到参数则给定默认参数print('参数缺少,使用默认参数端口为5000')port = 5000frame_name = 'application:app'returnfinally:# 获取接口名字切分,获取的是一个列表frame_list = frame_name.split(':')modle_name = frame_list[0]func_name = frame_list[1]# 这是新的一种导包方式# app是指接口的导用# __import__的参数是字符串,文件名frame_obj = __import__(modle_name)app = getattr(frame_obj, func_name)# 初始化我的服务器my_server = Server(port=int(port), app=app)gv = gevent.spawn(my_server.start())  # 这个添加函数gv.join()if __name__ == '__main__':main()

应用程序框架代码

from shopping import ShangHui'''这是装饰器工厂函数来实现 类似于flask'''
# 定义一个路径字典
urlfuncdict = {}
# shsp尚汇优品对象
shsp = None
# 用户信息包括名字和密码
userinfo = None
# 验证是否登录
is_login = False
# 登陆后的账户名
user_name = ''
# 订单的货物信息
order_info = Nonedef route(url):# 装饰器工厂函数def wrapper(func):# 添加键值对,key是路径,value是函数的引用urlfuncdict[url] = funcdef inner():response_body = func()return response_bodyreturn innerreturn wrapper@route('/signin.py')
def signin():with open('signin.html') as f:return f.read()@route('/handler.py')
def handler():# 用来处理登录,返回的是布尔值if shsp.login(user_info=userinfo):html = '''<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h3>登录成功,按确认按钮跳转主页</h3><br>
<form method="post" action="index.py"><input type="submit" value="确认">
</form>
</body>
</html>'''global is_login, user_nameuser_name = shsp.login_nameis_login = Trueprint('登陆后保存的:',user_name,is_login)else:html = '''<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h3>账号密码错误,请重新登录</h3><br><form method="post" action="login.py"><input type="submit" value="重新登录"></form></body></html>'''return html@route('/change_psd.py')
def change_psd():# 用来修改密码with open('change_psd.html') as f:return f.read()@route('/login.py')
def login():with open('login.html') as f:html_str = f.read()# 判断是否注册后进入这个界面if 'reg' in userinfo:# 这是注册方法,返回布尔值判断注册成功与否print('注册账号', userinfo)if shsp.signin(user_info=userinfo):html = html_str % ('注册成功请输入账号密码登录', '')else:html = html_str % ('注册失败,名字已存在请重试', '''<form method="post" action="signin.py"><input type="submit" value="跳转注册"></form>''')elif 'old' in userinfo:# 这是修改密码方法,返回布尔值print('修改密码', userinfo)if shsp.change_password(user_info=userinfo):html = html_str % ('修改密码成功请重新登录', '')else:html = html_str % ('修改密码失败,账号或密码是输入错误', '''<form method="post" action="change_psd.py"><input type="submit" value="再次修改密码">
</form>''')else:html = html_str % ('这是登录页面', '''<form method="post" action="signin.py"><input type="submit" value="跳转注册">
</form>''')return html@route('/exit.py')
def exit():# 用来关闭所有东西global is_loginis_login = Falseshsp.close()html = '''<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>你已经退出商城,欢迎再来!</h1>
</body>
</html>'''return html@route('/index.py')
def index():# 用来显示网页内容'''<form method="post" action="login.py"><input type="submit" value="跳转登录">
</form>'''with open('index.html') as f:str_html = f.read()print('验证是否登录:', is_login)if is_login:# 登录状态则添加两个按钮,退出和更改密码html = str_html % (shsp.get_data_str(), '''<form method="post" action="exit.py"><input type="submit" value="退出登录">
</form>''', '''<form method="post" action="change_psd.py"><input type="submit" value="修改密码">
</form>''')else:html = str_html % (shsp.get_data_str(), '''<form method="post" action="login.py"><input type="submit" value="跳转登录">
</form>''', '')return html@route('/order.py')
def order_comm():# 这是实现下订单if is_login:if shsp.order_comm(order_info=userinfo):global order_infoorder_info = userinfowith open('order_comm.html') as f:str_html = f.read()html = str_htmlelse:html = '''<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>下单失败,检查输入的商品名字或数量是否出错</h1><form method="post" action="index.py"><input type="submit" value="跳转主页面"></form></body></html>'''else :html = '''<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>账号没登录,请登录</h1>
<form method="post" action="index.py"><input type="submit" value="跳转主页面">
</form>
</body>
</html>'''return html@route('/confirm_order.py')
def confirm_order():shsp.confirm_order(add_info=userinfo, order_info=order_info, sys_name=user_name)html = '''<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>下单成功,确认返回主页面</h1><form method="post" action="index.py"><input type="submit" value="跳转主页面"></form></body></html>'''return html@route('/check_order.py')
def check_order():# 展示订单# if is_login:## else:#     print('没登录,请登录')'''<form method="post" action="check_order.py"><input type="submit" value="查看订单">
</form>'''pass@route('/error.py')
def error():return '404 not found'def app(environ, start_response, user_info):''':param environ: 字典,传入的是请求头:param start_response: 这是方法:return: 返回网页内容'''request_path = environ['path']print('app方法:', user_info)# 根据上面字典判断staus_code = ''global shsp, userinfoshsp = ShangHui()userinfo = user_info# 判断方法try:func = urlfuncdict[request_path]staus_code = '200 ok'except BaseException:staus_code = '404 NOT FOUND'func = errorfinally:start_response(staus_code, [('name', 'paul'), ('age', '18')])return func()

商城类代码(实现数据库交互)

import pymysql
import time
'''这个程序实现数据库交互'''class ShangHui(object):# _instance = None## def __new__(cls, *args, **kwargs):#     if not cls._instance:#         cls._instance = super(ShangHui, cls).__new__(cls, *args, **kwargs)#         return cls._instancedef __init__(self):'''初始化数据库'''self.connect_obj = pymysql.connect(host='127.0.0.1',port=3306,database='commodity',user='root',password='mysql',charset='utf8')self.cur = self.connect_obj.cursor()  # 操作self.login_name = ''  # 登录之后的用户名self.login_flag = 0  # 登陆验证def close(self):self.connect_obj.close()self.cur.close()print('再见')def login(self, user_info):'''这个是登录操作,返回一个布尔值'''#  username=zzz&password=123 取出名字和密码try:split_info = user_info.split('&')inname = split_info[0][9:]inpassword = split_info[1][9:]except :print('数据没有传输过来')return False# 不要用拼接sql字符串sql = 'SELECT * FROM user WHERE name=%s and password=%s'try:# 参数列表化ret = self.cur.execute(sql, [inname, inpassword])except Exception as e:ret = '数据库出错'print(e)if ret == 0:print('账号或密码出错')return Falseprint('#########登录成功###########')self.login_name = innameself.login_flag = 1return Truedef get_data_str(self):print('商品展示')try:sql = '''SELECT commodity.name AS'商品名',commodity.price AS'价格',cate.name AS'商品类别',brand.name AS'商品品牌' FROM commodity INNER JOIN cate ON commodity.cate_id=cate.id INNER JOIN brand ON commodity.brand_id=brand.id'''ret = self.cur.execute(sql)if ret:data_str = '商品名  价格  商品类别  品牌<br>'for i in self.cur.fetchall():for j in i:str1 = str(j)data_str += str1 + '  'data_str += '<br>'print(data_str)return data_strexcept Exception as e:print(e)def get_order_id(self):# 这是读写文件,目的是为了保存并读取订单id,每次自增1file_data = ''with open('order.txt', 'r') as f:for line in f:old_str = linenew_str = str(int(old_str) + 1)file_data = new_strwith open('order.txt', 'w') as f:f.write(file_data)# 最后异步读取with open('order.txt', 'r') as f:for line in f:file_data = linereturn file_datadef order_comm(self, order_info):'''下订单,判断可不可以'''#  name=zzz&num=1 取出名字和密码try:split_info = order_info.split('&')order_name = split_info[0][5:]  # 这里编码问题except:print('数据没有传输')return Falseprint('商品下单', order_name)try:sql = 'SELECT price FROM commodity WHERE name=%s'ret = self.cur.execute(sql, [order_name, ])if ret:print('下单成功,下面输入配送信息')return Trueelse:print('输入错误,返回主界面')return Falseexcept Exception as e:print(e)def confirm_order(self, add_info, order_info, sys_name):# 订单确认,然后写入订单表,订单详情表,配送表,#  name=zzz&phone=11132112&address=sdasda 取出名字和密码try:# 切割用户信息split_info = add_info.split('&')user_name = split_info[0][5:]user_phone = split_info[1][6:]user_addr = split_info[2][8:]except:print('数据没有传输')return# 切割订单split_order = order_info.split('&')print('############')print(split_order)order_name = split_order[0][5:]  # 商品名字order_num = int(split_order[1][4:])# 挑选并确认价格sql = 'SELECT price FROM commodity WHERE name=%s'self.cur.execute(sql, [order_name, ])pay_price = self.cur.fetchall()[0][0]payment = order_num * pay_price  # 总价# 获得用户idtry:user_sql = 'SELECT id FROM user WHERE name=%s'self.cur.execute(user_sql, [sys_name, ])user_id = self.cur.fetchone()[0]# 获得商品idcomm_sql = 'SELECT id FROM commodity WHERE name=%s'self.cur.execute(comm_sql, [order_name])comm_id = self.cur.fetchone()[0]print('写入订单表')# 写入订单表 id,payment,pay_time,user_id,send_timeorder_sql = 'INSERT INTO myorder VALUES(0, %s, %s, %s, %s)'self.cur.execute(order_sql, [payment, time.ctime(), user_id, '一天后'])# 获得订单id 用文件存储方法order_id = int(self.get_order_id())print('写入订单详情表')# 写入订单详情表 id,comm_id,order_id,num,price,total_feeitems_sql = 'INSERT INTO order_items VALUES(0, %s, %s, %s, %s, %s)'self.cur.execute(items_sql, [comm_id, order_id, order_num, pay_price, payment])# 写入配送表 id,order_id,user_id,name,phone,addressprint('写入配送表')ship_sql = 'INSERT INTO shipping VALUES(0, %s, %s, %s, %s, %s)'self.cur.execute(ship_sql, [order_id, user_id, user_name, user_phone, user_addr])self.connect_obj.commit()  # 写入except Exception as e:print(e)def signin(self, user_info):'''注册注意数据库保存'''#  reg_username=zzz&password=123 取出名字和密码split_info = user_info.split('&')inname = split_info[0][13:]inpassword = split_info[1][9:]print('注册账号')try:sql = 'SELECT * FROM user WHERE name=%s'ret = self.cur.execute(sql, [inname, ])if ret:print('用户名已经存在')return Falsesql_insert = 'INSERT INTO user VALUES(0, %s, %s)'ret1 = self.cur.execute(sql_insert, [inname, inpassword])if ret1:print('注册成功')self.connect_obj.commit()  # 提交return Trueexcept Exception as e:print(e)def change_password(self, user_info):'''注册注意数据库保存'''#  username=zzz&old_psd=123&new_psd=333 取出名字和密码split_info = user_info.split('&')user_name = split_info[0][9:]old_password = split_info[1][8:]print('修改密码')new_password = split_info[2][8:]try:sql = 'SELECT * FROM user WHERE name=%s and password=%s'ret = self.cur.execute(sql, [user_name, old_password])if ret == 0:print('旧密码输入错误')return Falseelse:sql_update = 'UPDATE user SET password=%s WHERE name=%s'ret1 = self.cur.execute(sql_update, [new_password, user_name])if ret1:print('修改成功')self.connect_obj.commit()  # 提交return Trueexcept Exception as e:print(e)def get_check_order(self):print('订单展示')try:sql = '''SELECT myorder.id,myorder.,cate.name AS'商品类别',brand.name AS'商品品牌' FROM commodity INNER JOIN cate ON commodity.cate_id=cate.id INNER JOIN brand ON commodity.brand_id=brand.id'''ret = self.cur.execute(sql)if ret:data_str = '商品名  价格  商品类别  品牌<br>'for i in self.cur.fetchall():for j in i:str1 = str(j)data_str += str1 + '  'data_str += '<br>'print(data_str)return data_strexcept Exception as e:print(e)

5.运行

这里使用了sys模块,使用终端启动py文件并且可以传输数据,先进入当前文件夹,然后启动,后面是端口和调用的框架及方法

相关代码截取出来:

启动服务器后,打开localhost或者本地回环地址127.0.0.1即可,这里自定义的逻辑是后缀为py


以上功能已经都实现了,自己手动下了个订单然后查看数据库,订单数据库有三个,用一下三表联查:

用python写一个商城网页服务器并且实现数据库和网页交互相关推荐

  1. python写一个通讯录step by step V3.0

    python写一个通讯录step by step V3.0 更新功能: 数据库进行数据存入和读取操作 字典配合函数调用实现switch功能 其他:函数.字典.模块调用 注意问题: 1.更优美的格式化输 ...

  2. python俄罗斯方块算法详解_用 Python 写一个俄罗斯方块游戏 (

    @@ -2,34 +2,34 @@ > * 原文作者:[Dr Pommes](https://medium.com/@pommes) > * 译文出自:[掘金翻译计划](https://g ...

  3. python编写测试工具-python 写一个性能测试工具(一)

    国庆重新学习了一下go的gin高性能测试框架. 用JMeter来测试gin与flask接口的性能,差别很大. 为什么我自己不尝试写一个性能工具,性能工具的核心就是 并发 和 请求. 请求可以选择Pyt ...

  4. pythongui登录界面密码显示_用python写一个带有gui界面的密码生成器

    需要用到的库: tkinter:构建gui界面 pyperclip:复制功能 random:生成随机数 string:处理字符串 代码: from tkinter import * import ra ...

  5. python写一个通讯录V2.0

    python写一个通讯录step by step V2.0 引用知识 list + dict用于临时存储用户数据信息 cPickle用于格式化文件存取 依旧使用file来进行文件的存储 解决问题 1. ...

  6. python软件界面-用Python写一个语音播放软件

    原标题:用Python写一个语音播放软件 单位经常使用广播进行临时事项的通知(将文字转换为语音然后通过功放广播),但是市面上多数语音播放软件都是收费的,要么发音失真,要么不够稳定--经常出现莫名其妙的 ...

  7. python写一个系统-使用Python写一个量化股票提醒系统

    大家在没有阅读本文之前先看下python的基本概念, Python是一种解释型.面向对象.动态数据类型的高级程序设计语言. Python由Guido van Rossum于1989年底发明,第一个公开 ...

  8. python写一个系统-熬了一晚上,小白用Python写了一个股票提醒系统

    码农小马七夕节去相亲了,见了一个不错的姑娘,长的非常甜美!聊着聊着很投缘!通过介绍人了解到,对方也很满意--想着自己单身多年的生活就要结束啦,心里满是欢喜,美美哒!但是突然想起年初还有几万块在股市里面 ...

  9. python写一个表白程序-用Python写一个表白神器让你脱单

    原标题:用Python写一个表白神器让你脱单 来自公众号:Python编程时光 今天是什么节日,就不用小明说了吧? 有女朋友的,该准备的礼物买了吗? 没有对象的,表白的套路学会了吗? 还没有?好吧,你 ...

最新文章

  1. Python怎么利用多核cpu
  2. 解决firefox ubuntu无法打开页面的问题
  3. 线上Tomcat支持Eclipse远程调试的方法
  4. 【TF-IDF】传统方法TF-IDF解决短文本相似度问题
  5. 【Libevent】Libevent学习笔记(一):简介和安装
  6. python tkinter详解 博客园_python tkinter-布局
  7. opencv空间色彩转换
  8. 不要束缚:为什么我们会错过GitHub条纹
  9. tinyxml 内存泄露_tinyxml优化之一
  10. 令牌桶算法和漏桶算法python_限流之漏桶算法与令牌桶算法
  11. 接口implements
  12. Qt开源作品40-图片及文字与base64编码互换
  13. 单例模式singleton
  14. 红色小吃加盟网站源码 织梦dede模板[带手机版数据同步]
  15. 堕落了!经典软件下载网站被查
  16. wps表格转换HTML且能修改,怎么把WPS表格文件转换为WPS文字文件?
  17. Win10 无法加载操作系统,关键系统驱动程序丢失或错误 蓝屏错误代码0xc000007b
  18. JAVA进阶之路-CountDownLatch源码走读
  19. STM32H743I-EVAL2_RTC_Tamper
  20. 群晖笔记一:使用Hyper Backup在多个硬盘间备份重要资料

热门文章

  1. html 字号代码,html文字代码
  2. 4412学习日记 - DDR3初始化
  3. Java-商品录入系统
  4. 9个免费图标下载网站
  5. 鉴别打印机真假墨盒墨水小妙招,收好勿谢!
  6. 数据透视表如何移动位置?
  7. 怎么删除feed php,php-如何删除html特殊字符?
  8. Intellij IDEA同步代码至远程FTP服务器
  9. Web3与非洲的密切关系
  10. BDM的驱动安装(继续~~~)