今天看啥  ›  专栏  ›  sunnnnnnnnnny

替朋友下载网课和刷课

sunnnnnnnnnny  · 简书  ·  · 2020-06-18 22:15

朋友在学习一个在线课程( http://www.zyyrcw.com/ ,我帮她将这个课程所有视频离线下载到本地,并替她将刷完了要求的课时。

开发环境

  • vscode 编辑器
  • anaconda3 python3环境
  • ffmpeg 用来读取视频文件的长度
  • Firefox开放版 用来查看前后端交互的http请求

爬取所有的视频地址

课程列表
#-*- coding:utf-8 -*-
# 2020年6月18日21:31
import time
import json
import requests,re,sys,pickle
from bs4 import BeautifulSoup
import pandas as pd


#全局变量
#reqeusts会话对象
s = requests.Session()

headers = {
    'User-Agent':"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0"
}

#cookies字符串
cookie_str ="xxx"
cookies = {}

#将cookies字符串转化成字典格式
def load_cookies():
    global cookie_str,cookies
    for item in cookie_str.split(';'):
        sep_index = item.find('=') 
        cookies[item[:sep_index]] =item[sep_index+1:]

data = []
#获取课程视频的下载链接
def get_video(title,url):
    r = s.get(url,headers = headers,cookies=cookies)
    if r.status_code ==  200:
        #通过正则表达式找到页面中的视频播放链接
        video_url = re.findall(r'"(http://www.zyyrcw.com/static/upfile/subject/.*?)"',r.text)[0]
        print(video_url)
        #将视频的名称,页面链接和下载链接添加到data中
        data.append({
            'title':title,
            'url':url,
            'donwload_url':video_url
        })



def spider():
    global s,cookie_str,cookies,headers
    url = 'http://www.zyyrcw.com/member/Train'
    #加载cookie
    load_cookies()
    #课程列表7页,page_size=8
    for i in range(0,56,8):
        #课程列表
        url = 'http://www.zyyrcw.com/category/product/6?p=%d' % i
        r = s.get(url,headers = headers,cookies=cookies)
        if r.status_code ==  200:
            #使用bs库提取出每节课的名字和链接
            soup = BeautifulSoup(r.text,'lxml')
            ul = soup.findAll('ul',class_='details-page-02')[0]
            for li in ul.findAll('li'):
                print(li.a['title'],li.a['href']) 
                #获取课程视频的下载链接
                get_video(li.a['title'],li.a['href'])
    #将结果保存为文件
    pickle.dump(data,open('data.db','wb'))

main()

下载所有的视频到本地

#-*- coding:utf-8 -*-
import requests
import pickle

headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0'
}

#打印出进度条
def process_bar(percent, start_str='', end_str='', total_length=0):
    bar = '\r' + start_str + ' {:0>4.1f}%|'.format(percent*100) + end_str
    print(bar, end='', flush=False)

#下载视频,video_url为视频的播放地址,filename为保存的文件路径
def download(video_url,filename):
    with open(filename,'wb') as f:
        #下载视频文件
        r = requests.get(url = video_url, headers=headers,stream=True)
        content_length = int(r.headers['Content-Length'])

        download_bytes = 0
        with open(filename, "wb") as f:
            for chunk in r.iter_content(chunk_size=4096):
                if chunk:
                    f.write(chunk)
                    download_bytes += len(chunk)
                    percent = float(download_bytes / content_length)
                    #print(download_bytes,content_length)
                    process_bar(percent, start_str=filename, end_str='100%', total_length=50)
        print('\n')
        


def main():
    data = pickle.load(open('data.db','rb'))
    count = 1
    for item in data:
        #print(item)
        url = item['donwload_url']
        filename = '小儿推拿\\%d.%s%s' % (count, item['title'],url[url.rfind('.'):])
        download(item['donwload_url'],filename)

        count += 1

main()

下载器的效果


image.png

刷时长

这个课需要在线时间达到要求的时长才能参加最后的考试,通过观察可以发现在线时长是通过一个post请求更新的。
页面中有这个一段js。

//触发页面关闭事件时候提交数据
//每隔三分钟提交一次数据
$(function () {
    var ntime, tm, tt;
    var view_id = $('#view_id').val();
    setInterval(function () {
        ntime = $('#time').val();
        tm = ntime.split('.');
        tt = Number(tm[0]);

        if (tt % 180 == 0 && tt != 0) {
            $.ajax({
                type: "Post",
                url: "/category/log_time",
                data: { "time": 180, 'id': view_id },
                error: function () { },
                success: function (data, textStatus) { }
            });
        };
    }, 1000);
    //触发页面关闭事件时候提交数据
    window.onbeforeunload = function (event) {
        var time_length = $('#time').val() % 180;//提交3分钟以内的剩余时间
        var view_id = $('#view_id').val();
        if (time_length > 0) {
            $.ajax({
                type: "Post",
                url: "/category/log_time",
                data: { "time": time_length, 'id': view_id },
                error: function () { },
                success: function (data, textStatus) { }
            });
        };
        //return '请保存好学习笔记!'; 
    };
});

这段js脚本的意思很简单,就是在视频播放的过程中,每隔3分钟会向后台发送一个post请求,当关闭页面时,会向发现剩下不到3分钟的时长。
可以使用requests来模拟这个过程,来实现刷时长的功能。
但是如何获取视频的长度,可以使用ffmpeg来实现,首先在ffmpeg的官网上下载安装包,里面有三个可执行文件,如下图所示。


image.png

可以使用下面的命令的来获取视频的长度,单位为秒

ffprobe -i "xxx.mp4" -show_entries format=duration -v quiet -of csv="p=0"

代码如下

#-*- coding:utf-8 -*-
import time
import json,os
import requests,re,sys,pickle

#全局变量
#headers_str中含有cookie,为用户的登录信息
headers_str = """Host: www.zyyrcw.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Origin: http://www.zyyrcw.com
Connection: keep-alive
Referer: http://www.zyyrcw.com/category/show_view/149
Cookie: xxx
Pragma: no-cache
Cache-Control: no-cache"""

headers = {}
#headers字符串解析出字典类型
for line in headers_str.strip().split('\n'):
    k,v = line.strip().split(': ')
    headers[k.strip()] = v.strip()
print(headers)

#使用ffprobe获取视频的长度
def get_video_length(input_video):
    cmd = 'ffprobe -i "%s" -show_entries format=duration -v quiet -of csv="p=0"' % input_video
    output =os.popen(cmd,'r')
    output = output.read()
    return str(output).strip()

#提交观看时长,id_为视频的id,video_len为视频的长度
def post_login_time(id_,video_len):
    url = 'http://www.zyyrcw.com/category/log_time'
    video_len = int(float(video_len))
    #提交的次数
    count = video_len // 180 
    #每次提交3分钟
    for i in range(count):
        data = {
            'time':"180",
            'id':id_
        }
        r = requests.post(url,data = data,headers=headers)
        print(r)
        
    #获取剩下的时长
    t = video_len % 180
    if t:
        data = {
            'time':str(t),
            'id':id_
        }
        r = requests.post(url,data = data,headers=headers)
        print(r)
#主函数
def main():
    i = 1
    for item in pickle.load(open('data.db','rb')):
        title = item['title']
        print(title)
        download_url = item['donwload_url']
        url  =item['url']
        id_ = url[url.rfind('/')+1:]
        #视频的本地路径
        filename = '小儿推拿\\%d.%s.%s' % (i, title, download_url[download_url.rfind('.')+1:])
        #获取视频的长度
        video_len = get_video_length(filename)
        #提交观看时长
        post_login_time(id_,video_len)
        i+=1
main()


参考资料




原文地址:访问原文地址
快照地址: 访问文章快照