一 重定向
爬虫抓取的过程中,遇到重定向时需要识别出重定向后的url。
总结下,主要有如下3类重定向:
1 http协议的3XX
301 永久移动
302 临时
305 必须通过指定定代理才能访问相应资源
307 临时
比如 访问http://www.meilishuo.com/,响应如下
HTTP/1.1 301 Moved Permanently Server: mls/1.1 Date: Mon, 01 Jul 2013 07:19:15 GMT Transfer-Encoding: chunked Connection: keep-alive Location: /welcome Cache-Control: no-cache,must-revalidate,no-store Pragma: no-cache
2 html的meta字段
meta字段的refresh属性
<html> <meta http-equiv="refresh" content="1; url=http://www.petermao.com" /> <body> </body></html>
3 js重定向
有好几种,后面总结下,这里给个简单例子:
<html><body> <script type='text/javascript'>window.open("http://www.petermao.com") </script> </body></html>
解决方案:
第一种在获得http首部后,根据状态码,就可以判断是否重定向,再根据location属性来获取重定后的url。
第二种已经获得了网页内容。一般是是通过正则匹配出meta,refresh以及content属性,再获取重定向后的url。当然也可以进行dom解析,正确性会好点,但可能会存在性能问题,个人感觉没有这个必要。正则匹配即可。
第三种是难点。我们先来看看js重定向有哪些表现形式。
二 JS重定向分类
主要有如下几类,这里得到了朋友@秋声落叶的帮助,欢迎其他朋友补充。
1 window:
window.open(url)
2 location属性:
location=url; location.href=url; window.location=url; document.location=url; window.location.href=url; window.location.replace(url) window.location.assign(url) location.replace(url); location.assign(url); self.location=url; top.location=url;
3 navigate方法:
window.navigate(url) top.navigate(url);
4 document+meta字段
document.write('<META HTTP-EQUIV="refresh" content="0;url=http://www.petermao.com">'); document.writeIn('<META HTTP-EQUIV="refresh" content="0;url=http://www.petermao.com">'); document.close();
三 JS重定向识别的1个思路
1 问题
要识别前面的重定向,通过正则可能不现实,比如前面的JS可以封装在函数里,通过变量来传递重定向后的url。比如前面的location的变种:
<html> <head> <script type='text/javascript'> function AutoRedirect(seconds,redirectUrl){ setTimeout("Redirect('"+redirectUrl+"')",seconds);} function Redirect(redirectUrl){ location.href=redirectUrl; } </script> </head> <body> <script type='text/javascript'>AutoRedirect(2, 'http://www.petermao.com'); </script></body></html>
此时要获得这些JS,只能通过完整的解析了,基于DOM树可以完整的获取这些JS。将script当作html的一个标签即可。
在完整获得这些JS后,怎么知道其实际表示什么内容了?这个就需要JS运行环境了。可以考虑开源的V8与SpiderMonkey了。(本文只考虑V8)。
2 使用V8的问题
不过这里有个问题,对于在浏览器中运行的html网页,浏览器会提供1个叫做window的全局对象,像location、top等对象,以及settimeout等函数就是window的子属性/方法,而这个window的实现被封装在BOM模型(浏览器对象模型)中,该模型不像DOM、JS那样有统一的标准。V8只提供JS运行环境,像window等对象是由浏览器内核提供的,比如webkit中有BOM的实现。之前曾短暂研究过webkit,不过这货太过复杂,还未入门,抽取其中的库来解析与运行网页,不知道是否可行,有研究的朋友可以一起讨论下。前面那些抽取出来的JS,如果直接交给V8来运行,会出现undefined的错误,也就是window、location、settimeout等在V8中并没有定义。
3 最后
在只有V8的情况下,如何运行网页中的JS了?那就需要mock了。
V8中表示JS运行环境的是Context对象,我们可以一开始在Context中初始化window等全局对象。然后轮流运行解析出来的JS脚本,最后通过location属性来获取重定向后的url。
最后给出部分初始化mocker代码如下,完整的需要mock前面给出的JS重定向的各种变形。实际mock可以参考Node.js,该货基于V8,提供了一些有用函数的实现,也有利于了解V8的特性。
至于V8的使用,还是翻翻官方文档吧,这里就不说了。
类似的,对于有些JSON数据,或者AJAX的抓取,应该也可以考虑类似的思路。欢迎大家讨论。
window = {}; location = {}; window.location = {}; location.href = {}; window.location.href = {}; top={}; parent={}; location.assign = function (url) { window.location.href = url; } location.replace = function (url) { window.location.href = url; } window.location.assign = location.assign; window.location.replace = location.replace; ---