2012-08-31 24 views
8

我需要的是成品呈現頁面與當前風格的HTML(也許內聯),並完成了運行腳本,使用服務器端應用程序將只有一個URL(沒有額外的信息,如餅乾,沒有POST,沒有阻礙形式等)。HTML內容與完成呈現頁面的當前樣式(也許內聯),並完成運行腳本

使用瀏覽器庫的臨時運行的瀏覽器或獨立實用程序的網橋/代理是可接受的解決方案(但是,所選的瀏覽器或瀏覽器庫必須在所有主要平臺上可用,並且必須能夠無需運行一個OS GUI存在或安裝)。

一個可選的要求是事後刪除所有腳本(已經有代表這個單獨的解決方案,將其添加在這裏,因爲也許給出的答案就能去除的腳本,同時渲染或類似的東西)。

如何獲取(使用data URI)在CURENT HTML文檔的當前樣式(也許內聯)和當前圖像的單個.html文件在HTML + CSS的快照?

如果可以使用純PHP這將是一個加號來實現(雖然我懷疑這一點,我還沒有發現什麼有趣的事)。

編輯:我知道如何加載HTTP資源,並得到HTML一個URL,這不是我要找的;)

編輯2 例輸入HTML:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html> 
    <head> 
     <title></title> 

     <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> 

     <link rel="stylesheet" type="text/css" href="/css/example.css"> 
     <script type="text/javascript" src="/javascript/example.js"></script> 

     <script type="text/javascript"> 
      window.addEventListener("load", 
       function(event){ 
        document.title="New title"; 

        document.getElementById("pic_0").style.border="0px"; 
       } 
      ); 
     </script> 
     <style type="text/css"> 
      p{ 
       color: blue; 
      } 
     </style> 
    </head> 
    <body> 
     <p>Hello world!</p> 
     <p> 
      <img 
       alt="" 
       style="border: 1px" 
       id="pic_0" 
       src="http://linuxgazette.net/144/misc/john/helloworld.png" 
      > 
     </p> 
    </body> 
</html> 

輸出示例:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html> 
    <head> 
     <title>New title</title> 

     <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> 

     <style type="text/css"> 
      b{font-weight: bold} 
     </style> 

     <style type="text/css"> 
      p{ 
       color: blue; 
      } 
     </style> 
    </head> 
    <body> 
     <p>Hello world!</p> 
     <p> 
      <img 
       alt="" 
       style="border: 0px" 
       id="pic_0" 
       src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoBAMAAAB+0KVeAAAAK3RFWHRDcmVhdGlvbiBUaW1lAFYgMzEgYXVnLiAyMDEyIDE3OjU4OjU1ICswMjAwWMdbPwAAAAd0SU1FB9wIHw8ABeoUyU4AAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAAABlBMVEX///8AAABVwtN+AAAAXklEQVR42uWQUQ6AMAhD6Q3a+19WqsawwMf+NLEfy3iDlC7idTGQp/YglFAsUMqSwjlQOhN3mIMTHDq70SeEWBbt0EG8POWkDySvmCh/SssvNfwIfb+hFmgjFKPf6gDQBAQ368m09AAAAABJRU5ErkJggg==" 
      > 
     </p> 
    </body> 
</html> 

通知如何<title>標記已更改,border: 1px如何變爲border: 0px,圖像URL如何轉換爲data URI

例如,一些這些變換(內聯的CSS和<title>標籤)可以檢查使用谷歌瀏覽器檢查器中的文檔時觀察到的。

編輯3:使用頁面內容(樣式和圖像)替換外部資源並刪除javascript是一個簡單的部分。最難的部分是在運行javascript之後計算CSS樣式。

編輯4也許這可以使用注入的JavaScript來完成(仍然需要瀏覽器控制)?

+2

您需要綁定到一個渲染引擎[例如: Webkit] - 這可能是一個不錯的開始:http://stackoverflow.com/questions/4362855/php-read-an-http-url-and-write-it-to-pdf – moonwave99

+0

