智慧樹刷網課python腳本


0x00 寫在前面

疫情期間肯定有很多小伙伴需要上網課,但是有些網課我們感覺十分的雞肋,自己不感興趣,又必須要學

所以我寫了這個刷網課的小程序,一方面是鍛煉自己的爬蟲技術,另一方面也給同學們節約寶貴的時間

幾點說明:

1.此程序只供學習交流,請勿用於商業用途

2.當前只支持“興趣課”的刷課,其他類型的課程還不支持

3.程序尚不完善,但是原理相通,舉一反三,歡迎交流

0x01 環境准備

python3.7+requests庫+selenium庫+火狐瀏覽器

python3.7和requests庫的安裝不必贅述 下面來講一下selenium庫,這也是我第一次用這個庫,記錄一下

因為目標網站是經過js渲染的,不使用selenium庫很難抓取想要的數據,selenium庫可以模擬瀏覽器進行操作,同時可以配合各大主流瀏覽器,十分好用

安裝:

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的用法,也是受益匪淺

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM