
半次元(byc.net)是一个中文的cosplay交流社区,里面有许多高质量的cosplay作品。有时候,我们看到自己喜欢的cosplay作品,可能想保存下来。半次元提供直接查看原图的功能,但是一张张图片保存比较麻烦。为了简化保存步骤,我使用JavaScript+PHP做了一个简单的下载工具。
本工具包含bcy_download.js和bcy_download.php两个文件。
配置步骤:
1.把bcy_download.php放到web环境中;
2.把bcy_download.js的phpPath变量的值配置为为bcy_download.php的url。
phpPath的默认值为:
http://127.0.0.1/bcy_download.php
使用步骤:
1.在半次元的cosplay图片详情页面执行bcy_download.js中的代码。(你可以复制bcy_download.js代码然后打开控制台手动执行;也可在greasyfork.org安装本UserScript脚本来自动执行)
2.页面右上角出现一个下载按钮:

3.点击即可开始下载。下载按钮上会有进度提醒:

等到下载完成,会有弹窗提醒。

下载的图片会存放在bcy_download.php同目录中的bcyimg文件夹中:


注意事项:
1.下载时可能会在一张图那里卡很久,等待即可。(我这边经常下几张就卡住一会儿,不知道半次元对于连续访问大图有没有做限制)
2.由于bcy_download.php会创建中文文件夹来保存图片,所以本工具仅建议在简体中文版本的Windows操作系统上使用。在其他系统上使用可能导致创建的文件夹名字变成乱码。
3.本工具仅做交流学习之用,请合理使用。下载的图片仅供收藏,请勿未经coser授权随意传播。因使用本工具产生的任何问题,本人概不负责。
计算第二天的日期的要点在于,在跨月时进入下一月,在跨年时进入下一年。其他时间只要天数加1就行。所以找出每个月的最后一天就ok了。
function tomorrow_date (y,m,d){ //参数里的月份需要是自然月。也就是说月份+1这步在传参前就设置好
var run = false; //判断是否是闰年
if (!(y%4)) { //如果年份是4的整数倍
if (!(y%100)) { //如果是整百年
if (!(y%400)) { //如果是400的整数
run = true;
}
}else{
run = true;
}
}
if(d==28&&m==2){ //2月28日
if(!run) { //非闰年
d=1;
m++;
}else{ //闰年
d++;
}
}else if(d==29&&m==2){ //2月29日
d=1;
m++;
}else if(d==30&&(m==4||m==6||m==9||m==11)){ //30天的月份里的第30天
d=1;
m++;
}else if(d==31){ //31天的月份里的第31天
d=1;
if (m==12){ //如果是12月则进入下一年
m=1;
y++;
}else{
m++;
}
}else{ //如果今天不是月份的最后一天
d++;
}
return "明天是"+y+"年"+m+'月'+d+'日';
}
var myDate = new Date();
var y = myDate.getFullYear();
var m = myDate.getMonth()+1; //因为js的月份是从0开始的,所以这里月份+1
var d = myDate.getDate();
document.write(tomorrow_date(y,m,d));
感谢TianMao指出闰年的问题~另外他提出了使用Date对象的setDate方法来计算下一天的办法。这个办法很简便,当我们重设了日期(第几天)后,JavaScript会自动更新Date对象的月份、年份,这样就我们就不用自己写上面的判断了。
var myDate = new Date(); myDate.setDate(myDate.getDate()+1); var y = myDate.getFullYear(); var m = myDate.getMonth()+1; var d = myDate.getDate(); console.log(y,m,d);
刚才我整理出来了一百多行url,想去掉重复的。于是我到JavaScript里建了个数组,把这些url都转成了数组里的字符串。之后我找到了一个使用hashtable(哈希表)的去重算法,比循环更简便:
function unique(arr) {
var result = [], hash = {};
for (var i = 0, elem; (elem = arr[i]) != null; i++) {
if (!hash[elem]) {
result.push(elem);
hash[elem] = true;
}
}
return result;
}
上面的方法是有缺陷的,今天在知乎专栏看到了更好的方法,用的是ES6的set类型。set类型不允许添加重复项,所以我们根本不用自己去重了,直接把数组转换文set对象就可以去掉重复项了:
function unique(arr){
var set = new Set(arr);
return Array.from(set);
}
JavaScript有个叫“变量提升”的特性,意为:
在函数里声明的所有变量会被提升到函数顶部声明(但不涉及到赋值)。
示例:
var a=0;
!function(){
alert(a);
var a=1;
}()
在函数外声明了变量a=0,之后在函数里重新声明了a=1。
在这中间我们弹出a,它的值不是0,而是undefined。这就是变量提升导致的。发生变量提升之后,函数里声明的a被提升到函数顶部声明了(但这并不涉及到赋值),所以代码实际上相当于这样:
var a=0;
!function(){
var a;
alert(a);
a=1;
}()
所以如果函数外已经声明了一个变量,那么就不要在函数内重复声明它,这样将会触发变量提前,可能会造成一些问题。
如果需要在函数内重复声明,那么将这些变量在函数顶部先声明但不赋值(就是写得和变量提升后的实际代码一样),可以让代码更直观。
其实这个概念是我昨天发布了搜索bilibili评论工具时,8q给我指出的。今天看书很巧的也看到了这个地方,弄清楚了。
此工具已失效!

