Fun

使用爬虫来批量下载明星的大量(公开的)音视频资源

Batch download audio-visual resources

用了很久Python,没怎么写过爬虫,最近看见我收藏夹里的一些视频悄无声息的失效了,歌也版权限定了,我决定学下爬虫,把音视频资源都下载下来自己保存。

发现其实挺简单的,想个办法批量的下载,然后就和数据挖掘比赛里清洗数据的操作差不多了。

1.找来源

显然,第一步是找到音视频资源的来源。根据平时“网上冲浪”的经验,我瞄准了三个主要来源:

  • B站:视频质量比较高,但相对来说视频资源比较松散,需要手动的去找一找。
  • Youtube:playlist的存在使得视频资源很集中,质量也不错,但下载视频存在网络传输上的难度
  • 网易音乐:完美的专辑下载源

和一些备用的来源:

  • 优酷,土豆,搜狐等:好视频都被勤劳的搬运工搬得差不多了,很难再找到意外惊喜
  • 贴吧:视频质量方差极大,好的贼好,差的贼差,需要活人(而不是机器)好好筛选
  • QQ音乐:恰好没有我的目标明星的版权。。。

2.下数据

找到了来源,就可以开始下载了,由于我下载的量有点大,所以这一步难度也是挺大的。

2.1. 爬B站

B站的单个视频都可以用you-get来批量下载,但由于我对前端和网站实在不太了解,竟然找不到批量提取视频链接的方法。。。

再加上视频资源比较松散,于是我使用了一个独特的“活人+电脑”的爬取方法:

  1. 用一个脚本来监测剪贴板,一旦剪贴板发生改变,就把剪贴板内容保存下来。
  2. 我亲自去B站上搜视频,看到优质的,就“右键,复制链接地址”
  3. 这样就获得了一大堆链接地址了,再用那个脚本一个一个的把you-get拼上地址写成一个sh,丢在电脑上没日没夜的下载。

于是就遇到了两个问题:

  • 下着下着,b站‘av’改‘bv’了,you-get识别不出来了(当时优秀的开发者还没有填上这个bug,现在已经好了)
  • 下着下着就没速度了,然后逐渐卡死。

使用暴力的方法来基本解决了这俩问题:

  • 看了半天you-get源码,也没完全看懂,于是暴力拆包,埋了个函数在那个库里,强行跳过url检验,就能在我的python脚本里直接调用,并且用线程池来控制它并行下载了
  • 发现B站是有反爬机制的,于是我开个线程用来当监工,要是它发现别的线程卡住了,就sleep几分钟,重新再下,靠监工能解决一半的问题,另一半可能就是我IP真被BAN了,于是用电脑发个邮件给我,我收到邮件以后去重启路由器,锵锵,又获得一个新IP啦!(电脑自动重启路由器老是配置不上,频次也不太高,干脆手动了)

2.2. 爬youtube

因为youtube的playlist可以一起下,同时某个用户的所有视频也算是一个playlist,再加上youtube-dl能筛选视频下载,所以用它下载是很方便的。

然后就是“有趣”的网络问题了。

虽然我能浏览youtube,但是(警告:开始疯狂套娃)

  • 但是,我的“高速网络”提供商是按流量计费的,大规模的爬取有点扛不住。
  • 但是,我还有一个小厂家的外国服务器,却与国内的连接很慢。
  • 但是,我还有一个阿里云ECS(学生机),却只有1M的上行带宽
  • 但是,我还有一个阿里云OSS(曾经试图用来做云盘,请求数量一多就太贵了就一直闲置)

针对这样的情况,我巧妙设计了一个方案:

  1. 用小厂家的外国服务器批量下载youtube视频
  2. 用阿里云的ECS(服务器)从外国服务器用sftp来7d-24h的持续下载
  3. 用阿里云的ECS从内网用ossutil把视频传给阿里OSS(对象存储)
  4. 我从OSS上把视频下下来

这样我的带宽基本上就能跑满了

2.3. 爬网易音乐

我需要下载270+张专辑,但我只找到了单首歌或者歌单可以用pymusic-dl下载。于是我需要先找到专辑的链接,再从专辑里找到歌曲的链接,所幸网易音乐很友好,从page source/frame source里很快找到了我需要的这两级链接。

很容易就拿到了所有歌曲的链接,接下来只需要一首一首下载就好了。

(有个插曲,pymusic-dl的命令行壳子好像有点影响批量下载的效率,我是拆包埋了个函数,开五个线程一起下的)

3. 洗数据

这是耗时最多的一步了。首先浏览一下这些音视频,发现他们:

  • 各种文件命名琳琅满目,时间,节目,曲名,全都按照原视频up主的习惯排列。
  • 由于搬运工的辛勤劳作,文件有大量重复。
  • 文件没有分类

开始洗:

3.1. 删除冗余视频0

把所有视频的时长和文件大小拿出来作比较,把完全一致,也就是直接搬运的视频给删掉。 (其实还剩了不少冗余的,后面会逐步继续清洗)

3.2. 批量修改文件名

为了方便整理和保存,我希望所有文件都是 [放送日期] 内容 歌名 (节目名) (备注)的格式。

我写了个脚本用来“推荐”文件的标准命名,然后人工审核:

  1. 从专辑和其他的一些地方获得明星的歌曲列表,常去的节目列表,以及明星的别称列表。
  2. 使用各种花式的正则表达式来匹配文件名中的这些内容,以及文件名中的视频拍摄或放送日期。
  3. 匹配后将其整理好,作为参考文件名,保存到一个文本文件里,同时,旧文件名也保存下来。
  4. 然后靠活人(我),左右新旧对照的一个个的审核文件名,再加上VScode的列操作等方便快捷的编辑方式,把修改文件名的任务变得尽量容易。

但改完我还是快瞎了。

3.3. 制作目录

比较容易,只需要写个脚本,匹配[放送日期] 内容 歌名 (节目名) (备注)的文件名,然后用我熟悉的opencv检查一下视频的各种属性,就能形成一个表头是年 月 日 分类 名称 文件类型 文件大小 时长 分辨率 fps/bps 备注 下载时间的目录来。

3.4. 删除冗余视频1

根据目录的提示,关于同一天的,同场节目或同个场景的“高风险”视频被单拎出来,人为的观看排除冗余。

3.5. 分类

根据目录提示,和肉眼观看,将所有视频进行分类

3.6. 补充数据

经过这么多的人眼识别,我对视频基本上有了一个印象,这时候就可以到那些备用数据源来挖掘一些优秀的数据。

4. 制作维护脚本

由于随着时间发展,逐渐有更多的视频出土,所以制作一个维护脚本是比较有必要的。在命名脚本和目录生成脚本的基础上,很容易能写出一个针对小批量视频的具有下载,命名建议,命名确认,目录更新,目录发布,数据增量上传等功能的脚本来。

5. 成果

这是截止1585750753.0044966(时间戳,我也不知道具体是多久)的经过严格清洗后的成果统计:

{
    "time": 1585750753.0044966,
    "video": {
        "count": 1108,
        "total_size": 55653407429,
        "total_duration": 397824.19662208564
    },
    "audio": {
        "count_song": 4643,
        "count_album": 278,
        "total_size": 15685054034,
        "total_duration": 981340.6563265321,
    }
}

(duration和size的单位分别是字节和秒)

其实还是有一些冗余视频片段,但一方面视频之间可以互为补充(比如有一个低清的电影和几个高清的电影片段),另一方面我也没有那么大的算力来支持它做关键帧抽取比对。

6. 声明

(我的音视频都留着自己看)

(假装这里有一段充分体现我遵纪守法的申明)

本文总字数: 2851