關於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能力還處於最低級的探索階段,如果有什么寫的不好或者錯誤的地方,請不吝指出,謝謝)

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM