云班课网课攻略--python

Scroll Down

云班课攻略

前言

疫情期间上英语课需要使用云班课来进行学习,但是我自己看学习效率太低,所以我就做了一个脚本,现学现用,代码可能写的不好,请多多包容

运行环境

  1. python 3.7.4 通过Anaconda安装
  2. request 模块
    安装方式 pip install request
  3. BeautifulSoup 模块
    安装方式 pip install beautifulsoup4
  4. math 模块 python 自带

步骤

模拟登录


通过查看网页源代码找到登录发送的request请求

读取源码发现,通过发送ajax的post请求完成账号密码登录,获得url等信息
返回值中有一个result_code,如果是0,就代表登录成功

account_name = 账户名
passWord = 密码
# 发送登录请求
r=requests.post("https://www.mosoteach.cn/web/index.php?c=passport&m=account_login",data={
    "account_name":account_name,
    "user_pwd":passWord,
    "remember_me":"N",
    "time":"",
    "sign":"",
    "scene":""
})

我们保存获取到的respond,接下来要从中提取cookie
登录完毕 ,我们需要跳转网页到班级界面

r2 = 
requests.post("https://www.mosoteach.cn/web/index.php?c=clazzcourse&m=index",cookies=r.cookies)        
# 请求网页,带有cookie

班级界面


可以看到我有加入两个班级,我们需要访问里面的内容就需要获取其url

通过查看网页源码发现其中的data-url属性后跟着的是该课的url,我们需要先定位到这个li元素上

html = r2.content
soup = BeautifulSoup(html,"html.parser")
classes = soup.findAll("li",class_="class-item")            #寻找课程

我们在这段代码中使用了BeautifulSoup库

Beautiful Soup
是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间

我们可以把他理解成jquery来获取元素
classes是所有课程的li的集合,我们遍历这个集合从中获取信息,并发送请求访问班级里面的内容

for tag in classes:
        temp_r = requests.post(tag.attrs['data-url'],cookies=r.cookies)
        bs2 = BeautifulSoup(temp_r.content,"html.parser")       

班级内容


班级的初始访问界面是这样的,我们找不到要看的视频,这些视频都在资源里面,所有我们要让python访问资源

通过查看网页源代码我们可以发现在<div id = "menu-content">中可以找到资源的访问路径

     menu = bs2.find(id="menu-content")      #定位菜单
     for child in menu:
            if child.name=="a":     #遍历菜单栏
                if child.span.string == "资源":     #找资源页
                    res = requests.post(child.attrs['href'],cookies=r.cookies)      #发送请求,请求资源页
                    resourses = BeautifulSoup(res.content,"html.parser")            #获取资源界面

我们会寻找其孩子,如果孩子span的内容是资源,我们就找到了这个路径,访问它

资源界面

我们可以发现我们有两个视频
我们现在需要试着去发现云班课是如何提交学习进度
通过chrome浏览器的开发者工具(通过f12调出),来查看一下请求

在这里,我们发现浏览器向https://www.mosoteach.cn/web/index.php?c=res&m=save_watch_to发送了一个请求,通过语义的观察,我们可以看出个url是记录观看视频的结果的

检查了一下传的参数,我们需要三个参数

  1. clazz_course_id
  2. res_id
  3. duration
    另外两个参数和duration的值相同就行了
    通过一些手段查找,我发现clazz_course_idres_id都在html中能够找到
<div class="res-row-open-enable res-row preview " 
            data-source=""
            data-mime="video"
            data-status="Y"
            data-release-status="Y"   
            data-value="E2E65769-8D95-49A2-8160-60B2068B4BA6"
            data-operater-id="1B1A3F4E-1BB0-11E9-832A-EC0D9ACEE976"                   
            data-operater-role=""
            data-operater-name="曹永利"
            data-drag="Y" 
            data-watch-to="0" 
            data-last-watch-to="0"
            data-href="https://www.mosoteach.cn/web/index.php?c=res&m=download&file_id=E2E65769-8D95-49A2-8160-60B2068B4BA6&clazz_course_id=82FA7052-53C8-11EA-BED0-B8599FEB4980"
            data-target-date=""
            data-target-time="">

其中的data-href中有一个clazz_course_iddata-value就是res_id
所以我们通过BeautifulSoup对象来获取内容

    href=find.attrs['data-href']
    clazz_id = href[href.find("clazz_course_id=")+16:]              #截取字符串
    res = find.attrs['data-value']                                  #获取res_id

剩下一个duration,我实在js代码中没找到,但是山穷水复疑无路,柳暗花明又一村,我看到界面上有显示的大概时间,便可以通过这个大概的时间进行换算得到duration

    timeParent = find.find("div",class_="create-box manual-order-hide-part")
    time = ""        #duration参数
    #获取duration,但是这个是根据网页元素获取,可能会有1min以内的偏差
    for child1 in timeParent:                                   
        if "分钟" in child1.string:
            time=child1.string[0:child1.string.find(" ")]
            break
    duration = float(time)*60

现在三个参数都齐了,我们来发送最后的请求

resp = requests.post("https://www.mosoteach.cn/web/index.php?c=res&m=save_watch_to",{
                "clazz_course_id": clazz_id,
                "res_id": res,
                "watch_to": math.ceil(duration),
                "duration": math.ceil(duration),
                "current_watch_to": math.ceil(duration)},cookies=r.cookies)

请求结束后刷新界面会发现已经获取到经验了

完整代码

import requests
import json
from bs4 import BeautifulSoup
import math

account_name = 用户名
passWord = 密码
# 发送登录请求
r=requests.post("https://www.mosoteach.cn/web/index.php?c=passport&m=account_login",data={
    "account_name":account_name,
    "user_pwd":passWord,
    "remember_me":"N",
    "time":"",
    "sign":"",
    "scene":""
})
# 转化json字符串为dict
jsons =json.loads(r.content)
if jsons['result_code']==0 :
    print("登录成功")
    r2 = requests.post("https://www.mosoteach.cn/web/index.php?c=clazzcourse&m=index",cookies=r.cookies)        # 请求网页,带有cookie
    html = r2.content
    soup = BeautifulSoup(html,"html.parser")
    classes = soup.findAll("li",class_="class-item")            #寻找课程
    for tag in classes:
        temp_r = requests.post(tag.attrs['data-url'],cookies=r.cookies)
        bs2 = BeautifulSoup(temp_r.content,"html.parser")       
        menu = bs2.find(id="menu-content")      #定位菜单
        for child in menu:
            if child.name=="a":     #遍历菜单栏
                if child.span.string == "资源":     #找资源页
                    res = requests.post(child.attrs['href'],cookies=r.cookies)      #发送请求,请求资源页
                    resourses = BeautifulSoup(res.content,"html.parser")            #获取资源界面
                    # classid 在 data-href
                    # res 在 data-value
                    finds = resourses.findAll("div",class_="res-row-open-enable")       #找到各种课程资源
                    for find in finds:
                        if find.attrs["data-mime"]!="video":                            #如果不是视频就跳过
                            continue
                        href=find.attrs['data-href']
                        clazz_id = href[href.find("clazz_course_id=")+16:]              #截取字符串
                        res = find.attrs['data-value']                                  #获取res_id
                        timeParent = find.find("div",class_="create-box manual-order-hide-part")
                        time = ""        #duration参数
                        #获取duration,但是这个是根据网页元素获取,可能会有1min以内的偏差
                        for child1 in timeParent:                                   
                            if "分钟" in child1.string:
                                time=child1.string[0:child1.string.find(" ")]
                                break
                        duration = float(time)*60
                        #发送请求已经观看
                        resp = requests.post("https://www.mosoteach.cn/web/index.php?c=res&m=save_watch_to",{
                            "clazz_course_id": clazz_id,
                            "res_id": res,
                            "watch_to": math.ceil(duration),
                            "duration": math.ceil(duration),
                            "current_watch_to": math.ceil(duration)},cookies=r.cookies)
                        print("已观看:"+find.find("span",class_="res-name").string)
else:
    print("登录失败")