如何用Matlab一键下载B站高清视频(下)

0 前言

N久以前分享了Matlab中利用现成的B站视频解析网站 贝贝bilibili-B站视频下载 来爬取视频,还没看到这个分享的朋友,可以戳下方的推文链接

推文链接: 如何用Matlab一键下载B站高清视频(上)

  • 利用模拟鼠标键盘的方法有一定的局限性,即不能干涉程序与当前交互的浏览器界面

  • 利用MatlabIE浏览器交互的方法只能适用于windows系统上,linux等系统并不适用;再一个问题是Matlab创建IE服务器对象时可能会报如下报错:

image

对于这个问题,目前解决的方法之一是需要重置Internet Explorer:在Internet选项中的高级选项卡中选择 Internet Explorer重置设置,然后重启电脑,无脑般地就可以用了。

当然有的电脑按照上面的设置后依然会报错,具体原因未知,目前我还没有找到其他解决的方法,不知道大家有没有好的解决方案呢?

接下来......

惨不忍睹的大型翻车现场:

image

限制反爬,添加了验证码!一个好的解析网站就该这样。只不过对于投机取巧的程序而言只能......

image

鉴于前面的两种方法都不太令人满意,何止是不太满意,根本是无法满意!那今天我们就介绍一种更常规的方法,利用API提供的接口,只需发送一个合理的请求就能得到b站视频的下载链接。

截止目前,网络上有很多爬取B站视频的帖子,大多数是利用Python的,有少部分是利用Matlab的。那为何我们还要再分一杯羹呢?原因有二:

  1. 此处的目的是学习和分享如何利用MATLAB爬取B站视频

  2. 已有的MATLAB爬取得到的B站视频是非1080P的,并且只针对单P视频

这篇推文主要的参考资料是:https://github.com/5ime/bilidown一款开源的基于Python的爬取B站视频工具,我们参考的B站视频API接口就来源于此。

