Python之项目课

1.项目准备

1.1.项目创建

此处省略一万字...

1.2.项目目标

  • 创建数据库movie并设置编码格式,并完成t_user(用户信息表)、t_movie(电影信息表)的创建任务;

  • 完成用户登录功能,登录成功之后跳转到电影主界面;

  • 完成电影排行榜和关键字电影查询功能;

  • 完成电影信息图表统计(选作)

1.3.项目结构

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                  # 运行程序入口

2.功能实现

安装

创建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"}' \
            ']'

2.1.电影排行榜

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}

2.2.关键字查询电影

下载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}

3.技术难点

3.1.下拉框数据绑定

初始化下拉框列表:

# 下拉列表框
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

3.2.列表滚动条设置

# 框架布局,承载多个控件
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)

3.3.列表事件

# 绑定treeview行的双击事件
# <Double-1>:双击事件
# <<TreeviewSelect>>:行选中事件
movie_tv.bind('<Double-1>', self.select_treeview)

3.4.点击查询防止GUI界面假死无响应

定义线程函数:

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是单独定义的函数。

3.5.统计图表

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()

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值