记录一次关于python爬取视频的过程

zhi前两天突然间脑子抽风想要用python来爬一下视频网站,获取视频。一开始无从下手,在网上搜了很多相关的博客,然而也并未找到一个理想的解决方案,但是好在最终能够将视频网站的视频给爬下来,尽管吃相难看了点。特此将整个过程以及思考给记录下来。

我的目标是爬取腾讯视频的视频内容,在网上搜索出来的结果是利用第三方解析网站对视频进行解析,然后在爬取,这是最简单的解决方案。于是乎也就照搬照做了。详细过程如下:

打开:http://jx.618g.com/?url=这个第三方解析网站,将待解析的视频url加在后面就行了。如:https://jx.618g.com/?url=https://v.qq.com/x/cover/c949qjcugx9a7gh.html

这个时候对https://jx.618g.com/?url=https://v.qq.com/x/cover/c949qjcugx9a7gh.html进行抓包。会发现有很多的.ts的文件,后来由查看了很多相关的博客知道了原来这些.ts的文件就是我们要抓去的对象。关于详细的介绍.m3u8以及.ts文件推荐一篇博客给大家,如果不懂的话可以去看看https://blog.csdn.net/a33445621/article/details/80377424

仔细的查看下这些ts文件发现并没有需要携带的其他参数直接访问其url便可实现下载,当然了下载下来的也只是一小段视频片段。

 按照网上的一种做法直接请求这些url将其下载下来然后在合并成一个完整的视频片段。这种做法的代码我先贴出来以供参考。

 1 import time
 2 import requests
 3 
 4 
 5 def loder(i):
 6     """直接请求ts文件的url然后在写入到本地"""
 7     url = 'https://doubanzyv3.tyswmp.com:888/2018/12/12/UEtWtHwTc0UniIDQ/out%03d.ts' % i  # %03d 左边补0方式
 8     html = requests.get(url).content
 9 
10     with open(r"D:\txsp_test\%s%03d.ts" % ("a", i), "wb") as f:
11         f.write(html)
12 
13 
14 if __name__ == "__main__":
15     pool.map(loder, range(400))
16     pool.join()
17     pool.close()

 这里你可以使用多进程或者多线程来进行优化,我在这里就不将代码贴出来了。通过这种方式下载下来的ts文件很可能会因为网络的问题出现漏下,少下的情况,为此我也是尝试了各种方法都没有找到一个最优解。我尝试了一个方法是为进程加锁,以信号量的形式对文件进行下载,确保ts文件的完整,同时也能保证异步、并发。但是这样做的话就相当于开启了400个进程。你的内存一定会溢出。(有兴趣的可以试下线程锁)

 1 import time
 2 import requests
 3 
 4 
 5 def loder(i, sem):
 6     """直接请求ts文件的url然后在写入到本地"""
 7     sem.acquire()  # 获取钥匙
 8     url = 'https://doubanzyv3.tyswmp.com:888/2018/12/12/UEtWtHwTc0UniIDQ/out%03d.ts' % i  # %03d 左边补0方式
 9     html = requests.get(url).content
10 
11     with open(r"D:\txsp_test\%s%03d.ts" % ("a", i), "wb") as f:
12         f.write(html)
13     sem.release()
14 
15 
16 if __name__ == "__main__":
17     start_time = time.time()
18     print(start_time)
19     sem = Semaphore(5)  # 规定锁的个数
20     # pool = Pool(5)
21     p_l = []
22     for i in range(400):
23         p = Process(target=loder, args=(i, sem))
24         p.start()
25         p_l.append(p)
26     for i in p_l:
27         i.join()
28     print(time.time()-start_time)

 然后在对下载好的文件进行合成

 1 file_dir = r"D:\txsp_test"  # 文件的保存路径
 2 new_file = u"%s\out.ts" % file_dir  # 合并之后的视频
 3 f = open(new_file, 'wb+')  # 二进制文件写操作
 4 
 5 for i in range(0, 338):
 6     file_path = r"D:\txsp_test\%s%03d.ts" % ("a", i)  # 视频片段名称
 7     print(file_path)
 8     for line in open(file_path, "rb"):
 9         f.write(line)
10     f.flush()
11 
12 f.close()

 ok上面的是一种做法,很显然可以将视频的下载与合并发在一起。我也不贴出来了。接下来就是另外一种做法。



这种做法相对来说更加的合理,就是先找到.m3u8的文件。

然后向其发送get请求,得到的响应结果就是一段段的.ts集合。(不知道我在说什么的请看上面的博客,或者自己动手requests.get()试下)这时可以通过正则匹配出.ts的文件然后在下载下来。最后合并成完整的视频。

