智慧树刷网课python脚本

0x00 写在前面

疫情期间确定有不少小伙伴须要上网课,可是有些网课咱们感受十分的鸡肋,本身不感兴趣,又必需要学css

因此我写了这个刷网课的小程序,一方面是锻炼本身的爬虫技术,另外一方面也给同窗们节约宝贵的时间html

几点说明:python

1.此程序只供学习交流,请勿用于商业用途git

2.当前只支持“兴趣课”的刷课,其余类型的课程还不支持github

3.程序尚不完善,可是原理相通,触类旁通,欢迎交流web

0x01 环境准备

python3.7+requests库+selenium库+火狐浏览器chrome

python3.7和requests库的安装没必要赘述 下面来说一下selenium库,这也是我第一次用这个库,记录一下小程序

由于目标网站是通过js渲染的,不使用selenium库很难抓取想要的数据,selenium库能够模拟浏览器进行操做,同时能够配合各大主流浏览器,十分好用api

安装:浏览器

pip install selenium

官网:http://www.seleniumhq.org 

中文文档:http://selenium-python-zh.readthedocs.io

selenium能够配合PhantomJS一块儿使用,PhantomJS能够建立无界面浏览器,使用起来要比浏览器高效,可是这回仍是先从简单的用起来吧,并且调试仍是很须要界面的

对于不一样的浏览器,须要安装不一样的驱动:

Chrome的驱动chromedriver 下载地址:http://chromedriver.storage.googleapis.com/index.html

Firefox的驱动geckodriver 下载地址:https://github.com/mozilla/geckodriver/releases/

IE的驱动IEdriver 下载地址:https://www.nuget.org/packages/Selenium.WebDriver.IEDriver/

我使用的是火狐浏览器,因此直接下载Firefox的驱动:

 下载解压后,将geckodriver.exe添加到python的根目录下,其余浏览器也是同样,添加到python根目录下便可

0x02 核心原理

如今环境已经准备好了,开始研究刷课的原理

根据Firefox抓包能够发现:

 

 

通过实验,我发现当每次用户离开当前界面(例如播放下一个视频、关闭网页)的时候,js都会向服务器发送一个名为save2CCoursProgressV2的post请求,这个包的参数是这样的:

这些参数直接看名字就能知道是什么含义,最重要的参数就是learnTime和totalTime,应该是你观看视频的时间和待在当前界面的时间

因此只要咱们构造这个save2CCoursProgressV2包,而后把相关的参数都填好,把learnTime和totalTime设置为一个很大的数,这样服务器就会认为你学习了很长很长时间

并且参数里面的uuid直接标注了用户的id,因此发这个包的时候甚至不须要cookie来认证,直接post就行了

可是须要注意的是,咱们从哪里获取videoid和lessonid呢?若是id不对的话也是没法记录时间的

通过查找我发现,videoid并非静态的存在网页中,js只会解析出当前播放的视频的videoid,这一点我会在后面的实现过程当中详细说明

因此咱们的工做还包括一个收集videoid和lessonid的过程

这就是本程序的核心原理,直接构造统计视频观看时长的数据包(其中相关参数须要收集),发送到服务器,从而避免浪费大量的时间来观看视频

0x03 实现过程

了解了实现的原理,就只差实现过程了

首先要初始化一个firefox浏览器:

browser = webdriver. Firefox()

尝试进入智慧树的学生主页:

browser.get('https://onlineh5.zhihuishu.com/onlineWeb.html#/studentIndex')

发现要模拟登录,不过幸运的是,智慧树登录不须要验证码,能够直接用selenium进行登录,不然的话就须要拿到cookie再发送请求了:

没有验证码,这一步就很简单,用selenium把用户名和密码填上,而后模拟浏览器去点击登录按钮便可

 

 

 

 能够看到输入用户名这里,有一个id属性,值是 lUsername ,因此能够直接经过id定位用户名输入框,同理密码也是同样:

usrname=browser.find_element_by_id('lUsername')    #定位输入框
password=browser.find_element_by_id('lPassword') usrname.send_keys('XXXXXX')                              #输入本身的用户名和密码
password.send_keys('XXXXXX')

登录按钮:

 

 

 能够看到按钮的class属性为 wall-sub-btn 因此也能够直接定位 而后模拟点击:

signin=browser.find_element_by_class_name('wall-sub-btn').click()

作到这一步就能够直接进入学生主页了,能够看到本身选修的课程:

 

 

下一步就是点开我要上的课:

 

 

 能够看到class属性值为 courseName 直接模拟点击就能够了:

watch=browser.find_element_by_class_name('courseName').click()

可是须要注意的是,在这个语句以前,须要加上一个等待时间,必须等到网页加载完成了以后才能点击,不然有可能根本就找不到这个按钮

等待的方法有不少种,我直接用了最简单暴力的sleep(由于其余的方法不会...)

time.sleep(5) watch=browser.find_element_by_class_name('courseName').click()

等待五秒钟后再点击就行了,不过要是实在网速不行,5秒也是有可能失败的....

以后就会出现一个弹窗:

 

 

 这里必需要把它点掉,也是和以前模拟点击按钮同样的操做

