共计 12019 个字符,预计需要花费 31 分钟才能阅读完成。
v1.2 更新
当初不会被动依照用户等级过滤了, 用户运行了 level(过滤等级)
才会对用户进行对应的过滤
代码如下
console.log('版本 ver1.0 视频页和动静页皆可用,评论数大于 10 时实用,未过滤反复发言的账号,如果遇见反复的,倡议依照反复次数从新 roll 几次')
console.log('版本 ver1.1 改良了算法,当初能够过滤反复发言的账号了,只会计算一次')
console.log('版本 ver1.2 改良了逻辑, 当初不会被动过滤用户等级, 能够利用 level(num)用户本人抉择过滤等级')
console.log("程序开始运行");
console.log("定义汇合存储数据");
let dataArray=[]
let rollTimes = 0;
console.log("数据载入中...");
// 循环变量
var my_loop;
// 下滑延时 500 毫秒 网速 / 加载速度较慢的敌人们最好加快速度 进步准确性
var r_time = 500;
// 评论数
var comment_num = 1;
let refer = '';
if(window.origin.match(/:\/\/[a-z]*\./)[0].match(/[a-z]/)[0]==='t'){// 动静页
refer = 'comment'
}else{// 视频页面
refer = 'total-reply'
}
if (document.getElementsByClassName(refer)[0].innerText.indexOf("万") != -1) {comment_num = 10000 * (parseInt(document.getElementsByClassName(refer)[0].innerText) + 1);
}else {comment_num = parseInt(document.getElementsByClassName(refer)[0].innerText);
}
// 下滑
function r() {window.scroll(0, 1920 * comment_num);
console.log(` 已加载 ${rollTimes+=1}页数据 `)
// 没有评论后主动进行下滑 并开始收集数据
if (document.getElementsByClassName("loading-state")[0].innerText == "没有更多评论") {
// 进行下滑循环
stop_r();
// 收集数据
draw();}
}
// 进行下滑循环
function stop_r() {clearInterval(my_loop);
}
// 收集数据
function draw() {var len = document.getElementsByClassName("con").length;
for (var i = 0; i < len; i++) {let name = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].getElementsByTagName("a")[0].innerText;
let id = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].getElementsByTagName("a")[0].getAttributeNode("data-usercard-mid").value;
let level = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].querySelector('img.level').src.match(/[level]_[0-9]{1}/)[0].match(/\d/)[0]
let content = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("text")[0].innerHTML
// if(level>=3){dataArray.push({name:name,id:id,content:content,level:level})
// }
}
duplicateRemove(dataArray)
console.log("全副数据加载结束");
console.log("符合条件共" + dataArray.length + "名用户",dataArray);
console.log('运行 level(过滤的等级) 即会在'+dataArray.length+'名用户中过滤等级小于输出数字的所有用户' );
console.log('运行 roll(要抽取的数量) 例如 roll(3) 即会在'+dataArray.length+'名用户中抽取对应数量的侥幸用户' );
console.log('以后版本: ver1.2')
}
// 去除反复发言的账号
function duplicateRemove(array){for(let z=array.length-1;z>=0;z--){let item = array[z];
for(let x=z-1;x>=0;x--){let subitem = array[x];
if(item.id===subitem.id){array.splice(x,1);
}
}
}
return array
}
function level(num){if(Number(num).toString()==='undefined'||Number(num).toString()==='NaN'||num<0||num>6){alert('请输出大于等于 0 小于等于 6 的整数')
return
}
// else{// if(num<0||num>6){// alert('请输出大于 0 小于等于 6 的数字')
// return
// }
// }
dataArray = dataArray.filter((item)=>{return item.level>=num;})
console.log('已将 b 站等级小于'+num+'的用户全副排除, 现有'+dataArray.length+'个符合条件的用户')
}
// 获取幸运儿
function roll(num) {if(num===0){alert('你是成心找茬是不是?抽 0 人?')
return
}
if(dataArray.length<=0){alert('符合条件的人为零,抽奖失败');
return
}
if(num>dataArray.length){// 抽奖数大于总人数
alert(` 抽奖数大于总人数,修改为 ${dataArray.length} 人获奖 `)
num = dataArray.length
}
var Lucky={}
var randomArray = this.randomFunc([],num)
randomArray.sort((a,b)=>{return a-b})
for (var i = 0; i < randomArray.length; i++) {let lucky_num = randomArray[i]
Lucky[i+1]={'用户 ID':dataArray[lucky_num].id,
'用户名': dataArray[lucky_num].name,
'评论内容': dataArray[lucky_num].content
}
}
console.table(Lucky,['用户 ID','用户名','评论内容'])
}
// 递归算法 排除取到雷同的随机数。function randomFunc(array,total){if(total<=0) {return array};
// 随机数算法中,如果应用的是 Math.round 这种左右都能取到的随机数算法,在抽奖数越靠近评论数的时候,就会遇到边际问题,长度为 9 的数组,会被取到 0 -9,实际上应该是 0 -8,所以采纳 Math.floor 办法。let num = Math.floor(Math.random() * (dataArray.length),10);
if(array.length>0){if(this.cycle(array,num)){array.push(num);
total-=1;
}
return this.randomFunc(array,total);
}else{array.push(num);
total-=1
return this.randomFunc(array,total);
}
}
function cycle(array, num) {for (let x = 0; x < array.length; x++) {if (array[x] === num) {return false}
}
return true
}
// 开始主动下滑 r_time 毫秒一次
my_loop = setInterval(r, r_time);
// 全副数据加载结束后,应用 roll(中奖数) 抽取中奖者
昨天敌人忽然发消息说以前用的 B 站的评论抽奖控制台脚本没用了,让我看看有没有什么方法。
b 站是这样的,在达到肯定的要求之前,是没有官网的抽奖工具的,也就是你想搞点评论关注抽奖这种货色是没有官网反对的,所以也就有了一些针对小 Up 的抽奖工具应用,就比方明天要谈的控制台脚本
先上演示吧:
很简略,只须要你关上对应的页面,而后和动图外面一样,按键盘的 F12(笔记本按 fn+F12), 或者右键,点击查看,而后点击 console(中文是控制台),而后把代码复制进去,敲下回车,等页面数据获取实现当前,敲入 roll(要抽取的数量)
就能够啦
要复制的代码如下:
console.log('版本 ver1.0 视频页和动静页皆可用,评论数大于 10 时实用,未过滤反复发言的账号,如果遇见反复的,倡议依照反复次数从新 roll 几次')
console.log('版本 ver1.1 改良了算法,当初能够过滤反复发言的账号了,只会计算一次')
console.log("程序开始运行");
console.log("定义汇合存储数据");
let dataArray=[]
let rollTimes = 0;
console.log("数据载入中...");
// 循环变量
var my_loop;
// 下滑延时 500 毫秒 网速 / 加载速度较慢的敌人们最好加快速度 进步准确性
var r_time = 500;
// 评论数
var comment_num = 1;
let refer = '';
if(window.origin.match(/:\/\/[a-z]*\./)[0].match(/[a-z]/)[0]==='t'){// 动静页
refer = 'comment'
}else{// 视频页面
refer = 'total-reply'
}
if (document.getElementsByClassName(refer)[0].innerText.indexOf("万") != -1) {comment_num = 10000 * (parseInt(document.getElementsByClassName(refer)[0].innerText) + 1);
}else {comment_num = parseInt(document.getElementsByClassName(refer)[0].innerText);
}
// 下滑
function r() {window.scroll(0, 1920 * comment_num);
console.log(` 已加载 ${rollTimes+=1}页数据 `)
// 没有评论后主动进行下滑 并开始收集数据
if (document.getElementsByClassName("loading-state")[0].innerText == "没有更多评论") {
// 进行下滑循环
stop_r();
// 收集数据
draw();}
}
// 进行下滑循环
function stop_r() {clearInterval(my_loop);
}
// 收集数据
function draw() {var len = document.getElementsByClassName("con").length;
for (var i = 0; i < len; i++) {let name = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].getElementsByTagName("a")[0].innerText;
let id = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].getElementsByTagName("a")[0].getAttributeNode("data-usercard-mid").value;
let level = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].querySelector('img.level').src.match(/[level]_[0-9]{1}/)[0].match(/\d/)[0]
let content = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("text")[0].innerHTML
if(level>=3){dataArray.push({name:name,id:id,content:content})
}
}
duplicateRemove(dataArray)
console.log("全副数据加载结束");
console.log("符合条件共" + dataArray.length + "名用户",dataArray);
console.log('运行 roll(要抽取的数量) 例如 roll(3) 即会在'+dataArray.length+'名用户中抽取对应数量的侥幸用户' );
}
// 去除反复发言的账号
function duplicateRemove(array){for(let z=array.length-1;z>=0;z--){let item = array[z];
for(let x=z-1;x>=0;x--){let subitem = array[x];
if(item.id===subitem.id){array.splice(x,1);
}
}
}
return array
}
// 获取幸运儿
function roll(num) {if(num===0){alert('你是成心找茬是不是?抽 0 人?')
return
}
if(dataArray.length<=0){alert('符合条件的人为零,抽奖失败');
return
}
if(num>dataArray.length){// 抽奖数大于总人数
alert(` 抽奖数大于总人数,修改为 ${dataArray.length} 人获奖 `)
num = dataArray.length
}
var Lucky={}
var randomArray = this.randomFunc([],num)
randomArray.sort((a,b)=>{return a-b})
for (var i = 0; i < randomArray.length; i++) {let lucky_num = randomArray[i]
Lucky[i+1]={'用户 ID':dataArray[lucky_num].id,
'用户名': dataArray[lucky_num].name,
'评论内容': dataArray[lucky_num].content
}
}
console.table(Lucky,['用户 ID','用户名','评论内容'])
}
// 递归算法 排除取到雷同的随机数。function randomFunc(array,total){if(total<=0) {return array};
// 随机数算法中,如果应用的是 Math.round 这种左右都能取到的随机数算法,在抽奖数越靠近评论数的时候,就会遇到边际问题,长度为 9 的数组,会被取到 0 -9,实际上应该是 0 -8,所以采纳 Math.floor 办法。let num = Math.floor(Math.random() * (dataArray.length),10);
if(array.length>0){if(this.cycle(array,num)){array.push(num);
total-=1;
}
return this.randomFunc(array,total);
}else{array.push(num);
total-=1
return this.randomFunc(array,total);
}
}
function cycle(array, num) {for (let x = 0; x < array.length; x++) {if (array[x] === num) {return false}
}
return true
}
// 开始主动下滑 r_time 毫秒一次
my_loop = setInterval(r, r_time);
// 全副数据加载结束后,应用 roll(中奖数) 抽取中奖者
对于数据我曾经做了筛选,首先是只有等级大于等于 3 级的用户会被纳入到抽取范畴内,其次是发了屡次评论的同一个账号,只会被计算一次,不必放心疯狂发评论的人被抽到的概率会大。所有人都是厚此薄彼,几率雷同。
对于普通用户看到这里其实就行了。
上面开始解说代码思路
首先这个脚本的起源是 b 站用户 Love 丶伊卡洛斯
没有 TA 的根底代码,也就没有当初的改进版抽奖代码
实现的思路就是在代码中管制浏览器的 Y 轴滚动,不停的往下滚动直到所有的评论都被加载进去,而后收集评论人的已有信息,再进行各种过滤筛选,比方筛掉用户等级有余的,用户 ID 屡次呈现的,只算作一次这种。
var comment_num = 1;
let refer = '';// 这个代表评论的起源,b 站中动静页和视频页的页面构造不同,所以要离开解决
if(window.origin.match(/:\/\/[a-z]*\./)[0].match(/[a-z]/)[0]==='t'){// 动静页
refer = 'comment'
}else{// 视频页面
refer = 'total-reply'
}
if (document.getElementsByClassName(refer)[0].innerText.indexOf("万") != -1) {comment_num = 10000 * (parseInt(document.getElementsByClassName(refer)[0].innerText) + 1);
}else {comment_num = parseInt(document.getElementsByClassName(refer)[0].innerText);
}
略微有点前端根底的人了解下面的代码是没有问题的,就是找到总共有多少个评论
拿到评论数当前,就大抵的计算一下要往下滚动多少间隔,等呈现‘没有更多评论’的时候,就进行滚动。
// 下滑
function r() {window.scroll(0, 1920 * comment_num);
console.log(` 已加载 ${rollTimes+=1}页数据 `)
// 没有评论后主动进行下滑 并开始收集数据
if (document.getElementsByClassName("loading-state")[0].innerText == "没有更多评论") {
// 进行下滑循环
stop_r();
// 收集数据
draw();}
}
来看看外围办法收集数据draw()
// 收集数据
function draw() {var len = document.getElementsByClassName("con").length;
for (var i = 0; i < len; i++) {let name = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].getElementsByTagName("a")[0].innerText;
let id = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].getElementsByTagName("a")[0].getAttributeNode("data-usercard-mid").value;
let level = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("user")[0].querySelector('img.level').src.match(/[level]_[0-9]{1}/)[0].match(/\d/)[0]
let content = document.getElementsByClassName("list-item reply-wrap")[i].getElementsByClassName("con")[0].getElementsByClassName("text")[0].innerHTML
if(level>=3){dataArray.push({name:name,id:id,content:content})
}
}
duplicateRemove(dataArray)
console.log("全副数据加载结束");
console.log("符合条件共" + dataArray.length + "名用户",dataArray);
console.log('运行 roll(要抽取的数量) 例如 roll(3) 即会在'+dataArray.length+'名用户中抽取对应数量的侥幸用户' );
}
首先 len 的意义就是失去页面上有多少个主评论,也就是评论中的回复是不算在内的
上图能够看到,一个 con
就是一个评论
而后再把所有的 con 进行循环,拿到对应的 id 数据,名称数据,评论内容数据
而后在这里进行判断,当用户的等级小于 3 级的时候,就不要这条数据。
而后留神,这时候的 dataArray
是所有除了 3 级以下的用户的数据都会被放进去,所以这时候要对数据进行筛选,如果同一个用户评论了多条,那在数据里他就会呈现屡次,这对其余评论一次的用户是不够平的,所以要对数据进行筛选,排除掉多余的雷同数据。也就是 duplicateRemove()
办法
// 去除反复发言的账号
function duplicateRemove(array){for(let z=array.length-1;z>=0;z--){let item = array[z];
for(let x=z-1;x>=0;x--){let subitem = array[x];
if(item.id===subitem.id){array.splice(x,1);
}
}
}
return array
}
一个简略的双重循环,本人和本人比照,如果 id 雷同,就第二个开始雷同的数据全副革除掉
把数据清理过当前,当初内存里就是能够抽奖的符合要求的用户数据了,上面就是抽奖的办法
// 获取幸运儿
function roll(num) {if(num===0){alert('你是成心找茬是不是?抽 0 人?')
return
}
if(dataArray.length<=0){alert('符合条件的人为零,抽奖失败');
return
}
if(num>dataArray.length){// 抽奖数大于总人数
alert(` 抽奖数大于总人数,修改为 ${dataArray.length} 人获奖 `)
num = dataArray.length
}
var Lucky={}
var randomArray = this.randomFunc([],num)
randomArray.sort((a,b)=>{return a-b})
for (var i = 0; i < randomArray.length; i++) {let lucky_num = randomArray[i]
Lucky[i+1]={'用户 ID':dataArray[lucky_num].id,
'用户名': dataArray[lucky_num].name,
'评论内容': dataArray[lucky_num].content
}
}
console.table(Lucky,['用户 ID','用户名','评论内容'])
}
var lucky={}
后面的判断能够不必看,精益求精的作用
上面的 var randomArray = this.randomFunc([],num)
这一段代码可得好好说说
这段代码的作用就是依据用户输出的数字 num
, 产生随机num
个不反复的整数,并且返回这些整数的数组,给 randomArray
接管,即最终返回的后果是相似于 num = 4 ;randomArray=[5,6,8,9]
这样的数据。
上面好好讲讲随机数组是怎么产生的
// 递归算法 排除取到雷同的随机数。function randomFunc(array,total){if(total<=0) {return array};
// 随机数算法中,如果应用的是 Math.round 这种左右都能取到的随机数算法,在抽奖数越靠近评论数的时候,就会遇到边际问题,长度为 9 的数组,会被取到 0 -9,实际上应该是 0 -8,所以采纳 Math.floor 办法。let num = Math.floor(Math.random() * (dataArray.length),10);
if(array.length>0){if(this.cycle(array,num)){array.push(num);
total-=1;
}
return this.randomFunc(array,total);
}else{array.push(num);
total-=1
return this.randomFunc(array,total);
}
}
function cycle(array, num) {for (let x = 0; x < array.length; x++) {if (array[x] === num) {return false}
}
return true
}
首先 randomFunc
办法接管两个参数,一个是数组 array
, 一个是total
即用户输出的抽奖个数,也就是循环次数,如果产生了 total
个随机数,就会完结递归。let num = Math.floor(Math.random() * (dataArray.length),10);
这段代码的意思就是如果当初有 18 个用户, 那么 num
的范畴就是[0,18)
, 也就是会取到 0, 然而取不到 18, 只能取到 17, 如果不这样解决, 在抽奖数趋近于用户总数的时候就会报错, 因为在 js 里, 数据不是从 1 开始, 是从 0 开始, 也就是一个长度为 5 的数组, 序号是[0,4]
, 也就是 0,1,2,3,4 而不是 1 数到 5, 所以须要这样的解决
拿到取值范畴内的 num
后, 如果 array
的长度为 0, 就间接把 num
放到数组里而后把循环次数 -1, 持续 randomFunc
办法, 如果 array
外面曾经有数据了, 就须要对 array
进行循环, 和产生的随机数 num
比照 (也就是cycle
办法), 如果 array
里曾经有了该随机数, 就打回去从新生成随机数, 直到 array
里没有和 num
雷同的数据, 再把 num
塞到 array
中, 而后把循环次数 -1, 反复 randomFunc
办法, 直到 total
也就是循环次数为 0 的时候, 返回array
要指出的是, 递归中, 如果没有 return 循环办法
而是间接执行循环办法的话, 最初完结里的 return 后的数据是不会返回到办法外的, 会间接返回undefined
.
以上就是这次实际过程中失去的教训了, 尽管曾经干了蛮久的前端, 然而还没有想过在浏览器中间接依据页面获取数据进行编程, 这种编程体验蛮乏味, 也蛮有意思的.