代码如下:(采用了多线程对.ts文件进行下载)

 1 import re
 2 import os
 3 import shutil
 4 from concurrent.futures import ThreadPoolExecutor
 5 from urllib.request import urlretrieve
 6 
 7 import requests
 8 from scrapy import Selector
 9 
10 
11 class VideoDownLoader(object):
12     def __init__(self, url):
13         self.api = 'https://jx.618g.com'
14         self.get_url = 'https://jx.618g.com/?url=' + url
15         self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
16                         'Chrome/63.0.3239.132 Safari/537.36'}
17 
18         self.thread_num = 32
19         self.i = 0
20         html = self.get_page(self.get_url)
21         if html:
22             self.parse_page(html)
23 
24     def get_page(self, get_url):
25         """获取网页"""
26         try:
27             print('正在请求目标网页....', get_url)
28             response = requests.get(get_url, headers=self.headers)
29             if response.status_code == 200:
30                 # print(response.text)
31                 print('请求目标网页完成....\n准备解析....')
32                 self.headers['referer'] = get_url
33                 return response.text
34         except Exception:
35             print('请求目标网页失败,请检查错误重试')
36             return None
37 
38     def parse_page(self, html):
39         """解析网页"""
40         print('目标信息正在解析........')
41         selector = Selector(text=html)
42         self.title = selector.xpath("//head/title/text()").extract_first()  # 获取标题(电影名称)
43         print(self.title)
44         m3u8_url = selector.xpath("//div[@id='a1']/iframe/@src").extract_first()[14:]  # 获取视频地址(m3u8)
45         self.ts_list = self.get_ts(m3u8_url)  # 得到一个包含ts文件的列表
46         print('解析完成,下载ts文件.........')
47         self.pool()
48 
49     def get_ts(self, m3u8_url):
50         """解析m3u8文件获取ts文件"""
51         try:
52             response = requests.get(m3u8_url, headers=self.headers)
53             html = response.text
54             print('获取ts文件成功,准备提取信息')
55             ret_list = re.findall("(out.*?ts)+", html)  # 匹配.ts的字段
56             ts_list = []
57             for ret in ret_list:
58                 ts_url = m3u8_url[:-13] + ret
59                 ts_list.append(ts_url)
60             return ts_list
61         except Exception:
62             print('缓存文件请求错误1,请检查错误')
63 
64     def pool(self):
65         print('经计算需要下载%d个文件' % len(self.ts_list))
66         if self.title not in os.listdir():
67             os.mkdir(r"D:" + self.title)  # 新建视频目录
68         print('正在下载...所需时间较长,请耐心等待..')
69         # 开启多进程下载
70         pool = pool = ThreadPoolExecutor(max_workers=16)  # 多线程下载
71         pool.map(self.save_ts, self.ts_list)
72         pool.shutdown()
73         print('下载完成')
74         self.ts_to_mp4()
75 
76     def ts_to_mp4(self):
77         print('ts文件正在进行转录mp4......')
78         str = 'copy /b ' + self.title+'\*.ts ' + self.title + '.mp4'  # copy /b 命令
79         os.system(str)
80         filename = self.title + '.mp4'
81         if os.path.isfile(filename):
82             print('转换完成,祝你观影愉快')
83             shutil.rmtree(self.title)
84 
85     def save_ts(self, ts_list):
86         print(self.title)
87         self.i += 1
88         print('当前进度%d' % self.i)
89         urlretrieve(url=ts_list, filename=r"D:" + self.title + '\{}'.format(ts_list[-9:]))
90 
91 
92 if __name__ == '__main__':
93     url = "https://v.qq.com/x/cover/c949qjcugx9a7gh.html"   # 视频url
94     video_down_loader = VideoDownLoader(url)

 运行代码,喝一杯caffe等待10来分钟视频就自动下载好了。但是这里依然会存在这下载下来的.ts文件不完整的情况,博客写到这里我脑海里面又想到了一种解决方法,明天试一试把。哦对了,关于视频的文件下载多线程,多进程我分别都试过,两者的下载速度区别并不大,因为这涉及到了网络的请求以及文件的读写等IO操作。所以采用多线程/进程没啥区别,建议还是用多线程来。结果如下所示:

1 # 下载400个.ts文件测试线程、进程的性能
2 
3 # >>> multiprocessing ——> 196.01457595825195  默认开启4个进程
4 # >>> multiprocessing ——> 196.01457595825195  强制开启16个进程,实际上5个
5 # >>> threading ——> 174.57704424858093  默认开启4个线程
6 # >>> threading ——> 202.30066895484924  默认开启40个线程(网络卡顿)
7 # >>> threading ——> 155.5946135520935  默认开启16个线程

 测试的结果表名,线程开启的速度确实比进程开启速度快。然并卵!

猜你喜欢

转载自www.cnblogs.com/pontoon/p/10306826.html