zoukankan      html  css  js  c++  java
  • 记录一次关于python爬取视频的过程

    前两天突然间脑子抽风想要用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:	xsp_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:	xsp_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:	xsp_test"  # 文件的保存路径
     2 new_file = u"%sout.ts" % file_dir  # 合并之后的视频
     3 f = open(new_file, 'wb+')  # 二进制文件写操作
     4 
     5 for i in range(0, 338):
     6     file_path = r"D:	xsp_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('请求目标网页完成....
    准备解析....')
    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)

     运行代码,喝一杯coffee等待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个线程

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

  • 相关阅读:
    sql中not exists的用法
    jsp中target="_blank"的用法
    jsp 运用 session 登录输出
    jsp留言板
    编写JSP 实现用户登录并判断用户或密码
    jsp打印九九乘法表
    jsp输出当前时间
    jsp输出金字塔
    jsp输出5的阶乘
    jdbc练习3
  • 原文地址:https://www.cnblogs.com/pontoon/p/10306826.html
Copyright © 2011-2022 走看看