关于javascript:bilibili评论区抽奖工具js脚本无需下载只需要复制代码在浏览器运行-更新到v12

2次阅读

共计 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.

以上就是这次实际过程中失去的教训了, 尽管曾经干了蛮久的前端, 然而还没有想过在浏览器中间接依据页面获取数据进行编程, 这种编程体验蛮乏味, 也蛮有意思的.

正文完
 0