signin=browser.find_element_by_class_name('know').click()

点击完以后,就能够搜集咱们想要的东西了(这里最好也加个sleep,给浏览器一点反应的时间)

首先是videoid,videoid怎么找呢?直接ctrl+F:

 

 

就能够定位到当前视频的videoid了,可是这个路径用以前找id属性或者class属性的话不是很好找,因此使用css选择器的方法 find_element_by_css_selector 定位到这里,

而后再用get_attribute方法获得dataid的值,也就是videoid

复制css选择器:

 

 

能够获得:.video-box > div:nth-child(1)

而后用这个值去定位,而后get参数便可:

videoid=browser.find_element_by_css_selector(".video-box > div:nth-child(1)").get_attribute("dataid")

如今有了videoid,那么lessonid在哪呢?

直接看右边的视频选择栏的代码,咱们能够看到全部的lessonid都整整齐齐的写在这里:

 

 

 因此咱们只须要遍历每个class="lessonItem"的模块,获取lessonid后点击这个视频,再获取这个视频的videoid,这样最关键的两个id咱们就均可以得到了:

classlist=browser.find_elements_by_class_name('lessonItem') for now in classlist: classid=now.get_attribute('id') classtitle=now.find_element_by_class_name("lessonName").text now.click() time.sleep(1) videoid=browser.find_element_by_css_selector(".video-box > div:nth-child(1)").get_attribute("dataid")

这里须要注意的,是第一行和第四行的find方法有略微的不一样,第一行element后面还有一个s,这样能够抓取到到一个列表,不然是选择第一个

而后就能够直接构造post请求发送save2CCoursProgressV2包了

ps:save2CCoursProgressV2包的最后一个参数是毫秒级时间戳,可是time方法得到的是秒级的时间戳,须要转化一下:

import time t = time.time()               #秒级时间戳
T=int(round(t * 1000))    #毫秒级时间戳

post请求(注意这里的url和以前的不同,能够经过分析save2CCoursProgressV2包来得到):

post_url='https://b2cpush.zhihuishu.com/b2cpush/courseDetail/save2CCoursProgressV2' post_data = { 'courseId':    '2068219',                       #courseid能够直接在当前url里面找到
'videoId':videoid, 'lessonId':classid, 'learnTime':'1000', 'chapterName':classtitle, 'sourceType':'3', 'totalTime':'1000', 'studyMode':'1', 'uuid':'XXXXX',                                    #用户id,但不是用户名
'dateFormate':int(round(t * 1000))         #毫秒级时间戳
} r=requests.post(post_url,post_data) print(r.status_code)                            #输出状态码

这样就大功告成了!

0x04 最终代码

from selenium import webdriver import time import requests post_url='https://b2cpush.zhihuishu.com/b2cpush/courseDetail/save2CCoursProgressV2' browser = webdriver. Firefox() browser.get('https://onlineh5.zhihuishu.com/onlineWeb.html#/studentIndex') usrname=browser.find_element_by_id('lUsername') password=browser.find_element_by_id('lPassword') usrname.send_keys('xxxxxx')          #用户名和密码
password.send_keys('xxxxxx') signin=browser.find_element_by_class_name('wall-sub-btn').click() time.sleep(5)                        #停一下 等页面加载完毕
watch=browser.find_element_by_class_name('courseName').click() time.sleep(2) signin=browser.find_element_by_class_name('know').click() time.sleep(2) videoid=browser.find_element_by_css_selector(".video-box > div:nth-child(1)").get_attribute("dataid") classlist=browser.find_elements_by_class_name('lessonItem') for now in classlist: classid=now.get_attribute('id') classtitle=now.find_element_by_class_name("lessonName").text now.click() time.sleep(1) videoid=browser.find_element_by_css_selector(".video-box > div:nth-child(1)").get_attribute("dataid") t = time.time() post_data = { 'courseId':'2068219',                  #能够根据url得到
    'videoId':videoid, 'lessonId':classid, 'learnTime':'1000',                     #设置为足够大
    'chapterName':classtitle, #视频标题 'sourceType':'3', 'totalTime':'1000', 'studyMode':'1', 'uuid':'xxxx',                        #uuid能够经过找其余save2CCoursProgressV2包来得到
    'dateFormate':int(round(t * 1000))         #毫秒级时间戳
 } r=requests.post(post_url,post_data) print(r.status_code)

0x05 总结

这个程序写的仍是比较简陋的,只支持了“兴趣课”,其余的课程因为网页格式不同,应该是不适用的,并且courseId还须要手动看url来得到:

 

 

 uuid也是经过查找save2CCoursProgressV2包获取的,不够智能化自动化,还须要好好打磨

如果学生选修了多门课程,那么在学生界面选择课程的语句也须要稍稍更改了,改为find_elements而不是find_element

不过这都是细节问题了,核心的登陆、收集id信息、发送统计时长都作出来了,也亲测有效:

 

 

如果以为效率不够,能够选择加多线程或者是PhantomJS来提升效率~~

此次学习到了不少selenium的用法,也是受益不浅

 

原文出处:https://www.cnblogs.com/dyhaohaoxuexi/p/12503153.html

相关文章
相关标签/搜索