打包用的是pyinstaller ,直接在cmd里面,进入到py文件(我的py文件名是main.py)跟login的文件夹中,

pyinstaller -w main.py

会生成几个文件夹,打包好的exe 文件就存在dist中的main,

把login图标跟chromedriver驱动复制到dist文件夹中,不然会出现如下错误提示

没有图标时:

failed to execute script main

没有chromedriver时:

程序没反映,

源程序 如下:

"""
tkinter GUI界面测试V0.1
介绍:jinru()是第二窗口的函数,利用登陆成功提示框返回的"ok"信息,注销原登陆框,并新开一个窗口
实现功能,登陆界面
未添加:输入密码后,按回车键等同登陆按键


"""
import requests
from selenium import webdriver
import time
import re
from tkinter import *
import pickle
from tkinter import messagebox

import threading
#定义一个界面选择列表
course_list = [
    {"name":"11.劳动合同法","course_id":11253,"course":11,"lesson_start":146026,"lesson_end":146055},
    {"name":"12.谈判理论与技巧","course_id":11237,"course":12,"lesson_start":145503,"lesson_end":145524},
    {"name":"13.逻辑学","course_id":10574,"course":13,"lesson_start":121663,"lesson_end":121716},
    {"name":"14.个人理财","course_id":10977,"course":14,"lesson_start":131999,"lesson_end":132045},
    {"name":"15.人力资源管理","course_id":2102,"course":15,"lesson_start":104823,"lesson_end":104855},
    {"name":"16.公共关系学","course_id":10031,"course":16,"lesson_start":114785,"lesson_end":114836},
    {"name":"17.宏观经济管理","course_id":1968,"course":17,"lesson_start":159058,"lesson_end":159088},

    #原来那些需要学习的课程,因为已经学习完,随着课程的进展,学习完的课程已经找不到观看按钮,无法进入
    # 后期需要观看的新课程,会显示出来新按钮
    {"name": "18.国家公务员制度", "course_id": 11288, "course": 18, "lesson_start": 147224, "lesson_end": 147275},  # 
    {"name": "19.公共政策分析", "course_id": 10824, "course": 19, "lesson_start": 128841, "lesson_end": 128852},  # 
    {"name": "20.社会调查研究方法", "course_id": 11121, "course": 20, "lesson_start": 140574, "lesson_end": 140704},  # 后期增加新课程时,会显示出来新按钮
   # {"name":"18.国家公务员制度","course_id":0000,"course":18,"lesson_start":0000,"lesson_end":0000},#未有课程添加
   # {"name":"19.公共政策分析","course_id":0000,"course":19,"lesson_start":0000,"lesson_end":0000},#未有课程添加
    #{"name":"20.社会调查研究方法","course_id":0000,"course":20,"lesson_start":0000,"lesson_end":0000},#未有课程添加
    {"name":"21.专业实践实训","course_id":0000,"course":21,"lesson_start":0000,"lesson_end":0000},#未有课程添加
    {"name":"22.综合能力考核","course_id":0000,"course":22,"lesson_start":0000,"lesson_end":0000}#未有课程添加
]

#定义全局变量cookie_play,接收登陆界面时,顺便接收的cookie,用于在播放教程时使用

# number = ""
# name = ""


#第一个窗口,登陆窗口的参数
window1 = Tk()
# 登陆窗口标题
window1.title("汕头大学继续教育学院")
# 登陆窗口420x300是大小,+500+300是离屏幕左边距离及上边距离
window1.geometry('420x300+500+300')
# 禁止登陆窗口拉伸
window1.resizable(width=False, height=False)

# 用画布加载图片,及图片大小
canvas = Canvas(window1, height=200, width=500)
# 读取图片
image_file = PhotoImage(file='login.gif')
# 设置图片离窗口左边及上边的距离 ,
image = canvas.create_image(210,10,anchor='n', image=image_file)
canvas.pack(side='top')

# 标签,place的x,y是离窗口左边及上边的距离,
Label(window1, text='用户名:',font = (14)).place(x=90, y=150)
Label(window1, text='密  码:',font = (14)).place(x=90, y=190)

# 输入框
var_usr_name = StringVar()
var_usr_pwd = StringVar()

#用户输入框离窗口左边及上边距离 ,还有输入框大小。
entry_usename = Entry(window1, textvariable=var_usr_name)
entry_usename.place(x=150, y=150,width = 150,height = 30)

entry_password = Entry(window1, textvariable=var_usr_pwd, show='*')
entry_password.place(x=150, y=190,width = 150,height = 30)

#生成线程,防止用户界面卡死
def thread_it(func, *args):
    '''将函数打包进线程'''
    # 创建
    t = threading.Thread(target=func, args=args)
    # 守护 !!!主界面关闭也留后台
    t.setDaemon(True)
    # 启动
    t.start()
    # 阻塞--卡死界面!
    # t.join()

