首页
社区
课程
招聘
[分享]利用抓包实现下载51cto视频
发表于: 2019-6-27 03:09 20127

[分享]利用抓包实现下载51cto视频

2019-6-27 03:09
20127

最近在尝试下载51cto中整个课程视频,难点在于它的获取m3u8文件的网址采用js加密,通过抓包分析js文件,得到加密方式,然后使用python下载。下载ts文件使用了多线程。

下载视频的步骤主要有以下几点:

1:找到网页的视频索引文件m3u8
2:分析m3u8文件,找到各个ts文件下载地址,将ts文件下载到临时文件中
3:使用ffmpeg工具将ts文件合并成视频(需要安装ffmpeg软件,请自行百度下载)
4:删除临时文件

主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import requests, os
from bs4 import BeautifulSoup
from urllib.request import urlretrieve
import hashlib
import threading
import queue
def get_html(url):
    headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
    'Host':'edu.51cto.com',
    }
    r = requests.get(url, headers=headers)
    r.encoding = 'utf-8'
    return r.text
def parse_couse_id(url):
    content = get_html(url)
    bsobj = BeautifulSoup(content, 'lxml')
    lesson_lists = bsobj.find("div", {"class":"lessonList"}).ul.findAll("li", {"class":"lesson"})
    lesson_id_list = []
    name_list = []
    for item in lesson_lists:
        name = item.a["title"]
        lesson_id = item.a["href"].split('?id=')[1]
        lesson_id_list.append(lesson_id)
        name_list.append(name)
    return lesson_id_list, name_list
def get_m3u8_url(vid):
    sign = "eDu_51Cto_siyuanTlw"
    auth_md5 = (vid + sign).encode("utf-8")
    # print(auth_md5)
    auth_str = hashlib.md5(auth_md5).hexdigest()
    # print(auth_str)
    headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3',
    'Host':'edu.51cto.com',
    'Refer':'http://edu.51cto.com/center/course/lesson/index?id='+vid,
    'Accept':'application/json, text/javascript, */*; q=0.01',
    'Accept-Encoding':'gzip, deflate, sdch',
    'Accept-Language':'zh-CN,zh;q=0.8',
    'Connection':'keep-alive',
    }
    parms = {
        'type':'course',
        'lesson_type':'course',
        'sign':'auth_str',
        'lesson_id':vid,
    }
    url = 'http://edu.51cto.com/center/player/play/get-lesson-info'
    r = requests.get(url, headers=headers, params=parms)
    r.encoding = 'utf-8'
    # print(r.json())
    return (r.json()['dispatch'][0]['url'])
def m3u8_list(m3u8_url, title):
    urlretrieve(m3u8_url, title)
    m3u8_url_list = []
    with open(title, 'rt') as fout:
        # print(fout.read())
        flag = False
        for f in fout.readlines():
            if f.startswith('#EXTINF'):
                flag = True
            elif flag:
                # print(f)
                m3u8_url_list.append(f[0:-1])
                flag = False
            else:
                pass
    if os.path.exists(title):
        os.remove(title)
    return m3u8_url_list
def down(url, path):
    def Schedule(a,b,c):
        '''''
        a:已经下载的数据块
        b:数据块的大小
        c:远程文件的大小
        '''
        per = 100.0 * a * b / c
        if per > 100 :
            per = 100
        print('%.2f%%' % per)
    urlretrieve(url, path)
def down_ts(m3u8_url_list, path='', title=''):
    for index, url in enumerate(m3u8_url_list):
        # print(url)
        path_name = path + '\\' + str(index) + '.ts'
        down(url, path_name)
        print('[%d/%d]\t\tNow Downing %s_%d.ts' %(index, len(m3u8_url_list)-1, title, index))
