學堂在線視頻字幕抓取1_分析數據接口


寫在最前:互聯網並非法外之地,爬蟲僅供技術交流

運行環境

  • python 3.7.4
  • requests 2.10.0

爬取目標

  • EDA技術與應用(2020秋)1.1.2 EDA技術概述 教學視頻

分析視頻字幕接口

找接口就只能憑借經驗去network里面翻找,或者借助於瀏覽器調試,沒有過多的技巧。

一、從資源回溯尋找接口

  • 帶有視頻接口的json文件URL分析

    https://www.xuetangx.com/api/v1/lms/service/playurl/7ED5FE6BE6C6DAC39C33DC5901307461/?appid=10000

    跟其他視頻比較,可以得出:

    有一個請求參數,這個參數似乎是固定的,所以不用管。

    7ED5FE6BE6C6DAC39C33DC5901307461是一個路徑變量,不同視頻有着不同的該參數。

  • 帶有字幕接口的json文件URL分析

    https://www.xuetangx.com/api/v1/lms/service/s_t_g_p/

    這個數據是通過POST請求的,數據查看后發現需要一個json對象

    {"c_d":"7ED5FE6BE6C6DAC39C33DC5901307461"}
    

    而且這個c_d與上文視頻的路徑變量一致。

    所以最后得出請求方案:

    c_d = val
    # method: GET
    # 視頻動態URL
    url_video = "https://www.xuetangx.com/api/v1/lms/service/playurl/{}/?appid=10000".format(c_d)
    # method: POST
    # 字幕URL
    url_subtitle = "https://www.xuetangx.com/api/v1/lms/service/s_t_g_p/"
    data = {"c_d":"7ED5FE6BE6C6DAC39C33DC5901307461"}
    
  • 視頻接口和字幕接口URL分析

    上面的兩個json文件直接提供的視頻和字幕的接口URL,所以即使他們的URL還帶了其它的參數,我們也不再需要關心這些。

    可能會擔心的就是鑒權問題,但是我已經嘗試過了,字幕和視頻的接口以及這兩個json文件都不需要專門的頭部信息進行鑒權。

    我們只要找到上面的兩個文件,就可進行視頻字幕的下載。

    所以我們現在需要找到c_d

