我已经懒出翔了,因为深深地爱上了干这种看起来没啥卵用却又闪耀着小聪明光芒的事儿……
为啥又来?
之前写过技术贴:使用UserScript自动通过百度网盘/360云盘提取码以及更新:使用UserScript自动通过百度网盘/360云盘提取码(附下载),我很高兴地看到有勤奋好学也和我一样懒的同学已经在跃跃欲试自己玩了。
但是鉴于网站的情况各有不同,所以同一套脚本机制很难沿用在各个网站上,所以很多时候还是很有必要单独写的。
这不,有个同学下午在折腾这个网站:http://xclient.info/,说这个网站也是用百度网盘的,却实现不了。
打开看了一下,这货是个分享MAC应用的网站……虽然我对MAC不感冒,但这并不影响我来玩对吧……
基本情况
打开这个网站,找了一下,找到了下载列表。
然后点击这个下载的时候,会出现一个悬浮窗,包含了下载链接和提取码。
看这个DOM结构,之前的提取脚本应该是可以支持的。可是为啥没能支持了呢?
因为……
你直接打开页面源码搜索一下,就会发现页面里根本没这个。
既然页面里没有,那么很明显这个链接是动态加载的。这时候,我们直接看一下原来的那个下载链接到底是个什么鬼。
是一个木有实际地址的链接,却带着data-token这样奇奇怪怪的东西。data-token看起来是个经过Base64加密的字符串,但是我尝试解密了一下并没有能解密出来,所以肯定是由服务器端进行解密的。
所以在点击这个下载之前,我们先打开开发者工具的Network面板看看是不是有请求。
是他是他就是他,我们的金刚小哪吒~~
再看下这个地址要提交啥参数。
只要眼不瞎的人都能看得出来,这个token就是上面下载连接上的data-token……
基本思路
既然这个链接和数据是后加载的,那么最简单的思路就是直接调用对应的接口获得结果,再将结果直接放到对应的下载按钮上。
这不是很理所当然的吗……
开工
看了一下源站,有使用jQuery。既然如此,要进行ajax请求,自然是使用jQuery是最方便的了。
但是这里有一个问题,就是沙箱的问题。
这里要解决沙箱的问题,不使用比较复杂的附加script标签方法,而是使用TamperMonkey拥有的unsafeWindow
对象。
unsafeWindow
对象?简单地说,这个对象就指代了浏览器网页本身的Javascript环境。由于jQuery是全局的$
中保存的,所以其实就是在 unsafeWindow.$
下的。写下UserScript元数据
按照之前介绍的,新建一个UserScript,然后修改至符合自己的要求。
// ==UserScript== // @name XClient 下载链接自动提取 // @namespace http://www.fishlee.net/ // @version 0.1 // @description // @author You // @match http://xclient.info/* // @grant unsafeWindow // ==/UserScript== /* jshint -W097 */
注意加粗的那行,以前是没有的,或是none
。这里声明为unsafeWindow
,因为我们要用这个东西。没有这行声明的话,unsafeWindow
便无法使用 。
函数框架
函数框架如下,整个函数为匿名函数,并使用call
将调用上下文转移到unsafeWindow
中。
(function () { var win = this; //这个win便指代了原始页面的环境,也就是说,通过这个对象可以访问原始页面的Javascript环境 //TODO 其它代码 }).call(unsafeWindow);
获得真实下载地址
先通过一个什么选择器找到所有的下载链接……比如我这里用的是a.btn-download[data-token]
。啥?你不知道啥叫选择器?……我这里不是入门基础班啊 。
考虑到下载链接可能有很多(不信你多看几个页面就知道了……有的版本有很多很多的),所以并发请求对服务器很不友好,所以这里设计了一个队列来挨个获取。
(function () { var win = this; //这个win便指代了原始页面的环境,也就是说,通过这个对象可以访问原始页面的Javascript环境 //获得所有的下载链接,并放到数组里 var arr = Array.prototype.slice.call(document.querySelectorAll("a.btn-download[data-token]")); //队列处理函数 var callback = function () { //如果队列为空,则返回 if (!arr.length) return; //获得下一个待处理链接 var obj = arr.pop(); //使用jQuery向服务器条请求 win.$.post("/action/service?do=download", { token: obj.dataset.token }).done(function (data) { //如果成功,则判断数据是否正确(含是否有数据、是否是百度网盘) //如果不是百度网盘,则不作处理 if (data.msg === "success" && data.data && data.data.pf === "baidu") { //修改链接属性,改为实际地址并直接附上提取码,这里引用的是obj这个闭包对象 obj.setAttribute("href", data.data.url + "#" + data.data.key); //修改链接文本告诉人家已经获得链接地址了。这和母鸡下蛋后打鸣差不多一个道理…… obj.innerText = "百度网盘直链"; } }).always(function () { //不管成功还是失败,等待500毫秒后处理下一个 setTimeout(callback, 500); }); }; //第一次调用,启动队列 callback(); }).call(unsafeWindow);
详细代码看注释,应该不用再多做解释了 。
完善体验
上面的代码其实已经可以工作了,只不过你会发现有不好的体验。
- 点击后的一瞬间,还是弹出了浮窗,并很快跳走
- 在当前页面打开的,离开了页面
要解决这俩问题,思路也很简单。首先依然会继续弹,肯定是因为链接上绑定了click
事件,解绑即可。而想新窗口打开……直接加个target
结了。
完善后的代码如下。
// ==UserScript== // @name XClient 下载链接自动提取 // @namespace http://www.fishlee.net/ // @version 0.1 // @description // @author You // @match http://xclient.info/* // @grant unsafeWindow // ==/UserScript== /* jshint -W097 */ 'use strict'; (function() { var win = this; var arr = Array.prototype.slice.call(document.querySelectorAll("a.btn-download[data-token]")); var callback = function() { if (!arr.length) return; var obj = arr.pop(); win.$.post("/action/service?do=download", { token: obj.dataset.token }).done(function(data) { if (data.msg === "success" && data.data && data.data.pf === "baidu") { obj.setAttribute("href", data.data.url + "#" + data.data.key); obj.innerText = "百度网盘直链"; } //解绑click事件,阻止页面继续弹浮窗 win.$(obj).off("click"); //洁癖治疗,清理无效的data-token obj.removeAttribute("data-token"); //设置新窗口打开 obj.setAttribute("target", "_blank"); }).always(function () { //不管成功还是失败,等待500毫秒后处理下一个 setTimeout(callback, 500); }); }; callback(); }).call(unsafeWindow);
最后提示一下
测试过程中遇到有百度网盘地址是https的情况。在之前的脚本中,@match匹配的是http开始的百度网盘。要同时匹配https,可以修改对应的@match,将http改为*号即可。
还有问题吗?
还有问题尽管问,反正问了我也不会回答
学习了,非常感谢,可是我js水平抓瞎,会看不会写
在Edeg都能扩展的今天,不了解一点us真是不太行
上次在公积金网站上试了下 不错~
虽然我什么是选择器,但是看到jquery什么unsafe就不懂了
可参考我之前分享的那个演示文档,里面有描述。