# Python_final_project
**Repository Path**: chen_ke_715/python_final_project
## Basic Information
- **Project Name**: Python_final_project
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-01-22
- **Last Updated**: 2021-01-24
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## Python期末项目之2020年考研调剂信息数据报告
[pythonanywhere部署地址](http://colleenproject.eu.pythonanywhere.com/)

## 问题及解决方案表述
- 加/价值主张宣言
Python项目中的数据集是针对国内考研网爬取调剂大学的专业等信息,可以分析对比调剂最多的专业获学校信息,且能够通过可视化直观地对比专业与学校,获取学校往年接受的专业和名额、调剂考生的背景情况等关键信息。
[数据集来源](https://www.kesci.com/mw/project/60066af97ed5ab0015f0a68b)
- 问题:许多考研党或者准备考研的同学对调剂学校信息关注度高,但是缺乏信息过于庞杂,信息不对称,缺乏统一且便捷的信息输入。
- 解决方案:
出发点:通过python从国内考研调剂信息中进行爬取,且依据[中国考研网]( http://www.chinakaoyan.com/)权威网站获取的数据,整合处理后将其进行数据可视化后呈现在web页面,这样可以让他们更加直观地对比专业与学校,分析调剂大学的专业信息,为其选择并进入合适且更好的读研平台提供一定的参考性。
技术点:利用pycharm将数据集进行筛选、转换后进行数据可视化,然后通过python flask框架将数据呈现在web,方便考研党/准备考研的同学们参考。最后,通过pythonanywher部署web项目,用户即可直接通过网址查看数据报告。
## 编程功能基本描述
### 数据集处理-文本处理
```
#导入模块
import pandas as pd
import numpy as np
df_info = pd.read_csv('大学信息_整理后.csv',encoding='utf-8') #读取整理后的csv文件
df_info.head()
def transform_attr(x):
#转换学校属性
if '211' in x and '985' not in x:
return 211
elif '985' in x:
return '985'
else:
return '双非'
def transform_type(x):
#转换学校类型
if '理工类' in x or '理工类院校' in x or '理工科' in x or '理工、教学研究型大学' in x or '理工类\n[4]' in x or '理工\n[6]' in x:
return '理工'
elif '综合类' in x or '综合性大学\n[3]' in x or '综合类(应用型大学)' in x or '综合、研究教学型大学' in x or '综合类大学' in x or '综合师范类' in x:
return '综合'
elif '师范类院校' in x or '师范类' in x or '师范类(综合类)' in x or '师范(综合)' in x or '地方师范院校' in x:
return '师范'
elif '农林类' in x or '农业类' in x:
return '农林'
elif '医药类' in x:
return '医药'
elif '民族类' in x:
return '民族'
elif '未知' in x or '国有企业' in x or '科技型企业' in x or '公立大学' in x:
return '其他'
else:
return x
# 筛选字段
df_info= df_info[['school','province','school_level','school_types']]
# 处理省份数据
df_info.loc[(df_info.school=='北京工商大学')&(df_info.province=='未知'), 'province']= '北京'
df_info.loc[(df_info.school=='哈尔滨工程大学')&(df_info.province=='未知'), 'province']= '哈尔滨'
df_info.loc[(df_info.school=='江苏大学')&(df_info.province=='未知'), 'province']= '江苏'
df_info.loc[(df_info.school=='青岛大学')&(df_info.province=='未知'), 'province']= '山东'
df_info.loc[(df_info.school=='北京石油化工学院')&(df_info.province=='未知'), 'province']= '北京'
df_info.loc[(df_info.school=='齐鲁工业大学')&(df_info.province=='未知'), 'province']= '山东'
df_info.loc[(df_info.school=='江苏科技大学')&(df_info.province=='未知'), 'province']= '江苏'
df_info.loc[(df_info.school=='浙江农林大学')&(df_info.province=='未知'), 'province']= '浙江'
df_info.loc[(df_info.school=='燕山大学')&(df_info.province=='未知'), 'province']= '河北'
df_info.loc[(df_info.school=='福州大学')&(df_info.province=='未知'), 'province']= '福建'
df_info.loc[(df_info.school=='内蒙古科技大学')&(df_info.province=='未知'), 'province']= '内蒙古'
#删除重复数据
df_info = df_info.drop_duplicates()
df_info.head()
# 查看缺失数据
df_all.isnull().sum()
```
- 数据可视化
```
import pyecharts
from pyecharts.charts import Line
from pyecharts import options as opts
from pyecharts.charts import Pie
from pyecharts.globals import ThemeType
from pyecharts.charts import Bar
from pyecharts.charts import Map
#调剂信息发布时间走势图
line1 = Line(init_opts=opts.InitOpts(width='1280px',height='600px'))
line1.add_xaxis(pub_time.index.tolist())
line1.add_yaxis('发布热度',pub_time.values.tolist(),
areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
label_opts=opts.LabelOpts(is_show=True))
line1.set_global_opts(title_opts=opts.TitleOpts(title='调剂信息发布时间走势图'),
toolbox_opts=opts.ToolboxOpts(),
visualmap_opts=opts.VisualMapOpts())
line1.render_notebook()
#学校层次
level_perc = df_all.school_level.value_counts() / df_all.school_level.value_counts().sum()
display(level_perc)
level_perc = np.round(level_perc * 100 ,2)
level_perc
#绘制饼图
pie1 = Pie(init_opts=opts.InitOpts(width='500px',height='600px'))
pie1.add("",
[*zip(level_perc.index, level_perc.values)],
radius=["40%","75%"])
pie1.set_global_opts(title_opts=opts.TitleOpts(title='学校层次分布'),
legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
toolbox_opts=opts.ToolboxOpts())
pie1.set_series_opts(label_opts=opts.LabelOpts(formatter="{c}%"))
pie1.render_notebook()
#学校类型
type_perc = df_all.school_types.value_counts() / df_all.school_types.value_counts().sum()
type_perc = np.round(type_perc*100,2)
pie2 = Pie(init_opts=opts.InitOpts(theme=ThemeType.WONDERLAND, width='1280px', height='650px'))
pie2.add("",
[*zip(type_perc.index, type_perc.values)],
radius=["40%","75%"])
pie2.set_global_opts(title_opts=opts.TitleOpts(title='学校类型分布'),
legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
toolbox_opts=opts.ToolboxOpts())
pie2.set_series_opts(label_opts=opts.LabelOpts(formatter="{c}%"))
pie2.render_notebook()
# 条形图
bar1 = Bar(init_opts=opts.InitOpts(width='1280px', height='1000px'))
bar1.add_xaxis(province_num.index.tolist())
bar1.add_yaxis("省份", province_num.values.tolist())
bar1.set_global_opts(title_opts=opts.TitleOpts(title="调剂信息发布数省份分布"),
toolbox_opts=opts.ToolboxOpts(),
visualmap_opts=opts.VisualMapOpts(max_=110))
bar1.set_series_opts(label_opts=opts.LabelOpts(position='right')) # 标签
bar1.reversal_axis()
bar1.render_notebook()
c = Map(init_opts=opts.InitOpts(width='800px', height='750px'))
c.add('',[list(z) for z in zip(province_num.index.tolist(), province_num.values.tolist())], 'china')
c.set_global_opts(title_opts=opts.TitleOpts('调剂信息省份分布地图'),
toolbox_opts=opts.ToolboxOpts(is_show=True),
visualmap_opts=opts.VisualMapOpts(max_=110))
c.render_notebook()
#词云
import jieba.analyse #cmd pip install jieba
from pyecharts.charts import WordCloud
from pyecharts.globals import SymbolType,ThemeType
word1 = WordCloud(init_opts=opts.InitOpts(width='1280px', height='750px'))
word1.add("", [*zip(key_words.words, key_words.num)],
word_size_range=[20, 200], shape='diamond')
word1.set_global_opts(title_opts=opts.TitleOpts(title="调剂专业分布"),
toolbox_opts=opts.ToolboxOpts())
word1.render_notebook()
```
### web项目启动文件app.py
- 导入所需模块(在pycharm运行时需要安装相应模块,也可通过cmd命令pip install package模块包)
```
from flask import Flask, render_template, request, flash, redirect, url_for, session
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import User
import hashlib
import pymysql
import numpy as np
import pandas as pd
import jieba.analyse
import logging
```
- Mysql数据库
```
pymysql.install_as_MySQLdb()
engine = create_engine("mysql://root:123456@127.0.0.1/work?charset=utf8mb4")
dbsession = sessionmaker(bind=engine)
db = dbsession()
```
- def函数调用及实现web页面
```
def load_data():
df = pd.read_csv('考研调剂数据-3.09.csv', encoding='utf-8')
df_info = pd.read_csv('大学信息_整理后.csv', encoding='utf-8')
return df, df_info
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = "as11sad"
return app
app = create_app()
@app.route("/")
def home():
return render_template('home.html')
#用户登陆页面
@app.route('/login/', methods=["GET", "POST"])
def login():
try:
if request.method == "GET":
session.clear()
return render_template("login.html")
name = request.form.get("name")
password = request.form.get("password")
if name == "":
flash("用户名不能为空")
return redirect(url_for('login'))
if password == "":
flash("密码不能为空")
return redirect(url_for('login'))
user = db.query(User).filter_by(name=name).all()
if user == []:
flash("该用户不存在,请先注册。")
return redirect(url_for('login'))
md5 = hashlib.md5()
md5.update(password.encode("utf-8"))
if md5.hexdigest() != user[0].password:
flash("密码错误,请重新输入密码。")
return redirect(url_for('login'))
flash("登录成功!")
session["id"] = user[0].id
session["name"] = name
app.logger.info('%s用户登录成功' % name)
return redirect(url_for('index'))
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
@app.route('/index/', methods=["GET", "POST"])
def index():
try:
user_id = session.get("id")
name = session.get("name")
if name == None:
return redirect(url_for('home'))
df, df_info = load_data()
df_info = df_info.drop_duplicates() # 删除重复数据
df_2020 = df[df['time'].str.contains('2020')].copy()
df_all = pd.merge(df_2020, df_info, how='left', on='school')
df_all = df_all[['school', 'name', 'time', 'province', 'school_level', 'school_types']]
level_perc = df_all.school_level.value_counts() / df_all.school_level.value_counts().sum()
level_perc = np.round(level_perc * 100, 2)
index = level_perc.index.tolist()
data = level_perc.tolist()
datas = []
for k,v in zip(index, data):
data_d = {}
data_d["value"] = v
data_d["name"] = k
datas.append(data_d)
print(datas)
app.logger.info('%s用户进入主页' % name)
return render_template('index.html', index=index, datas=datas)
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
@app.route('/register/', methods=["GET", "POST"])
#用户注册
def register():
try:
if request.method == "GET":
return render_template('register.html')
name = request.form.get("name")
password = request.form.get("password")
re_password = request.form.get("re_password")
if name == "":
flash("用户名不能为空")
return redirect(url_for('register'))
if password == "":
flash("密码不能为空")
return redirect(url_for('register'))
if re_password == "":
flash("确认密码不能为空")
return redirect(url_for('register'))
if len(password) <= 6 or len(password) >= 18:
flash("密码长度必须大于6位小于18位")
return redirect(url_for('register'))
if password != re_password:
flash("两次密码输入不一致")
return redirect(url_for('register'))
user = db.query(User).filter_by(name=name).all()
if user != []:
flash("改用户名已被占用")
return redirect(url_for('register'))
md5 = hashlib.md5()
md5.update(password.encode("utf-8"))
user = User(name=name, password=md5.hexdigest())
db.add(user)
db.commit()
flash("注册成功!")
app.logger.info('%s用户注册成功' % name)
return redirect(url_for('index'))
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
@app.route('/logout/', methods=["GET", "POST"])
def logout():
try:
user_id = session["id"]
name = session["name"]
session.clear()
flash("注销成功!")
app.logger.info('%s用户注销成功' % name)
return redirect(url_for('home'))
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
@app.route('/school_level/', methods=["GET", ])
def school_level():
try:
user_id = session.get("id")
name = session.get("name")
if name == None:
return redirect(url_for('home'))
app.logger.info('%s用户查看学校等级模块成功' % name)
return render_template('index.html')
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
@app.route('/school_type/', methods=["GET", ])
def school_type():
try:
user_id = session.get("id")
name = session.get("name")
if name == None:
return redirect(url_for('home'))
df, df_info = load_data()
df_info = df_info.drop_duplicates() # 删除重
df_2020 = df[df['time'].str.contains('2020')].copy()
df_all = pd.merge(df_2020, df_info, how='left', on='school')
df_all = df_all[['school', 'name', 'time', 'province', 'school_level', 'school_types']]
type_perc = df_all.school_types.value_counts() / df_all.school_types.value_counts().sum()
type_perc = np.round(type_perc * 100, 2)
index = type_perc.index.tolist()
data = type_perc.values.tolist()
datas = []
for k, v in zip(index, data):
data_d = {}
data_d["value"] = v
data_d["name"] = k
datas.append(data_d)
print(datas)
app.logger.info('%s用户查看学校类型模块成功' % name)
return render_template('school_type.html', index=index, datas=datas)
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
@app.route('/num_dis_info/', methods=["GET", ])
def num_dis_info():
try:
user_id = session.get("id")
user_name = session.get("name")
if user_name == None:
return redirect(url_for('home'))
df, df_info = load_data()
df_info = df_info.drop_duplicates() # 删除重
df_2020 = df[df['time'].str.contains('2020')].copy()
df_all = pd.merge(df_2020, df_info, how='left', on='school')
df_all = df_all[['school', 'name', 'time', 'province', 'school_level', 'school_types']]
province_num = df_all.province.value_counts().sort_values()
name = province_num.index.tolist()
num = province_num.values.tolist()
datas = []
for k,v in zip(name, num):
l = []
l.append(v)
l.append(k)
datas.append(l)
print(datas)
app.logger.info('%s用户查看调剂信息发布数省份成功' % user_name)
return render_template('num_dis_info.html', datas=datas)
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
#日志管理系统
@app.route('/log/')
def hello_world():
with open(r'flask.log', 'r', encoding='utf-8') as f:
data = f.readlines()
datas = []
for i in data:
d = {}
detail = i.split('-')
d["time"] = detail[0] + "-" + detail[1] + "-" + detail[2]
d["level"] = detail[3]
d["function"] = detail[5]
d["line"] = detail[6]
d["message"] = detail[7][0:-1]
print(d)
datas.append(d)
return render_template('log.html', datas=datas)
if __name__ == '__main__':
app.debug = True
handler = logging.FileHandler('flask.log', encoding='UTF-8')
handler.setLevel(logging.INFO) # 设置日志记录最低级别为DEBUG,低于DEBUG级别的日志记录会被忽略,不设置setLevel()则默认为NOTSET级别。
logging_format = logging.Formatter(
'%(asctime)s-%(levelname)s-%(filename)s-%(funcName)s-%(lineno)s-%(message)s')
handler.setFormatter(logging_format)
app.logger.addHandler(handler)
app.run()
```
## 云端项目部署的基本描述
- 参考文档
[手把手教你在pythonanywhere上部署Flask项目](https://www.jianshu.com/p/5d120cfd386e)
### 各个HTML页面简要介绍
此网站主题为“2020年考研调剂信息数据报告”,主要通过数据可视化表格、数据交互更直观、灵活地呈现数据。一共有十个页面:注册页面、注册成功页面、登陆页面、六个数据可视化图表页面、注销页面。以下提供部分URL链接。
- [登录页面](http://colleenproject.eu.pythonanywhere.com/)
☺经过测试,请您按照注册-登陆的顺序查阅报告。点击右上角,您可以进行[注册](http://colleenproject.eu.pythonanywhere.com/register/),注册成功后进入[登陆页面](http://colleenproject.eu.pythonanywhere.com/login/),输入账号密码即可报告首页。
进入首页后,您可以点击‘查看图表’查看其他数据可视化图表。
退出时,请您点击右上角的‘注销’按钮,出现注销成功后即您已退出登陆状态。
有时候需要刷新后重新注册才不会出现error页面喔!
- [调剂信息发布时间走势图](http://colleenproject.eu.pythonanywhere.com/time_chart/)
考研调剂信息发布数量有两个明显的高峰值
原因:
2020年考研成绩公布时间|2月左右
---|---
2020年考研国家线公布时间|3月下旬-4月初
2020年考研复试调剂时间|集中于3-4月份
(1)二月中下旬考研成绩公布阶段,所以各高校、各媒体陆续发布与调剂相关的信息;
(2)四月份左右,各高校陆续进入考研复试调剂阶段,有关调剂的信息也陆续增多。
- [学校层次分布图](http://colleenproject.eu.pythonanywhere.com/index/)
接受调剂学校层次以双非学校为主,211学校其次,985学校最后。
- [学校类型分布](http://colleenproject.eu.pythonanywhere.com/school_type/)
接受调剂学校类型排行前三的为:理工类、综合类以及师范类。
- [调剂信息发布数省份分布](http://colleenproject.eu.pythonanywhere.com/num_dis_info/)
调剂信息发布数量最多的省份/市为北京市,其次是辽宁省,再次是广东省。
- [调剂省份分布地图](http://colleenproject.eu.pythonanywhere.com/map/)
同时,我们也可以发现北京市、辽宁省、广东省恰恰接受调剂院校分布最多的地区。
- [调剂专业分布词云](http://colleenproject.eu.pythonanywhere.com/word_cloud/)
调剂专业排行前三的是材料科学与工程、工程、材料。
原因:化工材料专业考生与招生人数众多,所以造成学校考研名额不平衡,进行考研调剂。
### 心得
确保所使用模块都安装在所属环境下,python安装版本应为python3以上,python2.7容易出现安装错误,不停地报错T_T,pip安装命令行不通时,多去百度,可以直接复制ERROR搜索,很多有效的经验贴,然后部署时记得确保文件夹路径正确,所需要的虚拟环境已经创建完毕,Mysql数据库也应跟本地保持一致。最后就可以部署了,部署不成功记得常看操作日志文件,找寻错误,一般都是因为模块漏装导致报错。
## 数据传递描述
### app.py
引用了hashlib/pymysql/numpy/pandas/jieba.analyse/logging/pyecharts模块
- 登陆页面
```
def login():
try:
if request.method == "GET":
session.clear()
return render_template("login.html")
name = request.form.get("name")
password = request.form.get("password")
if name == "":
flash("用户名不能为空")
return redirect(url_for('login'))
if password == "":
flash("密码不能为空")
return redirect(url_for('login'))
user = db.query(User).filter_by(name=name).all()
if user == []:
flash("该用户不存在,请先注册。")
return redirect(url_for('login'))
md5 = hashlib.md5()
md5.update(password.encode("utf-8"))
if md5.hexdigest() != user[0].password:
flash("密码错误,请重新输入密码。")
return redirect(url_for('login'))
flash("登录成功!")
session["id"] = user[0].id
session["name"] = name
app.logger.info('%s用户登录成功' % name)
return redirect(url_for('index'))
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
```
- 读入数据,放上数据图表代码,并实现将其呈现在html页面中。
```
def school_type():
try:
user_id = session.get("id")
name = session.get("name")
if name == None:
return redirect(url_for('home'))
df, df_info = load_data()
df_info = df_info.drop_duplicates() # 删除重复
df_2020 = df[df['time'].str.contains('2020')].copy()
df_all = pd.merge(df_2020, df_info, how='left', on='school')
df_all = df_all[['school', 'name', 'time', 'province', 'school_level', 'school_types']]
type_perc = df_all.school_types.value_counts() / df_all.school_types.value_counts().sum()
type_perc = np.round(type_perc * 100, 2)
index = type_perc.index.tolist()
data = type_perc.values.tolist()
datas = []
for k, v in zip(index, data):
data_d = {}
data_d["value"] = v
data_d["name"] = k
datas.append(data_d)
print(datas)
app.logger.info('%s用户查看学校类型模块成功' % name)
return render_template('school_type.html', index=index, datas=datas)
except Exception as e:
app.logger.warning(e)
app.logger.error(e)
```
- 前端与后端的相互传值。将csv数据表格生成表单html,读取数据图表的值,返回html/title/数据图表/数据表格
```
def load_data():
df = pd.read_csv('考研调剂数据-3.09.csv', encoding='utf-8')
df_info = pd.read_csv('大学信息_整理后.csv', encoding='utf-8')
return df, df_info
```
日志管理系统位于flask.log文件
## 学习/实践心得总结及感谢
### 获得
- 学会利用百度搜索
- 学会寻找有效经验贴
- 学会询问前辈
- 和鲸社区有许多有效数据集以及项目经验,还有制图小技巧,可以多多学习
### 挑战
- 云端部署不断踩坑,但是通过百度以及前人经验贴顺利部署
- 跑代码是缩进问题很常见,要多多注意
### 经验帖分享
- [Mysql数据库连接问题](https://blog.csdn.net/u012604745/article/details/80632860)
- [阿里云服务器-Python的web部署到服务器](https://blog.csdn.net/liul99/article/details/106199829?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-7.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-7.control)
- [Python3数据可视化模块Matplotlib](https://blog.csdn.net/asialee_bird/article/details/79585869)
- [谈谈Python实战数据可视化之matplotlib模块(基础篇)](https://blog.51cto.com/12731497/2154195)
- [pythonanywhere部署资料](https://www.jianshu.com/p/058b778500de)
- [Pythonanywhere 部署 Django](https://www.jianshu.com/p/937694906ec0)
- [学习flask的番外5之Pythonanywhere部署Flask项目](https://www.jianshu.com/p/9974701034ef)
- [python 封装自己的log日志系统](https://blog.csdn.net/mr_hui_/article/details/103517031)
- [PythonWEB框架之Flask](https://www.so.com/link?m=bL4khQ9nh1q2rDQkko7XDnf9uOG02iCbrdKlsH8mOlR5UT8NX%2BNXx93PnbKEcyGOSXGeCmUtWIlQznZPMYBV9MYalzroJmrhbHhgCcuj2PYfNcagA%2F%2B%2FrzszbJ37WedUUVRiD23IuI5y7bkbjcOT1YI0zEzfSklwaRRtbY3tpzMrEuV%2FRnD%2BJPkD%2B8cU%3D)
- [python-def函数](https://www.cnblogs.com/derezzed/articles/8119592.html)