@ moonwave99是的我知道wkhtmltopdf: )如果這是開始,我有很多工作要做...... :( –

+0

嗯,我認爲渲染爲PDF是一個加號 - 應該有一個步驟,當他們在代碼中獲得純html + css時,如果你敢潛入圖書館,你可能會得到你需要的數據^^ – moonwave99

回答

6

PhantomJS是用JavaScript API無頭(GUI-更少)的WebKit。 它運行在所有主要平臺上,正如我在我的問題中所要求的。

它可以運行Javascript腳本來控制無GUI網頁瀏覽器。它有一個強大的API,以及大量的例子。

在我的業餘時間在過去的2-3天我寫的解決我的問題,它精美涵蓋了所有的要求。我還沒有找到一個它不工作的網頁。

用法,命令行:

phantomjs save_as_html.js http://stackoverflow.com/q/12215844/584490 saved.html 

Javascript在一切加載後都允許運行n秒,即使是完全由javascript生成的網頁,也應該可以運行。

注:

  • 如果可能的話,資源的XHR加載是首選了​​HTML5的畫布渲染,因爲減少文件大小和防止質量損失的(重複使用原始文件是比什麼都好)。

  • <link><img>標記保留在原位,data: URI分別在href和src屬性中使用,而不是URL。對於background-image也是如此,這是在所有DOM節點上使用getComputedStyle()讀取的。

  • <script>標籤和事件處理程序屬性被刪除。

  • <link>標籤與rel="alternative"也被刪除(也許他們不應該,而是固定到絕對URL,如果相對)。

  • 目前尚未處理,其src屬性設置爲about:blank

請注意,所有跨站點腳本安全限制都已取消,以便可以加載所有資源。確保在使用Facebook帳戶的一些祕密憑證時不要嘗試保存惡意網頁:)。

save_as_html.js內容:

//http://stackoverflow.com/a/12256190/584490 

var page = require('webpage').create(); 
page.onConsoleMessage = function (msg) { console.log(msg); }; 

var system = require('system'); 
var address, output, size; 


