此处省略一万字...
创建数据库movie并设置编码格式,并完成t_user(用户信息表)、t_movie(电影信息表)的创建任务;
完成用户登录功能,登录成功之后跳转到电影主界面;
完成电影排行榜和关键字电影查询功能;
完成电影信息图表统计(选作)
dao |-- __init__.py |-- movie_dao.py # 电影dao层接口类 |-- login_dao.py # 用户dao层接口类 ui |-- __init__.py |-- charts_ui.py # 统计界面 |-- login_ui.py # 登录界面 |-- movie_ui.py # 电影主界面 utils |-- __init__.py |-- db_helper.py # dbhelper帮助类 |-- movie_util.py # 电影排行榜和关键字查询电影接口定义 main.py # 运行程序入口
安装
创建movie_util.py类,用于统一处理电影排行榜和关键字查询电影接口定义及测试。
首先初始化电影类型,如下:
movieData = ' [' \
'{"title":"纪录片", "type":"1", "interval_id":"100:90"}, ' \
' {"title":"传记", "type":"2", "interval_id":"100:90"}, ' \
' {"title":"犯罪", "type":"3", "interval_id":"100:90"}, ' \
' {"title":"历史", "type":"4", "interval_id":"100:90"}, ' \
' {"title":"动作", "type":"5", "interval_id":"100:90"}, ' \
' {"title":"情色", "type":"6", "interval_id":"100:90"}, ' \
' {"title":"歌舞", "type":"7", "interval_id":"100:90"}, ' \
' {"title":"儿童", "type":"8", "interval_id":"100:90"}, ' \
' {"title":"悬疑", "type":"10", "interval_id":"100:90"}, ' \
' {"title":"剧情", "type":"11", "interval_id":"100:90"}, ' \
' {"title":"灾难", "type":"12", "interval_id":"100:90"}, ' \
' {"title":"爱情", "type":"13", "interval_id":"100:90"}, ' \
' {"title":"音乐", "type":"14", "interval_id":"100:90"}, ' \
' {"title":"冒险", "type":"15", "interval_id":"100:90"}, ' \
' {"title":"奇幻", "type":"16", "interval_id":"100:90"}, ' \
' {"title":"科幻", "type":"17", "interval_id":"100:90"}, ' \
' {"title":"运动", "type":"18", "interval_id":"100:90"}, ' \
' {"title":"惊悚", "type":"19", "interval_id":"100:90"}, ' \
' {"title":"恐怖", "type":"20", "interval_id":"100:90"}, ' \
' {"title":"战争", "type":"22", "interval_id":"100:90"}, ' \
' {"title":"短片", "type":"23", "interval_id":"100:90"}, ' \
' {"title":"喜剧", "type":"24", "interval_id":"100:90"}, ' \
' {"title":"动画", "type":"25", "interval_id":"100:90"}, ' \
' {"title":"同性", "type":"26", "interval_id":"100:90"}, ' \
' {"title":"西部", "type":"27", "interval_id":"100:90"}, ' \
' {"title":"家庭", "type":"28", "interval_id":"100:90"}, ' \
' {"title":"武侠", "type":"29", "interval_id":"100:90"}, ' \
' {"title":"古装", "type":"30", "interval_id":"100:90"}, ' \
' {"title":"黑色电影", "type":"31", "interval_id":"100:90"}' \
']'
在movie_util.py类中定义电影排行榜接口:
https://movie.douban.com/j/chart/top_list?type=&interval_id=100:90&action=unwatched&start=0&limit=
def get_movie_top(m_type: str,
num: int,
rating: float,
pj: int):
"""
排行榜查询方法
:param m_type: 电影类型
:param num: 爬取数量
:param rating: 影片评分
:param pj: 评价人数
:return:
"""
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
}
url = 'https://movie.douban.com/j/chart/top_list?type=' + str(
m_type) + '&interval_id=100:90&action=unwatched&start=0&limit=' + str(num)
req = request.Request(url=url, headers=headers)
# 用于打开一个远程的url连接,并且向这个连接发出请求,获取响应结果
f = request.urlopen(req)
# 获取响应对象
response = f.read()
# 将json转为python对象
jsonData = loads(response)
movies_list = []
# 循环获取的电影信息并提取有效数据
for subData in jsonData:
# 判断影片评分和评价人数是否达到要求
if (float(subData['score']) >= float(rating)) and (float(subData['vote_count']) >= float(pj)):
movie = {
...
}
movies_list.append(movie)
# 返回字典格式的结果,200代表成功;500代表异常
return {'code':200,'movies':movies_list}
except Exception as ex:
err_str = "出现未知异常:{}".format(ex)
return {'code':500,'msg':err_str}
下载selenium模块:
pip install selenium==4.10.0
通过selenium实现关键字电影查询之前,请在接口中先进行chrome配置,如下:
chrome_options = Options()
# 设置为无头模式,即不显示浏览器
chrome_options.add_argument('--headless')
# 设置user=agent
chrome_options.add_argument(
'user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"')
# 此步骤很重要,设置为开发者模式,防止被各大网站识别出来使用了Selenium
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 不加载图片,加快访问速度
chrome_options.add_experimental_option("prefs",{"profile.managed_default_content_settings.images": 2})
# 加载chromedriver驱动是否成功
load_driver_success = False
browser = None
try:
# 设置chromedriver驱动路径
browser = webdriver.Chrome(options=chrome_options)
# 页面加载超时时间为10s
browser.set_page_load_timeout(10)
# 页面js加载超时时间为10s
browser.set_script_timeout(10)
load_driver_success = True
except Exception as ex:
load_driver_success = False
err_str = "加载chromedriver驱动失败,异常信息:{}".format(ex)
return {'code':500,'msg':err_str}
在movie_util.py类中定义关键字查询电影接口:
电影 - 豆瓣搜索
def get_movie_kw(kw: str):
"""
基于关键字查询电影信息
:param kw:
:return:
"""
# 先进行chrome的配置,请参考上面的配置
if load_driver_success:
try:
url = 'https://search.douban.com/movie/subject_search?search_text='+urllib.parse.quote(kw)+'&cat=1002'
# print(url)
# get方式获取返回数据
browser.get(url)
...
except Exception as ex:
# 关闭浏览器
browser.quit()
err_str = "Selenium获取数据出现其他未知异常:{}".format(ex)
return {'code':200,'msg':err_str}
初始化下拉框列表:
# 下拉列表框
comvalue = StringVar()
movie_combox = Combobox(labelframe, width=5, textvariable=comvalue, state='readonly')
# 将影片类型输入到下拉列表框中
# json数据
jsonMovieData = loads(movieData)
movieList = []
# 对每一种类的电影题材进行操作
for subMovieData in jsonMovieData:
movieList.append(subMovieData['title'])
# 初始化
movie_combox["values"] = movieList
# 选择第一个
movie_combox.current(9)
# 设置显示位置
movie_combox.place(x=65, y=15)
获取下拉框的数据:
# 下拉框的数据 jsonMovieData = loads(movieData) # 循环获取选择下拉框中选中的值 movie_type = None for subMovieData in jsonMovieData: # 判断JSON数据中的title与选中的title是否相同 if subMovieData['title'] == self.movie_combo.get(): movie_type = subMovieData['type'] break
# 框架布局,承载多个控件
frame_root = Frame(labelframe,width=720)
frame_l = Frame(frame_root)
frame_r = Frame(frame_root)
movie_columns=("title","rank",...)
movie_tv = Treeview(frame_l,columns=movie_columns,show="headings")
# 设置列宽和显示位置
movie_tv.column("title",width=242,anchor="center")
..
# 设置显示表头
movie_tv.heading("title",text="电影名称")
...
# 垂直滚动条
vbar = ttk.Scrollbar(frame_r, command=movie_tv.yview)
movie_tv.configure(yscrollcommand=vbar.set)
movie_tv.pack()
vbar.pack(side=RIGHT, fill=Y)
# 框架的位置布局
frame_l.grid(row=0, column=0)
# sticky=NS表示拉高组件,让组件上下填充到表格框的顶端和底端
frame_r.grid(row=0, column=1, sticky=NS)
# 显示位置
frame_root.place(x=6, y=84)
# 绑定treeview行的双击事件 #:双击事件 # < >:行选中事件 movie_tv.bind(' ', self.select_treeview)
定义线程函数:
def thread_it(func, *args):
"""
将函数打包进线程(重要)
:param func:
:param args:
:return:
"""
# 创建
t = Thread(target=func, args=args)
# 守护
t.setDaemon(True)
# 启动
t.start()
给按钮绑定事件:
# lambda表示绑定的函数需要带参数,请勿删除lambda,否则会出现异常 # thread_it表示新开启一个线程执行这个函数,防止GUI界面假死无响应 btn_keyword = Button(labelframe, text="从关键字搜索",command=lambda: thread_it(self.do_search_kw)) btn_keyword.place(x=566, y=46)
此处self.do_search_kw是单独定义的函数。
import time
import matplotlib
import matplotlib.pyplot as plt
from pylab import mpl
from itertools import groupby
matplotlib.use('TkAgg')
class charts_ui():
def __init__(self):
pass
def show_charts(self,source_data,root):
# 指定默认字体
mpl.rcParams['font.sans-serif'] = ['FangSong']
# 解决保存图像是负号'-'显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False
# 提取电影数据中的评分
score = []
for k in source_data:
score.append(k['score'])
# 根据评分进行分组筛选统计计数
data = {}
groups = groupby(sorted(score))
for k,group in groups:
data[k] = len(list(group))
for k, v in data.items():
# ha 文字指定在柱体中间, va指定文字位置 fontsize指定文字体大小
plt.text(k, v + 0.05, '%.0f' % v, ha='center', va='bottom', fontsize=11)
# 设置X轴Y轴数据,两者都可以是list或者tuple
x_axis = tuple(data.keys())
y_axis = tuple(data.values())
# 如果不指定color,所有的柱体都会是一个颜色
plt.bar(x_axis, y_axis, color='orange')
# 指定图表描述信息
plt.title("电影评分统计表")
# 指定Y轴的高度
plt.ylim(0,max(data.values()) + 10)
# 获取当前figure manager
mngr = plt.get_current_fig_manager()
screenwidth = root.winfo_screenwidth()
screenheight = root.winfo_screenheight()
x = int((screenwidth - 600) / 2)
y = int((screenheight - 500) / 2)
# 调整窗口在屏幕上弹出的位置
mngr.window.wm_geometry(f"+{x}+{y}")
plt.show()