我用JavaScript写了一个小工具,在b站视频播放页面执行,可以在当前视频的所有评论里查找指定用户发出的评论。
举个例子:你(或是别人)在某视频下发了一条评论,但没记住楼层。现在,如果你想要在众多评论里找到自己(或其他某个人)发的评论,可以使用本工具。
本工具可以搜索完所有的楼层,但查找楼中楼时,本工具只能查找到每个楼中楼的前5条回复(所以对于楼中楼,可能会有遗漏)。
如果你有油猴扩展,可以到UserScript安装页面安装此用户脚本。
如果你没有油猴扩展,你可以手动执行代码。下面提供两种执行方式:
1.控制台执行以下代码,它会远程加载js文件来执行:
var newscript=document.createElement("script");
newscript.src="/f/bilibili_comment_search.js";
document.body.appendChild(newscript);
这种方法的话,由于我的js的url是http的,所以在B站的https页面里无法执行,可以在地址栏把b站这个页面的网址手动改成http的。
2.下载js文件,然后手动复制其代码到控制台执行。
代码执行之后,在视频上方会出现“查找评论”的文字:

点击它,输入想查找的用户的id(就是打开TA主页时网址里的数字)。之后,每当找到这个人发的一条评论,就会在控制台输出该评论的楼层信息。

注意要在控制台看哦~我们可以根据页数和楼层数提示去找到这条评论。
如果工具搜索完了所有评论,会弹出搜索到了几条结果。如果你想中途停止搜索,点击视频上方的提示文字即可。
Read More →
假设现在获得了一些格式化的时间/日期字符串,如何用JavaScript对其排序呢?
理想的办法就是用Date.parse()方法将这些字符串解析为时间,直接比较它们的毫秒数。示例如下:
function sortDate(sortby) {
return function(object1, object2) {
var value1 = Date.parse(object1);
var value2 = Date.parse(object2);
if(value1>value2){
return (sortby==="desc")?-1:1;
}else if(value1<value2){
return (sortby==="desc")?1:-1;
}
}
}
var a=["2017/4/20","2016/6/3","2016/7/1","2016/7/27","2017/8/4","2016/10/13","2016/12/27","2017/1/14","2017/2/3","2017/2/3","2017/2/3","2017/2/3","2017/2/3","2017/2/3","2017/2/3","2017/3/1","2017/3/1","2017/3/1","2017/3/1","2017/3/1","2017/3/1","2017/3/1","2017/3/23","2017/3/31","2017/5/19","2017/5/19","2017/6/2","2017/6/24","2017/6/24","2017/6/24","2017/9/1","2017/9/5","2017/12/12","2018/2/19","2018/9/26","2017/5/19","2017/3/9","2017/7/14","2017/5/19","2017/5/19"];
a.sort(sortDate("desc"));
结果如下:

上例定义了一个sortDate函数,我们在数组的sort方法里将它作为参数。如果不手动设置排序参数为desc,那么这个函数将始终按升序排序。
标准的时间/日期格式如下:
"2017/4/20 16:20:30"
当然,只用一部分,或者改变下连接符,也是可以解析的。
Read More →
因为JavaScript水平太渣,以前我都是用JQuery来做ajax。现在看了些资料,感觉原生的写法也不难,而且兼容性也已经不错了,所以跟着练一练。
同步的ajax请求:
var xhr=new XMLHttpRequest();
xhr.open("get","http://saber.我爱你/demo/checknew.php",false);
xhr.send(null);
if (xhr.status===200) {
alert(xhr.responseText);
}else{
alert("发生了异常,状态码为"+xhr.status+","+xhr.statusText);
}
返回的数据里有如下属性:
responseText:作为响应主体被返回的文本。
responseXML:如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存包含着响应数据的XML DOM 文档。
status:响应的HTTP 状态。
statusText:HTTP 状态的说明。
异步的ajax请求:
var xhr=new XMLHttpRequest();
xhr.onreadystatechange=function(){
if (xhr.readyState===4) {
if (xhr.status===200) {
alert(xhr.responseText);
}else{
alert("发生了异常,状态码为"+xhr.status+","+xhr.statusText);
}
}
}
xhr.open("get","http://saber.我爱你/demo/checknew.php",true);
xhr.send(null);
检测状态的部分也可以这样写:
xhr.onload=function (argument) {
if (this.status == 200) {
//……
}
}
异步请求除了在xhr.open里将最后的参数设置为true之外,在接收数据时也和同步请求不同。异步请求通过检查xhr对象的readyState属性来判断请求处在哪个阶段。当readyState为4时,表示接收到了数据,并且数据可用。
我们通过onreadystatechange事件来检测readyState的值,当值为4时即可对数据进行处理。
另外,在接收到响应之前还可以调用abort()方法来取消异步请求。即xhr.abort(); 。
<img src="/f/head15.jpg" id="stwRight">
<img src="/f/saber%E8%A1%A8%E6%83%85_1_1.jpg" id="stwCenter">
<img src="/f/x.png" id="swtClose">
<style type="text/css">
#LRdiv0,#LRdiv1{display:none !important;}
#stwRight{position: fixed;right: 0;top: 60px;display: none;z-index: 999;cursor: pointer;}
#stwCenter{position: fixed;display: none;width: 400px;height: 350px;left: 50%;margin-left: -200px;top: 50%;margin-top: -175px;z-index: 999;cursor: pointer;}
#swtClose{position: fixed;display: none;width: 24px;height: 24px;left: 50%;margin-left: 175px;top: 50%;margin-top: -175px;z-index: 9999;cursor: pointer;}
</style>
<script type="text/javascript">
window.onload=function(){
function showSwtC(){
document.getElementById("stwCenter").style.display="block";
document.getElementById("swtClose").style.display="block";
}
function showSwtR(){
document.getElementById("stwRight").style.display="block";
}
function hideSwtC(){
document.getElementById("stwCenter").style.display="none";
document.getElementById("swtClose").style.display="none";
}
function hideSwtR(){
document.getElementById("stwRight").style.display="none";
}
function openSwt(){
LR_HideInvite();openZoosUrl();return false;
}
showSwtR();
setTimeout(function(){
hideSwtR();
showSwtC();
},4000);
document.getElementById("stwRight").onclick=function(){
openSwt();
}
document.getElementById("stwCenter").onclick=function(){
hideSwtC();
showSwtR();
openSwt();
setTimeout(function(){
hideSwtR();
showSwtC();
},8000);
}
document.getElementById("swtClose").onclick=function(){
hideSwtC();
showSwtR();
setTimeout(function(){
hideSwtR();
showSwtC();
},8000);
}
}
</script>
侧边浮窗没做关闭按钮。
因为openSwt()里用了商务通自带的函数openZoosUrl(),所以页面上还是得加载商务通代码。如果把openSwt()改成直接用网址跳转,则不用加载商务通代码。
上面商务通不仅样式是我们自己做的,显示、隐藏的逻辑也需要自己定义。实际上对于有商务通代码的页面,我们可以直接利用商务通自身的函数来“偷梁换柱”,打开我们自己的商务通图片。
var sTimer = setInterval(function(){
if(typeof LR_showInviteDiv != "undefined"){
window.LR_showInviteDiv = function(){ //此处改写了商务通显示邀请框的函数
showSwt();
}
clearInterval(sTimer);
}
},100);
LR_showInviteDiv是显示邀请框的函数,如果页面上有这个函数,则改写它,让它执行showSwt,也就是我们自己定义的一个函数,显示自己的邀请框。
恩……我对这这个代码研究了好久,发现这个办法并没有多方便,浪费时间。唯一的优点是不用写css来隐藏商务通的邀请框了。另外改写商务通的这个函数会导致商务通客户端发送邀请失败。麻烦。
平时我们表单的提交多数是用action来指定提交到的文件,但这样会产生页面的刷新。用ajax提交可以避免刷新,但是需要我们自己组织要提交的数据。这就是表单序列化了。
jQuery有个serialize方法来做表单序列化,语法如下:
$(selector).serialize();
序列化之后,表单里需要提交的值会被组织成字符串,形如:
"name=124&company=baidu.com&fav=1,2,3"
如果里面有汉字之类,会自动进行url编码。表单序列化完全模拟了浏览器提交时的数据,这样我们就可以用ajax来提交表单了。
如一个id为“yy”的表单,它的提交的地址是"/plus/diy.php",提交按钮id是submit。那么表单序列化并提交的代码如下:
$("#submit").click(function(event) {
event.preventDefault();
$.ajax({
type: "post",
url: "/plus/diy.php",
data: $("#yy").serialize(),
success: function() {
alert("信息提交成功。");
}
})
})
注意用preventDefault()阻止浏览器默认事件(就是跳转到action页面,即使你没写action属性也会跳转的)。
后台对提交数据的处理和用action提交时的数据是一致的。
Read More →
// ==UserScript==
// @name 网站地图生成助手(Clearision主题专用)
// @namespace https://saber.love
// @version 0.1
// @description 自动生成当前网页的sitemap信息(文章页)
// @author saber
// @match https://saber.love/*.html
// @grant none
// ==/UserScript==
var sitemap_url = window.location.href; //url
var sitemap_pri = "0.5"; //权重
var changefreq = "Always"; //更新频率
var sitemap_year = new Date(); //年份
sitemap_year = sitemap_year.getFullYear(); // 因为主题生成的网页里没有包含年份,所以取当前年份
var sitemap_time_info = document.getElementsByClassName("post_time")[0].innerText.replace(/\n/g, "").replace(/\t/g, "").replace("/", "-").replace(" ", "");
var sitemap_date, sitemap_time; //时间和日期
if (sitemap_time_info.length === 9) {
sitemap_date = sitemap_time_info.substring(0, 4).replace("-", "-0"); //天数在10号以下时前面没有0,所以需要补上0
sitemap_time = sitemap_time_info.substring(4, 9);
} else if (sitemap_time_info.length === 10) {
sitemap_date = sitemap_time_info.substring(0, 5);
sitemap_time = sitemap_time_info.substring(5, 10);
}
window.stop(); //停止页面加载,可以节省图片流量
var this_sitemap_result = ("<url>" + "\r\n" + "<loc>" + sitemap_url + "</loc>" + "\r\n" + "<priority>" + sitemap_pri + "</priority>" + "\r\n" + ("<lastmod>" + sitemap_year + "-" + sitemap_date + "T" + sitemap_time + ":00+00:00</lastmod>") + "\r\n" + "<changefreq>" + changefreq + "</changefreq>" + "\r\n" + "</url>" + "\r\n"); //拼接出本页面的sitemap信息
localStorage.setItem("mymap", localStorage.getItem("mymap") + this_sitemap_result); //把本次的结果追加存储
var next_page = document.querySelector("#p_n_r a");
if (!!next_page) { //如果有下一页就自动打开下一页
next_page.click();
} else { //如果没有下一页就弹出结果
document.write("<xmp>" + localStorage.getItem("mymap") + "</xmp>");
alert("sitemap信息生成完毕");
localStorage.setItem("mymap", ""); //清空结果
}
本文代码是针对我这个网站写的,如果其他网站想要使用,可以对照自己网站进行修改。
使用方法:
在油猴里新建一个脚本,把下面的代码粘贴进去保存即可。之后打开或刷新本站任意一个网页,代码就会从这个页面一直抓取到最新的一篇文章,自动生成sitemap信息并存储到localStorage里。抓取完成后弹窗提醒,并把抓取结果输出到页面上。这样我们只需要等结果出来,最后复制结果就行了。
注意:
由于文章信息里没有包含年份,所以年份去的是当年。如果如在当年去获取之前的文章,那么年份就是错的。比如2018年1月去获取2017年12月的,结果会显示2018年12月。这个可以在获取后批量替换下。
使用之后记得关掉这个油猴脚本,以免影响正常浏览。