2022年10月15日,由于wuziqian211的粉丝数回升到2000,让wuziqian211非常开心,因此wuziqian211发布了一条动态,这条动态包括一张含所有粉丝的头像和昵称的图片。
那么,我们怎么生成这样子的图片呢?这篇文章就教您如何生成含所有粉丝的列表的图片。
如果您的粉丝数超过了1000,wuziqian211不建议您用下面的方法生成包含所有粉丝的图片。
这篇文章适合技术爱好者、程序员阅读。若您遇到任何问题,可以让wuziqian211教您一步步操作。
准备工作
您应该要预先安装Node.js (建议您下载长期维护版 )。
在登录了B站账号 的浏览器中,打开B站任意页面,按下F12键,在新窗口上方选择“应用”,在左侧点击“存储”部分中“Cookie”左边的箭头,点击下面的B站网址,在右侧表格的“名称”一栏中找到“SESSDATA”与“bili_jct”,分别双击它们右边的“值”,复制下来。
打开Node.js,您应该会看到一个命令行窗口。在这个窗口里输入代码const headers = { Cookie: 'SESSDATA=
SESSDATA的值 ; bili_jct=
bili_jct的值 ', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' };
,便于在后续操作中使用您账号的登录信息。
例:假如SESSDATA的值 为abcdef12%2C1678901234%2C56789*bc
,bili_jct的值 为0123456789abcdef0123456789abcdef
,那么就输入代码:
const headers = { Cookie : 'SESSDATA=abcdef12%2C1678901234%2C56789*bc; bili_jct=0123456789abcdef0123456789abcdef' , 'User-Agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' };
特别注意:请不要把您刚刚复制的“SESSDATA”“bili_jct”中的任何一个值告诉任何人!它们的值是您的账号的登录信息,与账号、密码的作用相似,别人可能会利用这些值来登录您的账号。
第一步 获取所有粉丝的列表
B站官方给我们提供的获取指定用户的粉丝列表的API是https://api.bilibili.com/x/relation/followers ,请求方式是GET。
这个API需要您提供有效的Cookie,最多只能获取最近关注您的1000名粉丝的列表 。
主要的URL参数包括:
参数名
内容
必要性
备注
vmid
目标用户UID
必要
ps
每页项数
非必要
默认为50,且最多为50
pn
页码
非必要
默认为1,其他用户仅可查看前5页
如果这个API被正确调用,那么会得到像下面这样的JSON回复(仅作为示例,一些项已经省略):
{ "code" : 0 , "message" : "0" , "data" : { "list" : [ { "mid" : 1 , "attribute" : 6 , "mtime" : 1678901234 , "tag" : [ -10 ] , "special" : 1 , "uname" : "粉丝1的昵称" , "face" : "https://i0.hdslb.com/bfs/face/xxx.jpg" , "sign" : "粉丝1的签名" , "face_nft" : 0 , "official_verify" : { "type" : -1 , "desc" : "" } , "vip" : { "vipStatus" : 1 , } , } , { } , ] , "total" : 2000 } }
我们就先来尝试获取一下自己粉丝列表的第1页吧。
下面是wuziqian211写的代码,记得要在顶层或者异步(async)函数中运行 ,在非异步函数中运行会报错,后面wuziqian211写的所有代码也需要在顶层或异步函数运行。
(await (await fetch ('https://api.bilibili.com/x/relation/followers?vmid=425503913&ps=50&pn=1' , { headers })).json ()).data .list ;
运行上面的代码后,正常情况下会显示一个含很多对象(object)的数组(array)。
我们可以在上面的代码的基础上稍作修改,来获取前20页的粉丝列表。
let followers = []; for (let i = 1 ; i <= 20 ; i++) { followers = followers.concat ((await (await fetch (`https://api.bilibili.com/x/relation/followers?vmid=425503913&ps=50&pn=${i} ` , { headers })).json ()).data .list ); }
这样,“followers”变量就存储了最多1000名粉丝的列表。
那不在这个列表里的粉丝该怎么获取呢?目前,出于安全目的,B站采取了一些措施,使用户无法通过常规手段获取所有粉丝的列表,所以不在刚刚得到的粉丝列表里的粉丝就没办法直接获取到了。
当然,如果您在没有超过1000粉丝的时候就保存了自己所有粉丝的列表,那么您可以将之前的列表与现在的列表合并,记得去除重复项。
for (const f of oldFollowers) { if (!followers.find (t => t.mid === f.mid )) followers.push (f); }
但是,合并后的列表里的粉丝现在不一定仍在关注您,所以要移除没有关注您的用户。下面的代码需要分别查询自己与每个用户的关系,可能会执行很长时间 。
let realFollowers = [];for (let i = 0 ; i < followers.length ; i++) { const rjson = await (await fetch (`https://api.bilibili.com/x/space/acc/relation?mid=${followers[i].mid} ` , { headers })).json (); if (rjson.data .be_relation .attribute !== 0 ) realFollowers.push (followers[i]); }
第二步 获取所有粉丝的详细信息、粉丝数(可选)
目前“realFollowers”变量虽然存储了所有粉丝的信息,但是这个信息不够详细。
获取多个用户的详细信息的API是https://api.vc.bilibili.com/account/v1/user/cards ,请求方式是GET,这个API执行一次可以获取最多50个用户的信息。
主要URL参数包括:
参数名
内容
必要性
备注
uids
目标用户的UID列表
必要
每个成员间用,
分隔,最多50个成员
如果这个API被正确调用,那么会得到像下面这样的JSON回复(仅作为示例,一些项已经省略):
{ "code" : 0 , "msg" : "" , "message" : "" , "data" : [ { "mid" : 1 , "name" : "用户1的昵称" , "face" : "https://i0.hdslb.com/bfs/face/xxx.jpg" , "sign" : "用户1的签名" , "vip" : { "status" : 1 , } , "pendant" : { "pid" : 0 , "name" : "" , "image" : "" , "image_enhance" : "" , } , "official" : { "role" : 0 , "title" : "" , "desc" : "" , "type" : -1 , } , "face_nft" : 0 , "is_senior_member" : 0 } , { } , ] }
获取用户关系状态数的API是https://api.bilibili.com/x/relation/stat ,请求方式是GET。
主要URL参数包括:
参数名
内容
必要性
vmid
目标用户UID
必要
如果这个API被正确调用,那么会得到像下面这样的JSON回复(仅作为示例,一些项已经省略):
{ "code" : 0 , "message" : "0" , "data" : { "mid" : 425503913 , "following" : 2000 , "follower" : 2000 } }
于是我们就可以写出下面的代码:
let followersWithoutInfo = followers.map (f => f.mid ); let info = [];while (followersWithoutInfo.length ) { info = info.concat ((await (await fetch (`https://api.vc.bilibili.com/account/v1/user/cards?uids=${followersWithoutInfo.slice(0 , 50 ).join(',' )} ` , { headers })).json ()).data ); followersWithoutInfo = followersWithoutInfo.slice (50 ); } for (let i = 0 ; i < info.length ; i++) { info[i].follower = (await (await fetch (`https://api.bilibili.com/x/relation/stat?vmid=${info[i].mid} ` , { headers })).json ()).data .follower ; }
这样,“info”变量就存储了所有粉丝的信息与粉丝数。
第三步 生成图片
我们可以根据自己的喜好,选择生成什么类型的文件。下面的代码可以生成HTML文件,界面类似于wuziqian211的动态里的图片。
const encodeHTML = str => typeof str === 'string' ? str.replace (/&/g , '&' ).replace (/</g , '<' ).replace (/>/g , '>' ).replace (/"/g , '"' ).replace (/ (?= )|(?<= ) |^ | $/gm , ' ' ).replace (/\n/g , '<br />' ) : '' ;const html = info.map (u => `<span class="face-wrap${u.pendant?.image ? ' has-frame' : '' } "><img class="face" src="${u.face} " referrerpolicy="no-referrer" />${u.pendant?.pid ? `<img class="face-frame" src="${u.pendant.image_enhance || u.pendant.image} " referrerpolicy="no-referrer" />` : '' } ${u.official.type === 0 ? '<img class="face-icon" src="https://api.wuziqian211.top/assets/personal.svg" />' : u.official.type === 1 ? '<img class="face-icon" src="https://api.wuziqian211.top/assets/business.svg" />' : u.vip.status ? '<img class="face-icon" src="https://api.wuziqian211.top/assets/big-vip.svg" />' : '' } </span> ${encodeHTML(u.name)} ` ).join ('<br />\n' );const content = `<style> * { font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-size: 20px; font-weight: bold; overflow-wrap: break-word; text-align: justify; transition: all 0.5s; } span.face-wrap { display: inline-block; position: relative; } img { vertical-align: middle; } img.face { border-radius: 50%; height: 60px; } span.face-wrap.has-frame img.face { height: 51px; padding: 19.5px; } span.face-wrap.has-frame img.face-frame { height: 90px; left: 0; position: absolute; top: 0; } span.face-wrap img.face-icon { bottom: 0; height: 18px; position: absolute; right: 0; } span.face-wrap.has-frame img.face-icon { bottom: 18px; right: 18px; } </style> ${html} ` ;fs.writeFileSync ('followers.html' , content);
如果您打开这个文件,浏览器显示的是一行一个粉丝。如果我们想让浏览器显示像一个粉丝紧跟着另一个粉丝这样的效果,只需要将上面代码中第2行代码后面的'<br />\n'
替换成''
就可以了。
我们如何将整个网页转换成图片呢?我们可以在浏览器中按下F12键,然后按下Ctrl+Shift+P,输入“screenshot”,再选择“截取完整尺寸的屏幕截图”,并选择保存图片的位置,就可以保存一张包括所有粉丝的图片了。
总结
生成自己的所有粉丝列表的图片看似很难,实际上是很简单的。如果您展开下面的代码,并直接复制,再进行一些适当的修改,也可以生成您自己的粉丝列表的图片。
点击查看代码
const headers = { Cookie : 'SESSDATA=abcdef12%2C1678901234%2C56789*bc; bili_jct=0123456789abcdef0123456789abcdef' , 'User-Agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' }; const encodeHTML = str => typeof str === 'string' ? str.replace (/&/g , '&' ).replace (/</g , '<' ).replace (/>/g , '>' ).replace (/"/g , '"' ).replace (/ (?= )|(?<= ) |^ | $/gm , ' ' ).replace (/\n/g , '<br />' ) : '' ;let followers = []; for (let i = 1 ; i <= 20 ; i++) { followers = followers.concat ((await (await fetch (`https://api.bilibili.com/x/relation/followers?vmid=425503913&ps=50&pn=${i} ` , { headers })).json ()).data .list ); } let realFollowers = [];for (let i = 0 ; i < followers.length ; i++) { const rjson = await (await fetch (`https://api.bilibili.com/x/space/acc/relation?mid=${followers[i].mid} ` , { headers })).json (); if (rjson.data .be_relation .attribute !== 0 ) realFollowers.push (followers[i]); } let followersWithoutInfo = followers.map (f => f.mid ); let info = [];while (followersWithoutInfo.length ) { info = info.concat ((await (await fetch (`https://api.vc.bilibili.com/account/v1/user/cards?uids=${followersWithoutInfo.slice(0 , 50 ).join(',' )} ` , { headers })).json ()).data ); followersWithoutInfo = followersWithoutInfo.slice (50 ); } for (let i = 0 ; i < info.length ; i++) { info[i].follower = (await (await fetch (`https://api.bilibili.com/x/relation/stat?vmid=${info[i].mid} ` , { headers })).json ()).data .follower ; } const html = info.map (u => `<span class="face-wrap${u.pendant?.image ? ' has-frame' : '' } "><img class="face" src="${u.face} " referrerpolicy="no-referrer" />${u.pendant?.pid ? `<img class="face-frame" src="${u.pendant.image_enhance || u.pendant.image} " referrerpolicy="no-referrer" />` : '' } ${u.official.type === 0 ? '<img class="face-icon" src="https://api.wuziqian211.top/assets/personal.svg" />' : u.official.type === 1 ? '<img class="face-icon" src="https://api.wuziqian211.top/assets/business.svg" />' : u.vip.status ? '<img class="face-icon" src="https://api.wuziqian211.top/assets/big-vip.svg" />' : '' } </span> ${encodeHTML(u.name)} ` ).join ('<br />\n' );const content = `<style> * { font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-size: 20px; font-weight: bold; overflow-wrap: break-word; text-align: justify; transition: all 0.5s; } span.face-wrap { display: inline-block; position: relative; } img { vertical-align: middle; } img.face { border-radius: 50%; height: 60px; } span.face-wrap.has-frame img.face { height: 51px; padding: 19.5px; } span.face-wrap.has-frame img.face-frame { height: 90px; left: 0; position: absolute; top: 0; } span.face-wrap img.face-icon { bottom: 0; height: 18px; position: absolute; right: 0; } span.face-wrap.has-frame img.face-icon { bottom: 18px; right: 18px; } </style> ${html} ` ;fs.writeFileSync ('followers.html' , content);
下面的图片,就包含wuziqian211生成的粉丝列表。