二、從未知變量回溯尋找接口

  • 帶有c_d(ccid)的json文件

    我們可以在這個文件下的data中的content_info中的media下找到一個ccidc_d相同,所以我們可以把這里獲得的ccid當成變量給下游的URL。

    同時我們還在這個文件下找到了視頻相關附件的鏈接,https://qn-next.xuetangx.com/15679498483925.pptx,同樣不需要鑒權就可以下載。

  • 帶有c_d(ccid)的json文件URL分析

    https://www.xuetangx.com/api/v1/lms/learn/leaf_info/4227236/6195112/?sign=NCIAE08091001906

    我們可以看到這個新的路徑給爬取增加了不少難度,它多出了兩個路徑變量(4227236/6195112)和一個請求參數(sign=NCIAE08091001906`)。

    而且從這里開始就已經需要鑒權了,頭文件得帶上相應的參數才可以進行訪問。

  • 帶有id的json文件

    https://www.xuetangx.com/api/v1/lms/learn/leaf_info/4227236/6195112/?sign=NCIAE08091001906中的6195112是這個文件的leaf_list中每一個json對象的id。我們成功的解決了下游URL的一個變量。

  • 帶有c_d(ccid)的json文件URL分析

    https://www.xuetangx.com/api/v1/lms/learn/course/chapter?cid=4227236&sign=NCIAE08091001906

    很幸運的是,下游URL的兩個未解決變量在這里出現了,經過這個URL,總體的未知變量沒有增多。

    經過兩個路由后,我們最后可以得出這樣的請求方案:

    cid = val1
    sign = val2
    # method: GET
    # 章節動態URL
    url_chapter = "https://www.xuetangx.com/api/v1/lms/learn/course/chapter?cid={}&sign={}".format(cid, sign)
    leaf_id = response(url_chapter)
    # method: GET
    # 小節動態URL
    url_leaf = "https://www.xuetangx.com/api/v1/lms/learn/leaf_info/{}/{}/?sign={}".format(cid, vid, sign)
    

三、回溯到頭再順流而下

  • 未解決的問題

    1. "NCIAE08091001906"到底是什么?cid是課程id嗎?

      我們可以通過退出再登錄,使用其它賬戶來判斷它們是否與用戶身份相關;通過等待一段時間看它們是否改變,判斷是否與時間有關。我們會發現它們既與用戶身份無關也與時間無關

      我們還可以通過瀏覽器的調試模式去判斷這一點。

      最后我們可以得出sign和cid都是課程識別碼。

      雖然你可以在進入這門課程學習后,在頂上的URL找到這兩個參數。但我依舊想更清楚的解釋它們是什么,sign(course_sign)是一門課程的標識,而cid(classroom_id)是一門課程每個學期的標識。這些信息都可以在更高的源頭追溯到。

      但這次我們就先追溯到這里。

    2. 關於鑒權的問題。

      我們在爬蟲的時候需要考慮清楚地告訴對方服務器我們是什么?

      所以我們需要去看瀏覽器為我們生成的請求頭和其它請求條件呢,這我們可以自己搭一個本地服務,去看requests的請求頭和瀏覽器的有什么區別。

      再通過不斷試錯,找到當前請求需要的請求頭和其它請求條件。

      很慶幸的是,學堂在線我們需要補充修改的請求頭參數非常簡單。示例如下:

      • 方式一

        # 這里的代碼請不要嘗試,sessionid我已經安全退出,失去效力。
        # 沒有安全退出的話可以保存兩周,在此期間可以任意爬取。當然這也跟瀏覽器的設置有關。
        headers = { "xtbz": "xt" }
        cookies = { "sessionid": "z3rvy7fpp4tqbc4opmzkq1amlvmqde7d" }
        requests.get("https://www.xuetangx.com/api/v1/lms/learn/leaf_info/4227236/6195112/?sign=NCIAE08091001906",headers=headers,cookies=cookies)
        
      • 方式二

        # 方式一直接帶上cookie是更好的選擇,至少在學堂在線是這樣的。
        headers = { "xtbz": "xt", "cookies": "sessionid=z3rvy7fpp4tqbc4opmzkq1amlvmqde7d" }
        requests.get("https://www.xuetangx.com/api/v1/lms/learn/leaf_info/4227236/6195112/?sign=NCIAE08091001906",headers=headers,cookies=cookies)
        
  • 正式順流而下

    1. 找到sign和cid

      img

    2. 找到cookies

      img

      只需要sessionid就好,其它瀏覽器找cookies自行百度。

    3. 根據sign和cid請求數據

      import json,requests,time
      
      cid = "4227236"
      sign = "NCIAE08091001906"
      
      # 請求頭 僅供參考
      headers = { "xtbz": "xt" }
      cookies = { "sessionid": "z3rvy7fpp4tqbc4opmzkq1amlvmqde7d" }
      
      # 章節信息
      url_chapter = "https://www.xuetangx.com/api/v1/lms/learn/course/chapter?cid={}&sign={}".format(cid, sign)
      time.sleep(0.2)
      chapter = json.loads(requests.get(url_chapter,headers=headers,cookies=cookies).content)
      ## 第一章的第一節的所有小節
      leaf_list = chapter['data']['course_chapter'][0]['section_leaf_list'][0]['leaf_list']
      ## 第一章的第一節的所有視頻小節
      video_leaf_list = list(filter(lambda item:item['leaf_type']==0, leaf_list))
      ## 第一章的第一節的第一個視頻小節的id
      vid = video_leaf_list[0]['id']
      
      # 視頻小節信息
      url_leaf = "https://www.xuetangx.com/api/v1/lms/learn/leaf_info/{}/{}/?sign={}".format(cid, vid, sign)
      time.sleep(0.2)
      video = json.loads(requests.get(url_leaf,headers=headers,cookies=cookies).content)
      
      ## ppt等附件
      url_file = video['data']['content_info']['download'][0]['file_url']
      time.sleep(0.2)
      file = requests.get(url_file).content
      with open('1.pptx','wb') as f:
        f.write(file)
      
      ccid = video['data']['content_info']['media']['ccid']
      ## 視頻
      time.sleep(0.2)
      url_video = json.loads(requests.get("https://www.xuetangx.com/api/v1/lms/service/playurl/{}/?appid=10000".format(ccid)).content)['data']['sources']['quality10'][0]
      time.sleep(0.2)
      content_video = requests.get(url_video).content
      with open('1.mp4','wb') as f:
        f.write(content_video)
      ## 字幕
      time.sleep(0.2)
      url_subtitle = json.loads(requests.post("https://www.xuetangx.com/api/v1/lms/service/s_t_g_p/",data={"c_d": ccid},headers=headers).content)['data'][0]['data']
      time.sleep(0.2)
      content_subtitle = requests.get(url_subtitle).text
      with open('1.txt','w') as f:
        f.write(content_subtitle)
      

寫在最后

上面的代碼主要是提供一個思路,實際只用於抓取EDA技術與應用(2020秋)第一章的第一節的第一個視頻小節,因為我們不可以保證每一門課的第一章的第一節都有視頻小節,也不能保證每一個小節都有附件,每一個視頻都有字幕,爬取其它視頻還要做容錯處理。

如果想一次爬所有視頻也可以實現,用for循環就可以。請記得不要過度頻繁地發送請求,會給服務器造成巨大的壓力,服務器針對此也有很多的反爬手段。

爬取的字幕是json數據,想要變成字幕文件還得做相應處理。

這篇文章還有后續,會繼續完善相應功能。


免責聲明!

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



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