利用Python制作一个MOOC公开课下载器
作者:Charles的皮卡丘 发布时间:2022-03-13 19:46:17
标签:Python,MOOC,公开课,下载
导语
记得很久以前写过一些中国大学MOOC上的视频下载器,不过好像都已经年久失修了。正好最近有需要,所以重新写了一个,顺便上来分享一波,寒假大家也可以用它来下载点课程内卷一下:
废话不多说,让我们愉快地开始吧~
开发工具
Python版本:3.7.8
相关模块:
DecryptLogin模块;
tqdm模块;
click模块;
argparse模块;
以及一些python自带的模块。
环境搭建
安装Python并添加到环境变量,pip安装需要的相关模块即可。
先睹为快
运行方式:
python moocdl.py --url 课程链接
效果如下:
moocdl
随便挑的一个课程测试的,结果是m3u8格式的,所以下载起来有点慢。默认会把所有的课件这些东西也一起下载下来放到对应的目录。
原理简介
首先,我们需要先模拟登录中国大学MOOC,这样才能下载对应的课程资料,这里借助公众号之前开源的DecryptLogin包就好啦:
'''登录'''
def login(self, username, password):
lg = login.Login()
infos_return, session = lg.icourse163(username, password)
return infos_return, session
接着,我们简单讲解一下如何下载对应课程里的资料。首先,我们需要获得课程相关的基本资料,随便点开个课程主页就可以发现直接在返回的页面里就有:
提取我们需要的课程信息的代码实现如下:
# 从课程主页面获取信息
url = url.replace('learn/', 'course/')
response = self.session.get(url)
term_id = re.findall(r'termId : "(\d+)"', response.text)[0]
course_name = ' - '.join(re.findall(r'name:"(.+)"', response.text))
course_name = self.filterBadCharacter(course_name)
course_id = re.findall(r'https?://www.icourse163.org/(course|learn)/\w+-(\d+)', url)[0]
print(f'从课程主页面获取的信息如下:\n\t[课程名]: {course_name}, [课程ID]: {course_name}, [TID]: {term_id}')
接着利用这些信息来爬取对应的资源列表:
# 获取资源列表
resource_list = []
data = {
'tid': term_id,
'mob-token': self.infos_return['results']['mob-token'],
}
response = self.session.post('https://www.icourse163.org/mob/course/courseLearn/v1', data=data)
course_info = response.json()
file_types = [1, 3, 4]
for chapter_num, chapter in enumerate(course_info.get('results', {}).get('termDto', {}).get('chapters', [])):
for lesson_num, lesson in enumerate(chapter.get('lessons', [])) if chapter.get('lessons') is not None else []:
for unit_num, unit in enumerate(lesson.get('units', [])):
if unit['contentType'] not in file_types: continue
savedir = course_name
self.checkdir(savedir)
for item in [self.filterBadCharacter(chapter['name']), self.filterBadCharacter(lesson['name']), self.filterBadCharacter(unit['name'])]:
savedir = os.path.join(savedir, item)
self.checkdir(savedir)
if unit['contentType'] == file_types[0]:
savename = self.filterBadCharacter(unit['name']) + '.mp4'
resource_list.append({
'savedir': savedir,
'savename': savename,
'type': 'video',
'contentId': unit['contentId'],
'id': unit['id'],
})
elif unit['contentType'] == file_types[1]:
savename = self.filterBadCharacter(unit['name']) + '.pdf'
resource_list.append({
'savedir': savedir,
'savename': savename,
'type': 'pdf',
'contentId': unit['contentId'],
'id': unit['id'],
})
elif unit['contentType'] == file_types[2]:
if unit.get('jsonContent'):
json_content = eval(unit['jsonContent'])
savename = self.filterBadCharacter(json_content['fileName'])
resource_list.append({
'savedir': savedir,
'savename': savename,
'type': 'rich_text',
'jsonContent': json_content,
})
print(f'成功获得资源列表, 数量为{len(resource_list)}')
最后根据资源类型解析下载即可:
# 下载对应资源
pbar = tqdm(resource_list)
for resource in pbar:
pbar.set_description(f'downloading {resource["savename"]}')
# --下载视频
if resource['type'] == 'video':
data = {
'bizType': '1',
'mob-token': self.infos_return['results']['mob-token'],
'bizId': resource['id'],
'contentType': '1',
}
while True:
response = self.session.post('https://www.icourse163.org/mob/j/v1/mobileResourceRpcBean.getResourceToken.rpc', data=data)
if response.json()['results'] is not None: break
time.sleep(0.5 + random.random())
signature = response.json()['results']['videoSignDto']['signature']
data = {
'enVersion': '1',
'clientType': '2',
'mob-token': self.infos_return['results']['mob-token'],
'signature': signature,
'videoId': resource['contentId'],
}
response = self.session.post('https://vod.study.163.com/mob/api/v1/vod/videoByNative', data=data)
# ----下载视频
videos = response.json()['results']['videoInfo']['videos']
resolutions, video_url = [3, 2, 1], None
for resolution in resolutions:
for video in videos:
if video['quality'] == resolution:
video_url = video["videoUrl"]
break
if video_url is not None: break
if '.m3u8' in video_url:
self.m3u8download({
'download_url': video_url,
'savedir': resource['savedir'],
'savename': resource['savename'],
})
else:
self.defaultdownload({
'download_url': video_url,
'savedir': resource['savedir'],
'savename': resource['savename'],
})
# ----下载字幕
srt_info = response.json()['results']['videoInfo']['srtCaptions']
if srt_info:
for srt_item in srt_info:
srt_name = os.path.splitext(resource['savename'])[0] + '_' + srt_item['languageCode'] + '.srt'
srt_url = srt_item['url']
response = self.session.get(srt_url)
fp = open(os.path.join(resource['savedir'], srt_name), 'wb')
fp.write(response.content)
fp.close()
# --下载PDF
elif resource['type'] == 'pdf':
data = {
't': '3',
'cid': resource['contentId'],
'unitId': resource['id'],
'mob-token': self.infos_return['results']['mob-token'],
}
response = self.session.post('http://www.icourse163.org/mob/course/learn/v1', data=data)
pdf_url = response.json()['results']['learnInfo']['textOrigUrl']
self.defaultdownload({
'download_url': pdf_url,
'savedir': resource['savedir'],
'savename': resource['savename'],
})
# --下载富文本
elif resource['type'] == 'rich_text':
download_url = 'http://www.icourse163.org/mob/course/attachment.htm?' + urlencode(resource['jsonContent'])
self.defaultdownload({
'download_url': download_url,
'savedir': resource['savedir'],
'savename': resource['savename'],
})
ok,大功告成啦,写的有点简略,因为晚上还有点其他事。大家可以自己在手机端抓包试试,很简单的~
来源:https://zhuanlan.zhihu.com/p/465060428
0
投稿
猜你喜欢
- 相信大家使用MySQL都有过重装的经历,要是重装MySQL基本都是在最后一步通不过,除非重装操作系统,究其原因就是系统里的注册表没有删除干净
- 一、绑定方法1.对象的绑定方法首先我们明确一个知识点,凡是类中的方法或函数,默认情况下都是绑定给对象使用的。下面,我们通过实例,来慢慢解析绑
- JavaScript是运行在客户端的脚本,因此一般是不能够设置Session的,因为Session是运行在服务器端的。而cookie是运行在
- 前言NumPy(Numerical Python)是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自
- 1、简述asp的6个内置对象和其功能答案 : session ,server ,response,request,objectcontext
- [Python标准库]decimal——定点数和浮点数的数学运算 &n
- MySQL 8.0.28引入新功能MySQL 8.0.28开始,新增一个特性,支持监控统计并限制各个连接(会话)的内存消耗,避免大量用户连接
- 对于大多数朋友而言,爬虫绝对是学习 python 的最好的起手和入门方式。因为爬虫思维模式固定,编程模式也相对简单,一般在细节处理上积累一些
- 图表库下面的类库可以让你很简单就能创建复杂的图表和图片。当然,它们需要GD库的支持。pChart - 一个可以创建统计图的库。Libchar
- 目标学习读取视频,显示视频和保存视频。学习从相机捕捉并显示它。你将学习以下功能:cv.VideoCapture(),cv.VideoWrit
- Python判断变量是否已经定义是一个非常重要的功能,本文就来简述这一功能的实现方法。其实Python中有很多方法可以实现判断一个变量是否已
- 随着电子邮件的广泛使用,垃圾邮件也日益增多,对用户造成了很大的困扰。因此,开发一个能够自动分类和过滤垃圾邮件的程序就显得非常重要。本篇文章将
- 准备工作下载python,本文以python3.6为例。python3.6下载地址:python3下载地址,选择合适的版本安装。安装成功后,
- 在进行文本分析、提取关键词时,新闻评论等文本通常是中英文及其他语言的混杂,若不加处理直接分析,结果往往差强人意。下面对中英文文本进行分离做一
- PHP xpath() 函数定义和用法xpath()函数运行对 XML 文档的 XPath 查询。如果成功,该函数返回 SimpleXMLE
- 一 接口介绍如果说gorountine和channel是支撑起Go语言的并发模型的基石,让Go语言在如今集群化与多核化的时代成为一道亮丽的风
- 背景项目中经常使用别人维护的模块,在git中使用子模块的功能能够大大提高开发效率。使用子模块后,不必负责子模块的维护,只需要在必要的时候同步
- 首先,先确认一下你的字段值是不是乱码,如果是,按照以下方法:我的字段值是来自于一个geojson字符串,我在对它解析时做了如下处理:prop
- 目录赋值语句直接赋值:增量赋值: 链式赋值: 多重赋值:语法糖:基本输入:input()函数:eval()函数:&nbs
- 1、MSSQL2000 SELECT 表名 = case when a.colorder=1 then d.name else '&