智慧樹刷網課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

相關文章
相關標籤/搜索