朋友在学习一个在线课程(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()
网友评论