另外,已有方法《MATLAB爬虫程序,批量获取文本、图片和视频(中)》《MATLAB爬虫程序,批量获取文本、图片和视频(下)》爬取下来的B站视频是非1080P的,下面是我们的方法和该方法下载同一视频的信息比较:(源视频地址:https://www.bilibili.com/video/BV11f4y1r7e8)

已有方法 我们的方法
image
image
image
image

当然,我们的主要需求是下载多P视频,这就迫使我们得造这个轮子了!友情提示:不到万不得已,不要造轮子!

下面就让我们一起看看造轮子的过程吧!

1 API接口

要想下载b站上某个UP主的视频,那首先得进入到他/她的空间里,我们以"尚硅谷" 为例,其空间地址是 https://space.bilibili.com/302417610/video,其中的数字编号 302417610 是每一个UP主独一无二的,称为mid

在下载视频时往往需要两个重要的参数:bvidcidbvid(bv号)我们在 如何用Matlab一键下载B站高清视频(上)

中已经见过,即单个视频或者多p视频都对应着一个bvid,而对于多p视频则有不同的cid号来决定具体的视频地址,当然单个视频仅有一个cid号。

下面说明通过UP主的mid号来得到bvid

简单抓一次包吧,我们再谷歌浏览器中输入空间地址,按F12后,查看Network选项卡,仔细看看每一个响应,最后会定位到下面的信息:

image

我们将响应的URL用新的窗口打开就会得到我们需要的bvid号的重要信息了:

image

总结一下

指定up主的空间信息接口:

https://api.bilibili.com/x/space/arc/search?mid=指定UP主的独一无二的编号&ps=30&tid=0&pn=UP主空间的页码总数

有了midpn这两个参数就可以查询cid号了, 因为cid不会在正常访问时查看到,于是也需要调用特定接口来获取:

https://api.bilibili.com/x/web-interface/view

接口使用很简单,只有一个参数:aid(av号)或 bvid(bv号)二选一

例如:

https://api.bilibili.com/x/web-interface/view?bvid=BV1XJ411X7Ud

通过上面的接口,我们就能获得cid

有了midcid参数,我们就可以构建playurl接口请求了:

https://api.bilibili.com/x/player/playurl?bvid=bv号&cid=cid号&qn=80&otype=json

其中,参数qn表示视频的质量,值设定为80可以获得高清1080p的视频,但需要注意的是我们请求时得提供cookie

否则下载的视频是480p的非高清

注意:已有方法中提供的playurl接口为

https://api.bilibili.com/x/player/playurl?bvid=bv号&cid=cid号&qn=120&fourk=1

这两个接口在结尾处有一定的区别,大家可以都尝试做一做。

2 实现过程

接下来还是聊下代码实现吧,这个是根据我的需求来写的:

  • 下载UP主所有发布的视频(这类UP一般是没有多p视频的,如罗翔说刑法

  • 下载UP主的某个教学视频(这类UP一般是有多p视频的,如尚硅谷Web前端HTML5&CSS3初学者零基础入门全套完整版

bilibiliVideoMain.m --- 脚本入口

setWebOptions.m --- 设置请求头函数

bilibiliVideoInfo.m --- 由bv或者av号获取cid号的功能实现

getSinglePageVideo.m --- 实现下载单p视频

getMultiPageVideo.m --- 实现下载多p视频

对于setWebOptions.m函数,源码如下:

<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="matlab" cid="n77" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 1.105em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(13, 17, 23); position: relative !important; border-radius: 3px; color: rgb(201, 209, 217); padding: 0.4em 0px 0px; margin: 0px 0px 20px; width: inherit; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> function options = setWebOptions(cookie)
randNum = randperm(23);
randNum(randNum>=10);
options = weboptions('Timeout', randNum(1));
options.UserAgent = ...
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36';
options.RequestMethod = 'get';
options.HeaderFields = {'Cookie', cookie; 'Origin', 'https://www.bilibili.com'; ...
'accept', 'application/json'; ...
'accept-encoding', 'gzip, deflate, br'; ...
'accept-language', 'en-US,en;q=0.8'; ...
'content-type', 'application/json'; ...
'Referer', 'https://www.bilibili.com'};
end % end functions</pre>

需要传入cookie参数,获取的方法也很简单,祖传方法===>首先登陆B站,然后按键F12刷新,在请求头中找到cookie,如下图,然后把那划红线的一坨东西粘贴过来即可。

image

bilibiliVideoInfo.m

<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="matlab" cid="n81" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 1.105em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(13, 17, 23); position: relative !important; border-radius: 3px; color: rgb(201, 209, 217); padding: 0.4em 0px 0px; margin: 0px 0px 20px; width: inherit; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> function mainInfo = bilibiliVideoInfo(mid, numberOfPage)
%bilibiliVideoInfo return some main data fou user from bilibili.com
% Inputs:
% mid string (mid = '517327498';)
% numberOfPage int (numberOfPage = 1|2|3|, ..., |n-1|n);
% Outputs:
% mainInfo struct (mainInfo.title|mainInfo.facePic|mainInfo.bvid)
% Usage:
% >>mainInfo = bilibiliVideoInfo('517327498', 5);
% mainInfo =
% title: {143×1 cell}
% facePic: {143×1 cell}
% bvid: {143×1 cell}
%
% -------------------------------------------
% Set request options
% -------------------------------------------
randNum = randperm(23);
randNum(randNum>=10);
options = weboptions('Timeout', randNum(1));
options.UserAgent = ...
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36';
options.RequestMethod = 'get';

% -------------------------------------------
% Set crawl mainly data
% -------------------------------------------
mainInfo.title = [];
mainInfo.facePic = [];
mainInfo.bvid = [];

for page = 1 : numberOfPage
disp([' | Climbing to the ', num2str(page), 'th page']);
pause(0.2)
% API
dataUrl = ['https://api.bilibili.com/x/space/arc/search?', 'mid=', mid, '&ps=30&tid=0&pn=', num2str(page)];
res = webread(dataUrl, options);
allInfo = res.data.list.vlist;
mainInfo.title = [mainInfo.title; {allInfo.title}'];
mainInfo.facePic = [mainInfo.facePic; {allInfo.pic}'];
mainInfo.bvid = [mainInfo.bvid; {allInfo.bvid}'];
end % end for

% mainInfo.facePic = regexprep(mainInfo.facePic, '//(.+?)', 'http://$1');
disp(' ')
end % end function
</pre>

getSinglePageVideo.m

<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="matlab" cid="n83" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 1.105em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(13, 17, 23); position: relative !important; border-radius: 3px; color: rgb(201, 209, 217); padding: 0.4em 0px 0px; margin: 0px 0px 20px; width: inherit; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> function getSinglePageVideo(mid, numberOfPage, numberOfVideoStart, saveDir, cookie)
%getSingleVideo Get a single page video
% Inputs:
% mid string (mid = '517327498';)
% numberOfPage int (numberOfPage = 1|2|3|, ..., |n-1|n);
% numberOfVideoStart int ---Video starting number
% saveDir string
% cookie string
% Outputs:
% none
%
%% Set request options
options = setWebOptions(cookie);

%% Set parameters
apiUrl = 'https://api.bilibili.com/x/player/playurl?';

%%
if ~exist(saveDir, 'dir')
mkdir(saveDir);
end

if ~exist([saveDir, '/facepics'], 'dir')
mkdir([saveDir, '/facepics']);
end
%% Get video's url by cid and bvd number
mainInfo = bilibiliVideoInfo(mid, numberOfPage);
numberOfBvid = length(mainInfo.bvid);

%
pat = '(?:|)|(?:\)|(?::)|(?:*)|(?:?)|(?:")|(?:<)|(?:>)|(?:/)';

for ii = numberOfVideoStart : numberOfBvid
bvid = mainInfo.bvid{ii};
title = regexprep(mainInfo.title{ii}, pat, '');
res = webread(['https://api.bilibili.com/x/web-interface/view?bvid=', bvid]);
% Get cid number
cid = num2str(res.data.pages.cid);
res = webread([apiUrl, 'bvid=', bvid, '&cid=', cid, '&qn=80&otype=json']);
% Get player url
playUrl = res.data.durl.url;
% Save face pictures
disp([' |-->Saving ', num2str(ii), 'th|', num2str(numberOfBvid), ' picture']);
faceImg = imread(mainInfo.facePic{ii});
imwrite(faceImg, [saveDir, '/facepics/', num2str(ii), '
', title, '.png']);
% Save video
disp([' |-->Saving ', num2str(ii), 'th|', num2str(numberOfBvid), ' video']);
websave([saveDir, '/', num2str(ii), '_', title, '.mp4'], playUrl, options);
disp(' ------Save finished!----------')
disp(' ')
end % end for
end % end function</pre>

鉴于篇幅原因,getMultiPageVideo.m的源代码在这里就不展示了,想要获得获得文中所有代码的朋友门,请在公众号后台回复:bilibili视频(下) (注:和 bilibli视频(上)有区别,抱歉,这里b后面少了个i,纠正一下)

需要说明的一点是:对于以标题作为视频名称保存需要注意文件名是否合法的问题,因此代码中用正则表达式pat = '(?:\|)|(?:\\)|(?:\:)|(?:\*)|(?:\?)|(?:")|(?:<)|(?:>)|(?:/)';title = regexprep(mainInfo.title{ii}, pat, '_'); 将标题进行了过滤。

最后调用脚本bilibiliVideoMain.m源码如下:

<pre class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded" spellcheck="false" lang="matlab" cid="n87" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 1.105em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(13, 17, 23); position: relative !important; border-radius: 3px; color: rgb(201, 209, 217); padding: 0.4em 0px 0px; margin: 0px 0px 20px; width: inherit; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> % ------------------------------------------------
% Main script for obtain video from bilibili
% ------------------------------------------------
clear
clc
close all

isSendMail = true;
cookie = ' '; % 需要将cookie粘贴到这里

% ------------------------------------------------
% Obtain single page video
% ------------------------------------------------
mid = ' ';
numberOfPage = [];
numberOfVideoStart = [];
saveDir = '保存视频的文件夹名称';
getSinglePageVideo(mid, numberOfPage, numberOfVideoStart, saveDir, cookie);

% ------------------------------------------------
% Obtain multi-page video
% ------------------------------------------------
bvid = 'BV1Cy4y117vt';
numberOfVideoStart = 1;
saveDir = 'shangguigu_zidingyi';
getMultiPageVideo(bvid, numberOfVideoStart, saveDir, cookie);</pre>

通过这种方法我爬取了一部分罗老师说刑法的视频,也爬取了有关尚硅谷的前端教学视频

image
image
罗老师说刑法部分视频 尚硅谷微信小程序部分视频

整个过程最让我受益的是爬取下来的这些视频,看了其中一部分,特别是罗老师说刑法,除了罗老师的讲课有趣味,更重要的是让人增加一些法律、刑法方面的知识,知法懂法才能更好的做一个守法的合格公民嘛

感兴趣的朋友们可以拿到代码后亲自运行下载视频对照一下,用这种方法下载的视频原格式是flv1080p的,这与 贝贝bilibili-B站视频下载 网站解析出来的.mp4格式的1080p稍微有些区别,比如文件大小,但视频质量上两者差别不大。

今天的分享就到这里了,有问题请朋友们后台发消息留言,祝大家生活愉快!

参考文献

https://github.com/5ime/bilidown

https://mp.weixin.qq.com/s/3EaVntbM07J6PpLScYnb5w

https://mp.weixin.qq.com/s/SIYlNO3ZR82VF1D4nYzIgQ

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容