龙象建设集团有限公司网站,WordPress 更改H标签,seo企业优化顾问,网站建设etw猜测、实现 B 站在看人数 猜测找到接口参数总结 实现 猜测
找到接口
浏览器打开一个 B 站视频#xff0c;比如 《黑神话#xff1a;悟空》最终预告 | 8月20日#xff0c;重走西游_黑神话悟空 (bilibili.com) #xff0c;打开 F12 开发者工具#xff0c;经过观察#xf… 猜测、实现 B 站在看人数 猜测找到接口参数总结 实现 猜测
找到接口
浏览器打开一个 B 站视频比如 《黑神话悟空》最终预告 | 8月20日重走西游_黑神话悟空 (bilibili.com) 打开 F12 开发者工具经过观察发现每 30 秒就会有一个如下的请求
https://api.bilibili.com/x/player/online/total?aid1056417986cid1641689875bvidBV1oH4y1c7Kkts57523354{code: 0,message: 0,ttl: 1,data: {total: 239,count: 182,show_switch: {total: true,count: true},abtest: {group: b}}
}返回值中的 data.total 就是在看人数如下 参数
请求有 4 个参数
aid1056417986
cid1641689875
bvidBV1oH4y1c7Kk
ts57523354aid、bvid 是稿件的编号cid 是视频的编号一个稿件可能有多个视频。通过三者可定位到唯一的视频。
ts 从命名上来看应该是时间戳比如 57523353、57523354 但显然太短了应该是经过处理的最后发现是时间戳秒除以 30 向上取整的结果
calcTs function(date) {// 时间戳秒const timestamp_second date.getTime() / 1000;// 除以 30 向上取整const ts Math.ceil(timestamp_second / 30);console.log(ts)return ts;
}下图是两个请求的参数以及请求的时间 在浏览器控制台验证猜想通过 calcTs 函数可计算出 ts与请求参数完全吻合 总结
B 站的实现思路应该是aid、bvid、cid 作为唯一编号以 30 秒为一个时间窗口进行统计在这 30s 中的请求都会使窗口值加 1每次累加完后返回最新值即可。
但同时还发现在多个标签页中打开同一个视频时比如 5 个标签页一开始在看人数都是 1等一会在看人数才会陆续变成 5。也就是说返回的不是最新值因为如果返回最新值的话5 个标签页的在看人数应该分别是 1 2 3 4 5。
猜测应该是同时存在两个 30 秒时间窗口这里称为当前窗口 currentWindow 也就是 ts 对应的 30s 窗口 和上一个窗口previousWindow 即 ts - 1 对应的 30s 窗口每次都累加到 currentWindow但返回 previousWindow。
这样就能解释为什么一开始在看人数都是 1等一会在看人数才会陆续变成 5 了。打开视频时previousWindow 不存在所以返回了 1同时创建 currentWindow 并从 1 累加到 5。这样等 30s 后下一个定时任务时currentWindow 就变成了 previousWindow5 个标签页都会返回 5在看人数就都陆续变成 5 了。
实现
后端可以使用 Redis 实现最简单的办法是使用 string 结构以 aid、bvid、cid、ts 作为 key给 key 设置大于 60s 的过期时间每次请求时使用 incr 自增即可。但这样会导致 Redis 找那个有大量的 key不好维护。
可以使用 hash 结构以 ts 为 key以 aid、bvid、cid 为 field窗口值为 value。这样 Redis 中只会有 ts、ts - 1 两个 key。如果必要的话也可以根据 field 的值将其 hash 分区到 2 * N 个 key 中。
TotalService
package com.example.demo3;import lombok.SneakyThrows;
import org.redisson.api.*;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;import java.time.Duration;
import java.util.concurrent.ExecutionException;Service
public class TotalService {private final RedissonClient redisson;public TotalService(RedissonClient redisson) {this.redisson redisson;}SneakyThrows({ExecutionException.class, InterruptedException.class})GetMappingpublic Integer total(String aid, String bvid, String cid, Long ts) {RBatch batch redisson.createBatch(BatchOptions.defaults());// currentWindow// 以时间戳作为 keyRMapAsyncString, Integer currentWindow batch.getMap(ts.toString());// 以 aid, bvid, cid 作为 currentWindow 的 keyString field field(aid, bvid, cid);// 自增 1currentWindow.addAndGetAsync(field, 1);// 过期时间必须大于 60scurrentWindow.expireIfNotSetAsync(Duration.ofSeconds(70));// previousWindowRMapAsyncString, Integer previousWindow batch.getMap(String.valueOf(ts - 1));RFutureInteger totalFuture previousWindow.getAsync(field);batch.execute();Integer total totalFuture.get();// 如果 previousWindow 不存在则返回 1if (total null || total 0) {return 1;}return total;}private String field(String aid, String bvid, String cid) {return aid : bvid : cid;}
}
TotalController
RestController
RequestMapping(/x/player/online/total)
public class TotalController {private final TotalService totalService;public TotalController(TotalService totalService) {this.totalService totalService;}CrossOrigin(originPatterns *)GetMappingpublic Integer total(RequestParam(aid) String aid, RequestParam(bvid) String bvid,RequestParam(cid) String cid, RequestParam(ts) Long ts) {return totalService.total(aid, bvid, cid, ts);}
}test.html
!DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/title
/head
body
divdivaid input idaid typetext value113071355923972bvid input idbvid typetext valueBV1giHnexEiDcid input idcid typetext value25714427593/divdiv在看span idtotal0/span/div
/div
/body
script typetext/javascriptconst elem_aid document.getElementById(aid);const elem_bvid_elem document.getElementById(bvid);const elem_cid_elem document.getElementById(cid);const elem_total document.getElementById(total);refreshTotal().then(() {// 30 秒执行一次setInterval(function () {refreshTotal();}, 30000)});async function refreshTotal() {const aid elem_aid.value;const bvid elem_bvid_elem.value;const cid elem_cid_elem.value;const ts calcTs(new Date());const url http://localhost:8080/x/player/online/total?aid${aid}cid${cid}bvid${bvid}ts${ts};const response await fetch(url);const total await response.json();console.log(total);elem_total.innerHTML total;}function calcTs(date) {// 时间戳秒const timestamp_second date.getTime() / 1000;// 除以 30 向上取整const ts Math.ceil(timestamp_second / 30);console.log(ts)return ts;}
/script
/html
文章转载自: http://www.morning.xpgwz.cn.gov.cn.xpgwz.cn http://www.morning.ysrtj.cn.gov.cn.ysrtj.cn http://www.morning.tssmk.cn.gov.cn.tssmk.cn http://www.morning.wqsjx.cn.gov.cn.wqsjx.cn http://www.morning.ssfq.cn.gov.cn.ssfq.cn http://www.morning.kfldw.cn.gov.cn.kfldw.cn http://www.morning.prjty.cn.gov.cn.prjty.cn http://www.morning.sfnjr.cn.gov.cn.sfnjr.cn http://www.morning.zdkzj.cn.gov.cn.zdkzj.cn http://www.morning.gbhsz.cn.gov.cn.gbhsz.cn http://www.morning.lkbyj.cn.gov.cn.lkbyj.cn http://www.morning.fqpyj.cn.gov.cn.fqpyj.cn http://www.morning.kqpsj.cn.gov.cn.kqpsj.cn http://www.morning.gnjkn.cn.gov.cn.gnjkn.cn http://www.morning.wpkr.cn.gov.cn.wpkr.cn http://www.morning.tddrh.cn.gov.cn.tddrh.cn http://www.morning.cfqyx.cn.gov.cn.cfqyx.cn http://www.morning.haolipu.com.gov.cn.haolipu.com http://www.morning.8yitong.com.gov.cn.8yitong.com http://www.morning.mpgfk.cn.gov.cn.mpgfk.cn http://www.morning.lokext.com.gov.cn.lokext.com http://www.morning.mpgfk.cn.gov.cn.mpgfk.cn http://www.morning.lgwpm.cn.gov.cn.lgwpm.cn http://www.morning.nlglm.cn.gov.cn.nlglm.cn http://www.morning.bzlgb.cn.gov.cn.bzlgb.cn http://www.morning.zqbrw.cn.gov.cn.zqbrw.cn http://www.morning.tpwrm.cn.gov.cn.tpwrm.cn http://www.morning.lhyhx.cn.gov.cn.lhyhx.cn http://www.morning.gryzk.cn.gov.cn.gryzk.cn http://www.morning.hmktd.cn.gov.cn.hmktd.cn http://www.morning.kskpx.cn.gov.cn.kskpx.cn http://www.morning.ntgsg.cn.gov.cn.ntgsg.cn http://www.morning.kdtdh.cn.gov.cn.kdtdh.cn http://www.morning.qsy38.cn.gov.cn.qsy38.cn http://www.morning.jkpnm.cn.gov.cn.jkpnm.cn http://www.morning.tqpnf.cn.gov.cn.tqpnf.cn http://www.morning.mqxzh.cn.gov.cn.mqxzh.cn http://www.morning.kdldx.cn.gov.cn.kdldx.cn http://www.morning.jrqw.cn.gov.cn.jrqw.cn http://www.morning.gnlyq.cn.gov.cn.gnlyq.cn http://www.morning.tmxtr.cn.gov.cn.tmxtr.cn http://www.morning.xlmgq.cn.gov.cn.xlmgq.cn http://www.morning.lqgfm.cn.gov.cn.lqgfm.cn http://www.morning.psxfg.cn.gov.cn.psxfg.cn http://www.morning.ykxnp.cn.gov.cn.ykxnp.cn http://www.morning.srnth.cn.gov.cn.srnth.cn http://www.morning.qfqld.cn.gov.cn.qfqld.cn http://www.morning.prgnp.cn.gov.cn.prgnp.cn http://www.morning.fhntj.cn.gov.cn.fhntj.cn http://www.morning.jbfjp.cn.gov.cn.jbfjp.cn http://www.morning.nwmwp.cn.gov.cn.nwmwp.cn http://www.morning.yrgb.cn.gov.cn.yrgb.cn http://www.morning.wnnfh.cn.gov.cn.wnnfh.cn http://www.morning.rgwz.cn.gov.cn.rgwz.cn http://www.morning.bkfdf.cn.gov.cn.bkfdf.cn http://www.morning.rwbh.cn.gov.cn.rwbh.cn http://www.morning.ypqwm.cn.gov.cn.ypqwm.cn http://www.morning.wqjpl.cn.gov.cn.wqjpl.cn http://www.morning.fhqdb.cn.gov.cn.fhqdb.cn http://www.morning.ydtdn.cn.gov.cn.ydtdn.cn http://www.morning.rqhdt.cn.gov.cn.rqhdt.cn http://www.morning.mrxqd.cn.gov.cn.mrxqd.cn http://www.morning.zgpgl.cn.gov.cn.zgpgl.cn http://www.morning.dhxnr.cn.gov.cn.dhxnr.cn http://www.morning.jkcpl.cn.gov.cn.jkcpl.cn http://www.morning.mmosan.com.gov.cn.mmosan.com http://www.morning.qkrgk.cn.gov.cn.qkrgk.cn http://www.morning.ngpdk.cn.gov.cn.ngpdk.cn http://www.morning.c7622.cn.gov.cn.c7622.cn http://www.morning.xcjbk.cn.gov.cn.xcjbk.cn http://www.morning.rmkyb.cn.gov.cn.rmkyb.cn http://www.morning.lyldhg.cn.gov.cn.lyldhg.cn http://www.morning.fplqh.cn.gov.cn.fplqh.cn http://www.morning.qxljc.cn.gov.cn.qxljc.cn http://www.morning.cmzcp.cn.gov.cn.cmzcp.cn http://www.morning.wnxqf.cn.gov.cn.wnxqf.cn http://www.morning.khyqt.cn.gov.cn.khyqt.cn http://www.morning.qsswb.cn.gov.cn.qsswb.cn http://www.morning.xprq.cn.gov.cn.xprq.cn http://www.morning.xbdrc.cn.gov.cn.xbdrc.cn