关于Iframe跨域,判断加载等的一些使用心得

  根据同源策略,ajax在非同源的情况下的访问是受限的,为解决跨域交互的问题,我们会想到利用jsonp 或者 Iframe 的 window.name 来传输数据。如果对两个域都有控制权,我们还会使用window.domain 使非同源的交互成为可能。 抑或是用代理页面这种中间层来传递数据等等。

  跨域访问的方法很多,根据自己的需求来选择合适的方案。

  最近,公司有个业务,抓取一个安全性很高的网站的数据,这个网站绑定了机器上的一些物理信息以及IP地址等,一个账号只能在一台机上面运行。一般而言,抓取数据,使用服务器代码(C#,java,ruby 等都可以),将页面下载下分析就可以了,也不会存在跨域访问的问题。然而,由于安全性的问题,这个网站拒绝一般情况下页面下载的请求。所以这个方案不可行。

  被抓取页面的结构(超链接列表对应页面的内容是我们想抓取的)

  

  此方案不可行,只能换个解决方案:用一个页面,嵌套被抓去的页面,主页面再读取Iframe的内容。这个方案咋乍看起来顺理成章,然而,在跨域的情况下,主页面无法操作Iframe的内容。此方案也流产了,原因是对于同源策略的不了解。

  最终方案:

  1. 登陆系统,以获得访问权限;

  2. 往页面中注入 js ,以获得对页面的操控权;

  3. 获取超链接连表,放入队列中,在页面中生成Ifream,指向超链接,解决跨域访问的问题;

  4. 再抓取Ifream加载的内容,以jsonp的形式发回服务器;

  在我上一篇博文中,我们知道怎么向一个页面中注入js,可以使用下面的代码:

注入页面的js代码
1 javascript: void((function() {
2     var d = document,
3     e = d.createElement("script");
4     e.setAttribute("charset", "UTF-8");
5     e.setAttribute("src", "the js's url" + Math.floor(new Date / 1E7));
6     d.body.appendChild(e)
7 })());

  将代码保存到收藏夹,然后打开想注入的页面,点击该收藏就OK了。

  注入完js后,我们对现有页面有了绝对控制权,当然,顺带也解决了跨域访问的问题(所有iframe都在受控的页面中生成,而iframe指向的页面跟主页面是同域的)。这样我们便可以很好的操作iframe生成的dom对象了。当然,刚开始要拿frame生成的对象时,总是报错“对象为空或不存在”,其实这跟iframe的生命周期有关,简单来说,iframe相当于一个新的浏览器窗口,想要操作窗口内的对象,得确保该对象已经加载完成。下面的代码,便是判断iframe是否加载结束:

判断iframe是否加载结束
 1 if (iframeobj.attachEvent) {
 2     iframeobj.attachEvent("onload",
 3     function() {
 4         alert(document.frames[iframename].document.body.innerHTML);
 5     });
 6 } else {
 7     iframeobj.onload = function() {
 8         alert(document.frames[ifreamname].document.body.innerHTML);
 9     };
10 }
11 }

  当ifream加载结束,我们才可拿到iframe的window对象或document对象,这时我们才可以为所欲为。

  使用iframe自动抓取数据的过程中遇到了一个问题,就是iframe会在页面加载时执行一些脚本,例如弹出提示框,这样,便会阻塞了主线程的运行。如何解决这个问题?首先想到的应该就是重写iframe的alert事件,代码如下:

重写iframe的alert事件
1 document.getElementById(iframeid).contentWindow.alert = function() {
2     return null;
3 }
4 或者
5 window.frames[iframename].window.alert = function() {
6     return null;
7 }

  iframe的alert事件重写结束了,现在又遇到了一个问题:服务器后台代码与javascript代码加载顺序的问题!也就是说,在我重写子页面的alert事件之前,已经先执行了一个alert提示,因而无法屏蔽掉它!!郁闷。换个想法,直接用noscript标签来禁用iframe的所有脚本,然而在动态生成iframe的时候会报错。后来,想起了富文本编辑器,也是用iframe,会屏蔽脚本,而且还能获取到内容。其实,富文本编辑器的原理很简单,就是打开iframe的designMode和editMode,所以,在创建iframe的时候,我加了以下的代码:

1 document.getElementById(iframeid).contentWindow.document.designMode = 'On';

  当然,这不是真正意义上的解决iframe弹提示框的问题,这样做的应用场景是:想获取页面的内容,却不想被提示框阻塞。

  现在,想抓取的内容抓取到了,把内容发送回服务器就完成了,刚开始的做法,动态创建超链接a,拼接带参的url指向服务器,模拟点击事件触发超链接。这样可以解决问题,但是我抓取的内容有20W之多,每次抓取在3000内,我不可能弹出3000个浏览器窗口!!!转眼想想,我不是在页面中创建了很多iframe么,把超链接的target指向iframe,这样便可以解决这个问题,而且,我们还对页面的iframe有绝对控制器。但是问题又来了,这时,target指向iframe,而超链接的url又与页面不同域,在IE下会报错,而抓取的网站只能在IE下运行!!!万恶的IE啊!!!

  行不通,就使用jsonp来解决跨域交互的问题。根据同源策略,浏览器会隔离各个域的资源,使它们间资源不共享,不能相互访问。然而,有个特例,那便是script!!同源策略允许加载不同域的javascript!!创建jsonp代码如下(当然,你可以在参数中加入回调函数):

最简单的jsonp
1 var _jsonpscript = document.createElement("script");
2 _jsonpscript.setAttribute("type", "text/javascript");
3 _jsonpscript.setAttribute("id", "jsonpscript");
4 _jsonpscript.setAttribute("src", serviceUrl? +参数(用&分隔));
5 document.body.appendChild(_jsonpscript);

  至此,完成了从抓取到回传的整个过程。(本人的javascript能力还处于最低级的探索阶段,如果有什么写的不好或者错误的地方,请不吝指出,谢谢)

  

posted @ 2012-06-20 11:16  三度空间  阅读(5085)  评论(6编辑  收藏  举报