'''
# 使用多线程下载视频
def down_ts(m3u8_url_list, path='', title=''):
    url_lists = []
    for index, url in enumerate(m3u8_url_list):
        # print(url)
        path_name = path + '\\' + str(index) + '.ts'
        url_lists.append((url, path_name))
    def consumer(url_lists, url_que):
        for urls in url_lists:
            url_que.put(urls)
    def producter(url_que):
        while True:
            urls = url_que.get()
            urlretrieve(*urls)
            print("Downing:", urls[1])
            url_que.task_done()
    url_que = queue.Queue()
    for n in range(4):
        down_thread = threading.Thread(target=producter, args=(url_que,))
        down_thread.start()
    consumer(url_lists, url_que)
    url_que.join()
'''
def write_confile(path, ts_len):
    txt = ''
    for i in range(ts_len):
        txt += 'file \'%s/%d.ts\'\n' %(path, i)
    with open('confile.txt', 'w') as fout:
        fout.write(txt[0:-1])
def delete_file(file_path):
    if os.path.exists(file_path):
        os.remove(file_path)
    else:
        print('ERROR : Path not exist [%s]' %file_path)
# 合并ts视频文件
def merge_ts_video(title, v_type='.mp4'):
    cmd = 'ffmpeg -f concat -i confile.txt -c copy %s%s' %(title, v_type)
    print(cmd)
    p = subprocess.Popen(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = p.communicate()
    print(str(out, 'utf-8'))
    print(str(err, 'utf-8'))
def usage():
    print()
    print('Usage: down_51cto_video.py url')
    print('Example: down_51cto_video.py http://edu.51cto.com/center/course/lesson/index?id=98717')
    print()
def main(url):
    lesson_id_list, name_list = parse_couse_id(url)
    for index, url_id in enumerate(lesson_id_list):
        m3u8_url = get_m3u8_url(url_id)
        v_name = name_list[index]    
        m3u8_url_list = m3u8_list(m3u8_url, url_id)
        ts_len = len(m3u8_url_list)
        if os.path.exists(url_id):
            os.removedirs(url_id)
        os.mkdir(url_id)
        down_ts(m3u8_url_list, path=url_id, title=test_name)
        write_confile(url_id, ts_len)
        try:
            merge_ts_video(url_id)
            os.rename(url_id+'.mp4', title+'.mp4')
            # 删除临时文件
            delList = os.listdir(v_key)
            for item in delList:
                del_path = os.path.join(v_key, item)
                os.remove(del_path)
            os.removedirs(v_key)
            os.remove('confile.txt')
            os.remove(title+'.m3u8')
        except Exception as e:
            print(e)
if __name__ == '__main__':
    try:
        url = sys.argv[1]
    except Exception as e:
        # print(e)
        usage()
    else:
        main(url)

目前上面的代码只能下载免费的视频!!!
小提示:该代码也能下载51cto中的付费视频,不过需要自己修改其中的代码,大家可以尝试一下!!!


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (12)
雪    币: 603
活跃值: (4223)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
http://www.qmaile.com/
2019-6-27 03:13
0
雪    币: 11314
活跃值: (2907)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
Powerful
2019-6-27 08:37
0
雪    币: 165
活跃值: (1486)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
ts格式不用引什么库,把顺序搞清楚用copy /b ......就搞完了
2019-6-27 12:39
0
雪    币: 4839
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
这个可以有
2019-6-27 17:19
0
雪    币: 396
活跃值: (54)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
干这种事还是py方便
2019-7-2 04:05
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
第一份代码看不见,正在学这方面的东西,求代码,麻烦发一份在1775885969@qq.com,非常感谢!
2019-7-11 00:49
0
雪    币: 4839
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
其实就是研究其加密原理
从中找突破口
然后获得免费资源
2019-7-12 10:24
0
雪    币: 236
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
顶一个
2019-7-12 11:08
0
雪    币: 603
活跃值: (4223)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
python的大佬进来
https://edu.51cto.com/course/14417.html?source=so


谁能爬取这个
记得分享下
2019-7-18 10:34
0
雪    币: 256
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
这个合成的MP4应该是不能正常播放的
2019-9-4 17:12
0
雪    币: 27
活跃值: (1698)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
代码有问题,看了一遍,基本是不可行的,fake
这样下载下来的无法播放
应该下载也有问题
2019-10-2 23:59
0
雪    币: 27
活跃值: (1698)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
核心的关于m3u8 key的处理根本没有,应该是抄的很久以前的代码
2019-10-3 00:00
0
游客
登录 | 注册 方可回帖
返回
//