需求
我想下载视频网站的视频,但是现在的视频网站不提供视频下载的链接。如果我们分析网页的源码,也许也不会发现视频的地址(网页源码太长,我没有分析过,我只是猜测网页中没有视频的链接)。
通过FireFox的FireBug工具,我能获得浏览器向视频服务器下载视频文件的请求,那么我们能不能用程序来获得这些视频文件的地址,然后自动化地下载它呢?答案是肯定的。
解决方案
我想通过SlimerJS打开一个视频网站,通过Python监听网卡的数据包,将所有HTTP请求截获下来,分析HTTP请求的请求头,将请求头带mp4,flv的请求路径截获下来,因为这些请求就是视频文件的请求。那我们可以用这些请求来获得视频。
我的前2篇博文已经讲解了原理,1.获取网页中的视频下载地址(利用抓包), 2.获取网页中的视频下载地址(用headless browser), 还有源码在github上,源码现在只是实现了一个tornado的服务器,来接受post过来的url,url是我们需要下载的视频的视频页面。tornado服务器收到请求后,会开一个线程,用SlimerJS打开那个视频页面,再开一个线程来监听网卡上的数据。用户下次再带着那个url请求访问tornado服务器,如果视频下载完成了就返回结果,否则返回In progress提示用户晚点再来。
改进方案
因为SlimerJS打开网页视频后,就任由其播放,播放速度较慢,我们需要等待视频播放完了才能将所有视频下载下来。如果我们的网络带宽够大,视频很快就缓冲好了,我们可以快进,让网页去缓冲后面的内容。那么我就想到了让SlimerJS去触发快进的事件。
通过观察,点击播放窗口后,可以按键盘的右箭头来进行快进。
问题
理想太丰满,现实很骨感。那么我们怎么来模拟键盘的右箭头呢?通过查SlimerJS(Phantomjs)的文档可以知道
var page = require("webpage").create()
page.sendEvent("keypress", page.event.key.Right, null, null, null);
sendEvent
可以发送键盘事件,但是它发送的键盘事件是发送到document下的。元素监听的keydown事件都不会触发。
实验:
html页面如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="divId" style="width: 200px; height: 200px; background-color: #0000cc" >
<article>
<p style="background-color: #1cc09f">hello</p>
<p style="background-color: #1cc09f">hello</p>
</article>
</div>
<a>
aaaaaa
<p>hello</p>
</a>
<input type="text" id="inputId" name="">
</body>
<script src="./jquery.min.js"></script>
<script>
$("div").click(function (e) {
console.log('click div');
//alert("click div");
});
$("p").click(function (e) {
console.log('click p');
//alert("click p");
//return false; // 使得div的点击事件消失
});
$(document).keydown(function(event){
console.log("doc keydown");
console.log(event.keyCode);
});
$(document).keypress(function(event){
console.log("doc keypress");
console.log(event.keyCode);
});
$("divId").keydown(function(){
console.log("div keydown")
});
$("divId").keypress(function(){
console.log("div keypress")
});
$("#inputId").keydown(function(event){
console.log("input keydown");
});
$("#inputId").keypress(function(event){
console.log("input keypress");
});
</script>
</html>
var page = require('webpage').create();
var videoUrl = "click_test.html" //上面的那个页面
page.open(videoUrl , function () {
}
//通过这个函数监听浏览器的控制台输出
page.onConsoleMessage = function (msg) {
console.log('Page console msg: ' + msg);
};
window.setInterval(function(){
page.sendEvent('keypress', page.event.key.Space, null, null, null);
page.sendEvent('keydown', page.event.key.Space, null, null, null);
console.log("page key press");
}, 1000);
输出结果是:
...
Page console msg: doc keypress
Page console msg: 0
Page console msg: doc keydown
Page console msg: 32
page key press
...
我们回去看页面的脚本,我们可以发现我们监听的keypress事件有document,div,input这三个。但是只有document的事件被触发了。那么我们怎么让我们的子元素的keypress事件触发呢?
我们通过page.evaluate()函数来获取网页的元素,来触发元素的事件。
var page = require('webpage').create();
var videoUrl = "click_test.html" //上面的那个页面
page.open(videoUrl , function () {
page.includeJs("jquery.min.js", function(){
window.setInterval(function(){
page.evaluate(function() {
console.log("in evaluate function");
var ev = document.createEvent("KeyboardEvent");
ev.initKeyEvent("keydown", // typeArg,
true, // canBubbleArg,
true, // cancelableArg,
null, // viewArg, Specifies UIEvent.view. This value may be null.
false, // ctrlKeyArg,
false, // altKeyArg,
false, // shiftKeyArg,
false, // metaKeyArg,
39, // keyCodeArg,
0); // charCodeArg);
//document.querySelector("a").dispatchEvent(ev);
document.getElementById('divId').dispatchEvent(ev);
document.getElementById('inputId').dispatchEvent(ev);
//page.sendEvent('keypress', page.event.key.Right, null, null, null);
});
}, 5000);
});
});
输出的结果如下:
...
Page console msg: in evaluate function
Page console msg: doc keydown
Page console msg: 39
Page console msg: input keydown
Page console msg: doc keydown
Page console msg: 39
...
这里我对input,div这两个元素发送了keydown事件,但是,doc捕获了了2此,input捕获了一次。这是因为一般的div不能触发keydown事件。
对优酷视频网站触发快进事件
通过观察,打开优酷视频网站之后,点击右方向键,页面向右移动。鼠标点击视频之后,再点击右方向键,视频就可以快进了。
猜想
鼠标点击视频之后,focus到视频的元素,键盘事件传到视频元素,触发快进。
我将键盘事件发给这个flash播放器,可是没有效果。
document.getElementById('movie_player').dispatchEvent(ev);
到底是为什么呢?我查看了优酷的注册事件,键盘事件就那么4个
keydown:
1.
(function (t){t=t||B.event;var e=t.target||t.srcElement;a(e,t)})
2.
(function(t) {
if (y.railslocked && 0 == y.page.maxh) return !0;
t = t ? t : window.e;
var e = y.getTarget(t);
if (e && /INPUT|TEXTAREA|SELECT|OPTION/.test(e.nodeName)) {
var n = e.getAttribute("type") || e.type || !1;
if (!n || !/submit|button|cancel/i.tp) return !0
}
if (l(e).attr("contenteditable")) return !0;
if (y.hasfocus || y.hasmousefocus && !a || y.ispage && !a && !r) {
var i = t.keyCode;
if (y.railslocked && 27 != i) return y.cancelEvent(t);
var o = t.ctrlKey || !1,
s = t.shiftKey || !1,
c = !1;
switch (i) {
case 38:
case 63233:
y.doScrollBy(72), c = !0;
break;
case 40:
case 63235:
y.doScrollBy(-72), c = !0;
break;
case 37:
case 63232:
y.railh && (o ? y.doScrollLeft(0) : y.doScrollLeftBy(72), c = !0);
break;
case 39:
case 63234:
y.railh && (o ? y.doScrollLeft(y.page.maxw) : y.doScrollLeftBy(-72), c = !0);
break;
case 33:
case 63276:
y.doScrollBy(y.view.h), c = !0;
break;
case 34:
case 63277:
y.doScrollBy(-y.view.h), c = !0;
break;
case 36:
case 63273:
y.railh && o ? y.doScrollPos(0, 0) : y.doScrollTo(0), c = !0;
break;
case 35:
case 63275:
y.railh && o ? y.doScrollPos(y.page.maxw, y.page.maxh) : y.doScrollTo(y.page.maxh), c = !0;
break;
case 32:
y.opt.spacebarenabled && (s ? y.doScrollBy(y.view.h) : y.doScrollBy(-y.view.h), c = !0);
break;
case 27:
y.zoomactive && (y.doZoom(), c = !0)
}
if (c) return y.cancelEvent(t)
}
})
3.
(function (t){var e=t.ctrlKey||!1;e&&(y.wheelprevented=!0)})
keyup:
(function (t){var e=t.ctrlKey||!1;e||(y.wheelprevented=!1)})
这里只有第2个函数详细一点,那我们仔细看看第二个函数的代码,可是也没有发现它监控如何处理快进的,只是处理了怎么去左右上下滑动。我现在就卡在如何触发快进的功能了!
如何触发快进功能是我接下来的需要实现的目标。