if (system.args.length!=3) 
{ 
    console.log('Usage: save_as_html.js URL filename'); 
    phantom.exit(1); 
} 
else 
{ 
    address = system.args[1]; 
    output = system.args[2]; 

    page.viewportSize = {  
     width: 1680, 
     height: 1050, 
    }; 

    //SECURITY_ERR: DOM Exception 18: An attempt was made to break through the security policy of the user agent. 
    //Enable cross site scripting: 
    page.settings.XSSAuditingEnabled=false; 
    page.settings.localToRemoteUrlAccessEnabled=true; 
    page.settings.webSecurityEnabled=false; 

    page.settings.userAgent="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"; 
    page.settings.ignoreSslErrors=true; 

    page.open(address, function (status){ 
     if (status!=='success') 
     { 
      console.log("Unable to load URL, returned status: "+status); 
      phantom.exit(1); 
     } 
     else 
     { 
      window.setTimeout(function(){ 
       page.evaluate(function(){ 
        var nodeList=document.getElementsByTagName("*"); 

        var arrEventHandlerAttributes=[ 
         "onblur", "onchange", "onclick", "ondblclick", "onfocus", "onkeydown", "onkeyup", "onkeypress", "onkeyup","onload", 
         "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onreset", "onselect", "onsubmit", "onunload" 
        ]; 


        //http://stackoverflow.com/a/7372816/584490 
        var base64Encode=function(str) 
        { 
         var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/"; 
         var out = "", i = 0, len = str.length, c1, c2, c3; 
         while (i < len) { 
          c1 = str.charCodeAt(i++) & 0xff; 
          if (i == len) { 
           out += CHARS.charAt(c1 >> 2); 
           out += CHARS.charAt((c1 & 0x3) << 4); 
           out += "=="; 
           break; 
          } 
          c2 = str.charCodeAt(i++); 
          if (i == len) { 
           out += CHARS.charAt(c1 >> 2); 
           out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); 
           out += CHARS.charAt((c2 & 0xF) << 2); 
           out += "="; 
           break; 
          } 
          c3 = str.charCodeAt(i++); 
          out += CHARS.charAt(c1 >> 2); 
          out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); 
          out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); 
          out += CHARS.charAt(c3 & 0x3F); 
         } 
         return out; 
        }; 


        for(var n=nodeList.length-1; n>0; n--) 
        { 
         try 
         { 
          var el=nodeList[n]; 

          if(el.nodeName=="IMG" && el.src.substr(0, 5)!="data:") 
          { 
           /*var canvas=document.createElement("canvas"); 

           canvas.width=parseInt(el.width); 
           canvas.height=parseInt(el.height); 

           var ctx=canvas.getContext("2d"); 
           ctx.drawImage(el, 0, 0); 
           el.src=canvas.toDataURL();*/ 

           var xhr=new XMLHttpRequest(); 

           xhr.open(
            "get", 
            el.src, 
            /*Asynchronous*/ false 
           ); 

           xhr.overrideMimeType("text/plain; charset=x-user-defined"); 

           xhr.send(null); 

           var strResponseContentType=xhr.getResponseHeader("Content-type").split(";")[0].replace(/[^a-z0-9\/-]/gi, ""); 
           el.src="data:"+strResponseContentType+";base64,"+base64Encode(xhr.responseText); 
          } 
          else if(el.nodeName=="LINK") 
          { 
           if(el.rel=="alternate") 
           { 
            el.parentNode.removeChild(el); 
           } 
           else if(el.href.substr(0, 5)!="data:") 
           { 
            var xhr=new XMLHttpRequest(); 

            xhr.open(
             "get", 
             el.href, 
             /*Asynchronous*/ false 
            ); 

            xhr.overrideMimeType("text/plain; charset=x-user-defined"); 

            xhr.send(null); 

            //var strResponseContentType=xhr.getResponseHeader("Content-type").split(";")[0].replace(/[^a-z0-9\/-]/gi, ""); 
            //el.href="data:"+strResponseContentType+";base64,"+base64Encode(xhr.responseText); 
            el.href="data:"+el.type+";base64,"+base64Encode(xhr.responseText); 
           } 

           continue; 
          } 
          else if(el.nodeName=="SCRIPT") 
          { 
           el.parentNode.removeChild(el); 

           continue; 
          } 
          else if(el.nodeName=="IFRAME") 
          { 
           el.src="about:blank"; 

           continue; 
          } 

          for(var z=arrEventHandlerAttributes.length-1; z>=0; z--) 
           el.removeAttribute(arrEventHandlerAttributes[z]); 

          var strBackgroundImageURL=window.getComputedStyle(el).getPropertyValue("background-image").replace("/[\s]/g", ""); 
          if(strBackgroundImageURL.substr(0, 4)=="url(" && strBackgroundImageURL.substr(4, 5)!="data:") 
          { 
           strBackgroundImageURL=strBackgroundImageURL.substr(4, strBackgroundImageURL.length-5); 

           /*var imageTemp=document.createElement("img"); 
           imageTemp.src=strBackgroundImageURL; 

           imageTemp.onload=function(e){ 
            var canvas=document.createElement("canvas"); 

            canvas.width=parseInt(imageTemp.width); 
            canvas.height=parseInt(imageTemp.height); 

            var ctx=canvas.getContext("2d"); 
            ctx.drawImage(imageTemp, 0, 0); 
            el.style.backgroundImage="url("+canvas.toDataURL()+")"; 
           }; 

           if (imageTemp.complete) 
            imageTemp.onload(); 
           */ 

           var xhr=new XMLHttpRequest(); 

           xhr.open(
            "get", 
            strBackgroundImageURL, 
            /*Asynchronous*/ false 
           ); 

           xhr.overrideMimeType("text/plain; charset=x-user-defined"); 

           xhr.send(null); 

           var strResponseContentType=xhr.getResponseHeader("Content-type").split(";")[0].replace(/[^a-z0-9\/-]/gi, ""); 
           el.style.backgroundImage="url("+"data:"+strResponseContentType+";base64,"+base64Encode(xhr.responseText)+")"; 
          } 

          if(el.nodeName=="A") 
          { 
           el.href="#";//TODO convert relative paths to absolute ones (keep URLs); 
           el.setAttribute("onclick", "return false;");//TODO: remove this when the above is fixed. 
          } 
          else if(el.nodeName=="FORM") 
          { 
           el.setAttribute("action", ""); 
           el.setAttribute("onsubmit", "return false;"); 
          } 
         } 
         catch(error) 
         { 
          //what can be done about it? 
         } 
        } 
       }); 

       require("fs").write(output, page.content, "w"); 

       phantom.exit(); 
      }, 1000); 
     } 
    }); 
} 

+1

您可以使用原生'btoa'而不是自制'base64Encode'函數。 – vbarbarosh

相關問題