"""                      这是selenium模拟登陆到获取视频播放界面cookie的代码                   """
#selenium设置
def selenium_set():
    # 加载启动项,新版本关闭正受到自动测试软件的控制
    chrome_option = webdriver.ChromeOptions()
    chrome_option.add_argument('headless')  # 设置option不打开浏览器
    chrome_option.add_experimental_option("excludeSwitches", ['enable-automation'])
    #打开浏览器
    wd = webdriver.Chrome(options=chrome_option)# 调用带参数的谷歌浏览器
    return wd

def course_watch(course_id,lesson_start,lesson_end,cookie,text):
    """

    :param course_id: 课程id
    :param lesson_start: 课程章节开始Id
    :param lesson_end: 课程章节结束id
    :param cookie: 视频播放界面所需cookie
    :return: None
    """
    #print(f"这是要播放时的cookie\n{cookie}")
    headers = {
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',

        'Referer': 'https://learning.mhtall.com/play/player.html?course_id=11253&course_uuid=d99f5205-88be-4e33-9001-8372163efeec&course_code=null&lesson_id=146026',
        'cookie':cookie
    }


    #视频播放地址
    url_wacth ="https://learning.mhtall.com/rest/user/course/lesson/onexit?course_id="
    num = 0

    for lesson_id in range(lesson_start,lesson_end+1):
        result_new = 0
        for y in range(10,2000,10):
            response = requests.get(url = url_wacth + str(course_id) + "&lesson_id=" + str(lesson_id) + '&finish_len=' + str(y),headers = headers)
            time.sleep(4)
            #服务器每次得到get,用len后面增加的数,来判断是否时间间隔,太快会返回观看时间太短,成功会用result返回一个数字
            #当result返回的数相同时,代表视频已经观看完成。重新观看这个数字也不会变。
            result = response.json()['data']
            #message会显示操作成功
            result1 = response.json()['message']
            #利用result是否相同来判断视频是否已经看完
            if result_new == result:
                 text.insert(END,f"已完成第{lesson_id}小节\n")
                 text.insert(END,time.strftime('%Y-%m-%d %H:%M\n'))
                 text.see("end")
                 text.update_idletasks()
                 break
            text.insert(END,f"{result1}\t")
            text.insert(END,f"{result}\n")
            text.see("end")
            text.update_idletasks()
            result_new = result
    return


#selenium模拟登陆到主页
def selenium_login(usr_name,usr_pwd):

    #调用selenium设置头
    wd = selenium_set()
    wd.get('https://se.mhtall.com/stuedu/login/')
    #wd.implicitly_wait(2)
    #登陆框输入
    wd.find_element_by_xpath('//*[@id="txtLoginName"]').send_keys(usr_name)
    wd.find_element_by_xpath('//*[@id="txtPassword"]').send_keys(usr_pwd)
    # 点击学生按键
    wd.find_element_by_xpath('//*[@id="rdoUserType3"]').click()
    # 点击登陆按键
    wd.find_element_by_xpath('//*[@id="login_button"]').click()
    wd.implicitly_wait(1)
    html = wd.page_source

    if re.findall(r".*?sysUserName: '(.*?)',", html):


        name = re.findall(r".*?sysUserName: '(.*?)',", html)
        number = re.findall(r".*?sysStudentNo: '(.*?)',", html)
        wd.get('https://se.mhtall.com/stuedu/rs/roll_course')
        # 随便选个课程进去,这里是18课的按键位置
        wd.find_element_by_xpath('//*[@id="app"]/div[2]/div[3]/table/tbody/tr[18]/td[4]/div/button/span').click()

        #原14课地址,不过学完之后,已经不能选择,找不到
        #wd.find_element_by_xpath('//*[@id="app"]/div[2]/div[3]/table/tbody/tr[11]/td[4]/div/button/span').click()
        # 重新访问到课程要播放的界面,这样取得cookie
        wd.get('https://learning.mhtall.com/play/course_detail.html?course_id=11288')
        # selenium得到的cookie转化为requests字符串
        global cookie_play
        cookie_play = '; '.join(item for item in [item["name"] + "=" + item["value"] for item in wd.get_cookies()])
        print(cookie_play)
        # 关闭浏览器
        wd.quit()
        # 登陆成功后的提示框,messagebox.showinfo,按确认后,返回"ok"
        if messagebox.showinfo(title='提示:', message='登录成功!') == "ok":

            window2(name, number)
        #print(name)

    elif re.findall(r".*?<body>(.*?)错误!</body.*?", html):
        print("错误")
        messagebox.showerror('提示:', message='用户名或密码错误')
        # 输入错误后,登陆按键变正常
        btn_login["state"] = 'normal'
        return

    return


"""                          这是selenium模拟登陆到获取视频播放界面cookie的代码        结束"""


# 输入框输入用户密码,登陆返回用户名,学号,cookie
def wondows_login():
    # 按键变灰
    btn_login["state"] = 'disabled'
    # 把输入框输入的内容返回给变量
    usr_name = var_usr_name.get()
    usr_pwd = var_usr_pwd.get()

    # 获取浏览器登陆成功后源码,方便取出用户信息

    selenium_login(usr_name, usr_pwd)

    # print(f"这里用户登陆函数第一次\n{cookie_play}")
    # 进入课程选择界面


# 登陆成功后主窗口
def window2(name,number):
    global root  # //控制外部界面
    #注销原窗口
    window1.destroy()
    #新建窗口
    window2 = Tk()
    # 登陆窗口标题
    window2.title("汕头大学继续教育学院")
    # 登陆窗口420x300是大小,+500+300是离屏幕左边距离及上边距离
    window2.geometry('520x400+500+300')
    # 禁止登陆窗口拉伸
    window2.resizable(width=False, height=False)
    # 生成右边的多行文本框
    text = Text(window2, relief=SUNKEN, font=(14))
    text.place(x=230, y=10, width=250, height=350)

    # 生成显示用户跟学号的位置,relief 边框样式,justify 对齐方式
    Label(window2,text = f"用户:{name[0]}\n学号:{number[0]}",relief = SUNKEN ,font = (14),justify = "left").place(x=10, y=10,width = 180,height = 70)

    #显示 请选择需要的课程  这一行的代码
    Label(window2, text="请选择需要的课程", font=(14), justify="left").place(x=10, y=80, width=180, height=30)



    #批量生成单选框,把variable变量全绑定在一个变量上,value值不同,选择任意一个时,任意一个就替代了变量V,让V无法接收多个值 。也就无法多选
    i = 0
    v = IntVar()
    #V.set是让单选框默认选择11这一框,顺便把课程编号course_id,课程开始lesson_start到结束lesson_end,各小节编号 都打印出来
    v.set(11)
    #这一行代码,本来想说,直接打印各参数,结果测试才发现,想多了,这只是循环打印了一次全课程表,并不会每一次按按钮都循环一次更新
    for course in course_list:
        Radiobutton(window2, text=course["name"], variable=v, value=course["course"],font = (14)).place(x = 10,y =110 + i)
        coures_name = course["name"]

        i += 20

    #获得选择的单选框数字 ,打印对应文本在右边的多行显示框里面,
    def print_course_text():
        #得到单选框对应数字
        course = v.get()
        #print(v.get())
        #判断单选框数字,打印对应内容到右边多行文本框
        if 11 > course or course > 22:
            text.insert(END, "你输入的课程不在可观看范围内,请重新输入:\n")
            return (0, 0, 0)

        elif 11 <= course <= 17 or 21 <= course <= 22:
            text.insert(END,"你选择的课程已观看完成\n")
        #elif 11 <= course <= 22:
        #    text.insert(END, "你选择的课程还未增加\n")
        #    return (0, 0, 0)
        else:
            for course_dict in course_list:
                if course_dict["course"] == course:
                    course_id = course_dict["course_id"]
                    lesson_start = course_dict["lesson_start"]
                    lesson_end = course_dict["lesson_end"]
                    text.insert(END, f'你选择的课程是:\n{course_dict["name"]}\n')

                    text.insert(END,"现在开始播放课程\n")
                    cookie_play
                    course_watch(course_id, lesson_start, lesson_end, cookie_play,text)
                    text.insert(END,"课程已完成\n")

        # 这一行代码很重要,可以让超出文本框的内容显示出来 。
        text.see("end")
        text.update_idletasks()

    #单选框后面,确认按钮 的位置代码
    # 按钮command后面带函数,可以使用匿名函数 command = lambda:a(x),这样来给所激活函数带参数
    # 定义一个确认按钮,
    btn_whtch = Button(window2, text='确认',command=lambda :thread_it(print_course_text))
    btn_whtch.place(x=50, y=360, width=120, height=30)
    window2.mainloop()

# 登陆按钮,thread_it是线程
btn_login = Button(window1, text='登录',command=wondows_login)
btn_login.place(x=160, y=230,width = 120,height = 30)

# 调用组件的mainloop()方法,进入事件循环
window1.mainloop()

遇到的问题有:

因为使用的是谷歌浏览器,会遇到重开程序,观看其它课程时,显示无权观看该视频,只有在浏览器登陆一下该账号,重新打开程序才能继续使用,感觉应该是浏览器cookie用到了原来的账号,造成程序在登陆别的账号时,调用的却是原来的cookie,无法通过验证,没法观看。

不足地方:

1,不智能,没有停止按键,也没法直接在原程序中观看别的课程,要重新打开程序,重新选择,

2,没法选择多课程直接播放完成,

3,程序无法解决在打开时,窗口未响应,也就是登陆获取cookie时,窗口卡死情况,

4,无法在win32位上运行,怎么解决pyinstaller在64位打包成32位可执行软件

5,程序关闭重新登陆其它账号时,显示登陆,但播放视频时显示无法登陆,没办法观看 ,怀疑是浏览器内存在关闭时没有清除。

6,已学习完成的课程会没有 开始学习 按钮,能增加自动检测哪些课程有按钮来判断哪些还没学最好

7,刷新视频进度时,如果显示是哪一课,会更直观,其实这个直接可以在网页源代码中找到,

请输入图片描述

最后修改:2021 年 04 月 06 日
如果觉得我的文章对你有用,请随意赞赏