项目背景

今天一个朋友想下载东方财富网上的2019巴菲特股东大会直播视频,我觉得还挺有趣,之前没有爬取过视频,所以研究了下,用python实现了爬取这个视频。

其实,以现在的带宽网速及wifi普及,我们已经很少去下载视频了,很多视频,都是直接在线观看,但偶尔也有下载的需求,比如下载课程视频,或者下载一些视频后,剪辑合成一些视频,或者下载一些你觉得比较重要有帮助的视频,就像这个例子一样,巴菲特的股东大会视频对很多投资者来说,还是很有价值的。

python抓取直播间的评论信息完整理代码 python爬取直播视频_3c

项目思路

  • 确定相关的url
  • 爬取相应的ts文件
  • 合并ts文件

以下是要爬取的视频网站

url = "http://finance.eastmoney.com/a/201905041112402538.html"

F12打开开发者模式,Netwoking下,选择XHR,当视频播放时,会产生如下的带.ts的url,这实际上将整个视频,分成了众多的小视频片段,任意选择一个,复制网址,打开就会直接下载一个.ts格式的视频小片段,可以直接用播放器播放。所以,我们就是要爬取这些小视频片段,然后合并成一个视频即可。

python抓取直播间的评论信息完整理代码 python爬取直播视频_ide_02

python抓取直播间的评论信息完整理代码 python爬取直播视频_3c_03

 

这样看问题就简单多了,获取或者构造.ts的url,然后去爬取下载视频即可。

分析ts url

比较url_1和url_3,我们会发现,.ts前边是1和14,如果我们看了其他的ts url,会发现就是划分的视频小片段,而且一般划分数量都不少,这里有178个。

比较url_1和url_2,都是在14.ts下的,只不过后边start, end不一样,我们直接去掉 “?”后边的部分,打开前半部分的url后,可以下载整个14片段的视频,相当于这边视频片段14又进行了子划分,我们只需构造问号前半部分的url即可。

再比较url_1和url_3前半部分url,会发现只有“3228861720_780805890_14.ts” 与 “3228861720_1565367120_1.ts”这部分是不一样的,问题简化为,找到或者构造出这部分的内容,发现中间部分很难直接构造。

url_1:
http://1252033264.vod2.myqcloud.com/4697b79evodcq1252033264/3cea53065285890788726506999/
3228861720_780805890_14.ts?start=15163892&end=16534411&type=mpegts

url_2:

http://1252033264.vod2.myqcloud.com/4697b79evodcq1252033264/3cea53065285890788726506999/
3228861720_780805890_14.ts?start=12526252&end=15163891&type=mpegts

url_3:
http://1252033264.vod2.myqcloud.com/4697b79evodcq1252033264/3cea53065285890788726506999/
3228861720_1565367120_1.ts?start=8069900&end=9171203&type=mpegts

这块让我很郁闷,看了网上的帖子后,继续分析Networking, 直到我发现了含有.m3u8的url,我直接打开后,或者打开去掉url后边的参数部分,会下载一个.m3u8的文件。打开文件后,发现里边包含了我们需要字段名,所以构造url的问题也解决了,访问这个网址获取文本内容,正则表达式匹配我们需要的部分,然后就可以构造我们的url了。

http://1252033264.vod2.myqcloud.com/4697b79evodcq1252033264/3cea53065285890788726506999/
playlist.m3u8?t=5f37a52f&exper=0&us=7106fcd370&sign=86432b5694bc6b668690312c790d7d11

python抓取直播间的评论信息完整理代码 python爬取直播视频_ide_04

以下是代码:

# -*- coding:utf-8 -*-
import requests
import re
from urllib.request import urlretrieve
import os
from concurrent.futures import ThreadPoolExecutor
from shutil import rmtree


class DownloadVideo:
    def __init__(self, m3u8_url):
        self.m3u8_url = m3u8_url
        self.ts_url = []
        self.count = 0

    # 分析networking中,可以找到一个m3u8的链接,其包含了所有ts video的url名字
    def get_ts_url(self):
        m3u8_text = requests.get(self.m3u8_url).text
        # 正则表达式获取.ts的字段
        re_list = re.findall(r".*?\.ts", m3u8_text)
        for ts_name in re_list:
            # 拼接出ts的url
            pre_url = re.match(r"(.*?/)[A-Za-z0-9._]+\.m3u8.*", self.m3u8_url).group(1)
            target_url = pre_url + ts_name
            if target_url not in self.ts_url:
                self.ts_url.append(target_url)
        return self.ts_url

    # 下载单个ts文件
    def __download_ts(self, url, i):
        # 创建存放ts的文件夹
        path = os.getcwd() + "/ts_file"
        if not os.path.exists(path):
            os.makedirs(path)
        name = "%03d" % i + ".ts"
        filename = path + "/" + name
        # 开始下载ts
        urlretrieve(url, filename=filename)

    # 多进程下载
    def pool_download(self):
        print("需要下载%d个文件" % len(self.ts_url))
        print('正在下载...所需时间较长,请耐心等待...')
        pool = ThreadPoolExecutor(max_workers=16)
        pool.map(self.__download_ts, self.ts_url, range(1, len(self.ts_url)+1))
        pool.shutdown()
        print("下载完成")

    def merge_ts(self):
        file = os.getcwd() + "\\ts_file\\*.ts"
        target = os.getcwd() + "\\video.ts"
        cmd = "copy /b " + file + " " + target
        try:
            print("开始合并ts文件...")
            os.system(cmd)
            print("完成ts文件合并")
            # 合并完成后,删除其余的ts文件
            rmtree(os.getcwd() + "\\ts_file")
        except Exception as e:
            print(e)

    def execute(self):
        self.get_ts_url()
        print("获取ts url...")
        self.pool_download()
        self.merge_ts()
        print("完成")


if __name__ == "__main__":
    url = "http://1252033264.vod2.myqcloud.com/4697b79evodcq1252033264/3cea53065285890788726506999/playlist.m3u8?"
    download_video = DownloadVideo(url)
    download_video.execute()

几点说明:

  1. 根据ts url下载视频,我们采用了urllib.request模块下的 urlretrieve(url, filename=filename)函数来下载ts视频片段
  2. 在__download_ts中,下载ts前,会在当前工作目录下,创建./ts_file文件夹,用来临时存放下载的ts文件,后期合并后(函数merge_ts),会通过shutil.rmtree来移除整个ts_file文件夹和ts文件。
  3. 我们采用多进程进行下载,使用了concurrent.futures模块中的ThreadPoolExecutor(max_woker=16),以及map(func, iterable)函数,来实现多进程下载。

 

PS: 如果你有疑问,或者意见、建议,欢迎讨论!!!