一时心血来潮,想写个小工具来提取B站视频的封面。很多发游戏实况的UP,他视频封面搞一些插画美图啥的,又不放出来,我只好自己提取封面然后自己搜图了。
先尝试用爬虫获取B站的封面。但是B站是有反爬的,分析网页搞半天没啥用。但是我寻思B站这么大的网站肯定一堆人试过爬它,就直接开搜准备照抄,果然找到了一些贴子讲相关内容,不过比较意外的是几乎找不到直接的成品开源代码。总之,可以通过B站的 api 和视频 BV 号来直接爬取视频相关信息:
# 根据BV号获取cid
https://api.bilibili.com/x/player/pagelist?bvid=(bvid)
# 根据BV号和cid获取视频播放列表
https://api.bilibili.com/x/player/playurl?cid=(cid)&qn=(qn)&bvid=(bvid)
# 根据BV号和cid获取aid
https://api.bilibili.com/x/web-interface/view?cid=(cid)&bvid=(bvid)
# 根据av号获取封面
https://api.bilibili.com/x/web-interface/view?aid=(aid)
# 根据BV号获取封面
https://api.bilibili.com/x/web-interface/view?bvid=(bvid)
web-interface/view这个接口信息最全,且支持 aid 和 bvid 双向查询,最终选用了它。
爬取过程中的数据基本上都是 JSON 数据,从返回的 JSON 中只提取我们需要的 title(标题)和 pic(封面图)。
以BV1dNkjBGEgF为例,抓取到的 JSON 数据都有:
{
"code": 0,
"message": "OK",
"ttl": 1,
"data": {
"bvid": "BV1dNkjBGEgF",
"aid": 115925613354775,
"videos": 1,
"tid": 17,
"tid_v2": 2069,
"tname": "",
"tname_v2": "",
"copyright": 1,
"pic": "http://i1.hdslb.com/bfs/archive/4e489460fe371c2e7e9066de2b2f492917aa219a.jpg",
"title": "红警敌人最失败的外交!遇上我最成功的外交!",
"pubdate": 1768966200,
"ctime": 1768884593,
"desc": "红警敌人最失败的外交!遇上我最成功的外交!",
"desc_v2": [
{
"raw_text": "红警敌人最失败的外交!遇上我最成功的外交!",
"type": 1,
"biz_id": 0
}
],
"state": 0,
"duration": 800,
"rights": {
"bp": 0,
"elec": 0,
"download": 1,
"movie": 0,
"pay": 0,
"hd5": 0,
"no_reprint": 1,
"autoplay": 1,
"ugc_pay": 0,
"is_cooperation": 0,
"ugc_pay_preview": 0,
"no_background": 0,
"clean_mode": 0,
"is_stein_gate": 0,
"is_360": 0,
"no_share": 0,
"arc_pay": 0,
"free_watch": 0
},
"owner": {
"mid": 483246073,
"name": "红警魔鬼蓝天",
"face": "https://i1.hdslb.com/bfs/face/09978726cc291d0a4aeff8f2fd6022687012150c.jpg"
},
"stat": {
"aid": 115925613354775,
"view": 75554,
"danmaku": 582,
"reply": 341,
"favorite": 308,
"coin": 973,
"share": 45,
"now_rank": 0,
"his_rank": 0,
"like": 5108,
"dislike": 0,
"evaluation": "",
"vt": 0
},
"argue_info": {
"argue_msg": "",
"argue_type": 0,
"argue_link": ""
},
"dynamic": "",
"cid": 35497774466,
"dimension": {
"width": 1920,
"height": 1080,
"rotate": 0
},
"premiere": null,
"teenage_mode": 1,
"is_chargeable_season": false,
"is_story": false,
"is_upower_exclusive": false,
"is_upower_play": false,
"is_upower_preview": false,
"enable_vt": 0,
"vt_display": "",
"is_upower_exclusive_with_qa": false,
"no_cache": false,
"pages": [
{
"cid": 35497774466,
"page": 1,
"from": "vupload",
"part": "红警敌人最失败的外交!遇上我最成功的外交!",
"duration": 800,
"vid": "",
"weblink": "",
"dimension": {
"width": 1920,
"height": 1080,
"rotate": 0
},
"first_frame": "http://i1.hdslb.com/bfs/storyff/_000037hhw5j982wfv22s8usrng6ifhb_firsti.jpg",
"ctime": 1768884593
}
],
"subtitle": {
"allow_submit": false,
"list": []
},
"is_season_display": false,
"user_garb": {
"url_image_ani_cut": ""
},
"honor_reply": {},
"like_icon": "",
"need_jump_bv": false,
"disable_show_up_info": false,
"is_story_play": 1,
"is_view_self": false
}
}
各字段都代表什么还是比较清楚的,不赘述了。
至于视频链接,我们只需要通过正则匹配,
抓取 BV 号:/BV([a-zA-Z0-9]+)/,
抓取 AV 号:/av([0-9]+)/i(忽略大小写)。
前端 <img> 标签要加上 referrerpolicy="no-referrer",因为B站服务器会检查 HTTP 请求头中的 Referer,发现是外站请求就拦截,导致提取到的图片无法正常显示。
而手机分享出来的 b23.tv 链接本身不包含视频 ID,它是一个 302 重定向链接,如果不处理,正则会匹配失败。解决方法是,使用 wp_remote_head(只请求头信息,不下载网页,速度快),获取 HTTP 响应头里的 Location 字段,拿到真实的跳转长链接后,再丢给上面的正则去处理。
最后举个栗子:
//如果是短链,先获取跳转后的真实地址
if (strpos($url, 'b23.tv')) {
$url = get_real_url($url);
}
//正则提取视频ID
if (preg_match('/BV.../', $url)) {
//调用B站api
$api = "https://api.bilibili.com/x/web-interface/view?bvid=" . $bvid;
$data = request($api);
//返回 JSON 给前端
echo json_encode([
'title' => $data['title'],
'pic' => $data['pic']
]);
}
本来想用 Python 做个微服务,后来一想 WordPress 本身就是 PHP 框架,功能足够,不必舍近求远,反正我们的代码不涉及网站数据库,很安全了。成品就部署在我的博客里。
Comments NOTHING