Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国内外一级毛片,欧美视频亚洲,香蕉免费看一区二区三区

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          JavaScript SDK 設(shè)計(jì)指南

          JavaScript SDK 設(shè)計(jì)指南

          文地址:http://sdk-design.js.org/

          介紹

          本指南為您介紹了在臺(tái)式機(jī)和移動(dòng)網(wǎng)絡(luò)在不同的平臺(tái)和瀏覽器( < 99.99 %我可能會(huì)跳過一些瀏覽器)開發(fā)的JavaScript SDK ,對(duì)于那些非瀏覽器開發(fā)的支持(硬件,嵌入式,節(jié)點(diǎn)/ IO JS )被排除在本文檔之外,在未來予以考慮。

          因?yàn)槲覜]有找到一個(gè)關(guān)于設(shè)計(jì)JavaScript SDK的比較好的文檔,所以我在這里收集并記下了我個(gè)人的經(jīng)驗(yàn)。這份文檔已經(jīng)寫了好幾個(gè)月,有一點(diǎn)我們需要知道,JavaScript的SDK-設(shè)計(jì)不僅僅是設(shè)計(jì)SDK本身,這也是有關(guān)于開發(fā)者與設(shè)備瀏覽器中間的聯(lián)系。我們寫的越多,越會(huì)更多的思考我們真正關(guān)心的是不同平臺(tái)和瀏覽器之間的性能和兼容問題。你可以根據(jù)情況自由的更改或者完全放棄我在文章里列出的建議。

          什么是SDK

          我知道它確實(shí)是很普通很常見。一般是一些軟件工程師為特定的軟件包、軟件框架、硬件平臺(tái)、操作系統(tǒng)等建立應(yīng)用軟件時(shí)的開發(fā)工具的集合。通常一個(gè)SDK包含一個(gè)或多個(gè)API,編程工具和檔。

          設(shè)計(jì)理念

          這取決于你的SDK用來干什么的,但是它必須具備原生的,短,速度快,干凈,可讀可測(cè)試特性。用原生javascript寫,不要用像Livescript, Coffeescript, Typescript和其它的編譯語(yǔ)言。必須有更好的方法來編寫自己的javascript原生代碼比別人更快。請(qǐng)不要在你的SDK里用JQuery,除非它非常有必要。你可以使用其它的類似jQuery的庫(kù),譬如zetpo.js,用于DOM操作,如果你需要用到HTTP Ajax請(qǐng)求,可以使用另外一種輕量庫(kù)像window.fetch。

          每一次的SDK版本發(fā)布,確保它不僅適用于舊版本而且適應(yīng)于未來的新版本。所以,記得為你的SDK寫文檔,代碼要寫注釋,同時(shí)做好單元測(cè)試和用戶場(chǎng)景測(cè)試。

          適應(yīng)范圍

          基于《Third-Party JavaScript》這本書。在何種情況下,你應(yīng)該為你的應(yīng)用設(shè)計(jì)一個(gè)JavaScript SDK?

          • 嵌入式組件 – 嵌入在出發(fā)布者的網(wǎng)頁(yè)中的交互式應(yīng)用程序(Disqus, Google Maps, Facebook Widget)。
          • 分析與數(shù)據(jù) – 搜集網(wǎng)站訪問者以及其與網(wǎng)站互動(dòng)的數(shù)據(jù)信息。(GA, Flurry, Mixpanel)
          • web服務(wù)API封裝 -對(duì)于發(fā)展與外部Web服務(wù)通信的客戶端應(yīng)用程序。(Facebook的圖形API)

          在什么情況下,我們應(yīng)該在JavaScript環(huán)境中使用SDK呢?大家可以想想還有其它情沒?

          引入SDK

          建議你采用異步加載腳本的方式。我們要優(yōu)化網(wǎng)站的用戶體驗(yàn),所以不希望我們的SDK庫(kù)阻塞其它主要進(jìn)程。

          異步加載

          (function() {vars=document.createElement('script');s.type='text/javascript';s.async=true;s.src='http://xxx.com/sdk.js';varx=document.getElementsByTagName('script')[0];x.parentNode.insertBefore(s, x);})();**
          

          在新的現(xiàn)代瀏覽器(chrome)你可以使用

          <script asyncsrc="http://xxx.com/sdk.js"></script>
          

          傳統(tǒng)加載方法

          <script type="text/javascript"src="http://xxx.com/sdk.js"></script>
          

          對(duì)比:

          下面是簡(jiǎn)單的圖形顯示異步加載和傳統(tǒng)同步加載方式之間的區(qū)別

          異步:

          |----A-----|
           |-----B-----------|
           |-------C------|
          

          同步:

          |----A-----||-----B-----------||-------C------|
          

          異步和延遲腳本執(zhí)行解釋

          異步的問題

          當(dāng)你使用異步加載的時(shí)候,將會(huì)出現(xiàn),頁(yè)面中的函數(shù)無法正常調(diào)用SDK方法的情況。

          <script>
           (function () {
           var s=document.createElement('script');
           s.type='text/javascript';
           s.async=true;
           s.src='http://xxx.com/sdk.js';
           var x=document.getElementsByTagName('script')[0];
           x.parentNode.insertBefore(s, x);
           })();
           // execute your script immediately hereSDKName('some arguments');
          </script>
          

          結(jié)果會(huì)報(bào)undefined錯(cuò)誤,因?yàn)镾DKName()在腳本加載之前執(zhí)行了。所以我們應(yīng)該使用點(diǎn)技巧讓腳本正確執(zhí)行。把事件保存在SDKName.q數(shù)組里,SDK初始化的時(shí)候執(zhí)行SDKName.q。

          <script>
           (function () {
           // add a queue event here
           SDKName=SDKName ||function () {
           (SDKName.q=SDKName.q|| []).push(arguments);
           };
           var s=document.createElement('script');
           s.type='text/javascript';
           s.async=true;
           s.src='http://xxx.com/sdk.js';
           var x=document.getElementsByTagName('script')[0];
           x.parentNode.insertBefore(s, x);
          })();
           // execute your script immediately hereSDKName('some arguments');
           </script>
          

          或者用 [ ].push

          <script>
           (function () {
           // add a queue event here
           SDKName=window.SDKName|| (window.SDKName=[]);
           var s=document.createElement('script');
           s.type='text/javascript';
           s.async=true;
           s.src='http://xxx.com/sdk.js';
           var x=document.getElementsByTagName('script')[0];
           x.parentNode.insertBefore(s, x);
          })();
          // execute your script immediately hereSDKName.push(['some arguments']);
          </script>
          

          其他方式

          還有其它不同方式加載腳本

          Import in ES2015

          import"your-sdk";
          

          模塊加載

          這里有完整的源碼和非常棒的教程. Loading JavaScript Modules

          module('sdk.js',['sdk-track.js', 'sdk-beacon.js'],function(track, beacon) {
          // sdk definitions, split into local and global/exported definitions// local definitions// exports
          });
          // you should contain this "module" method
          (function () {
          var modules={}; // private record of module data// modules are functions with additional informationfunctionmodule(name,imports,mod) {
          // record module informationwindow.console.log('found module '+name);
          modules[name]={name:name, imports: imports, mod: mod};
          // trigger loading of import dependenciesfor (var imp in imports) loadModule(imports[imp]);
          // check whether this was the last module to be loaded// in a given dependency grouploadedModule(name);
          }
          // function loadModule// function loadedModulewindow.module=module;
          })();
          

          SDK版本

          避免使用自己的特例作為版本名稱像

          標(biāo)識(shí)-v<時(shí)間戳>.js 標(biāo)識(shí)-v<日期>.js 標(biāo)識(shí)-v1-v2.js

          它可能導(dǎo)致使用SDK的開發(fā)者很混亂不知道哪個(gè)是最新版本。

          使用 Semantic Versioning (語(yǔ)義化版本規(guī)范)去定義SDK的版本號(hào)以”大.小.補(bǔ)丁”形式。

          版本以v1.0.0 v1.5.0 v2.0.0的形式,會(huì)讓使用者搜索跟蹤日志文件更容易。

          通常情況下,我們會(huì)有不同的方式去聲明SDK的版本,這取決于具體針對(duì)的業(yè)務(wù)和設(shè)計(jì)。

          使用查詢字符串路徑

          http://xxx.com/sdk.js?v=1.0.0
          

          使用文件夾命名

          http://xxx.com/v1.0.0/sdk.js
          

          使用主機(jī)名或者子域名

          http://v1.xxx.com/sdk.js
          

          為了以后版本的升級(jí)迭代,建議用stable unstable alpha latest experimental 版本。

          http://xxx.com/sdk-stable.js
          http://xxx.com/sdk-unstable.js
          http://xxx.com/sdk-alpha.js
          http://xxx.com/sdk-latest.js
          http://xxx.com/sdk-experimental.js
          

          更新日志文件

          你應(yīng)該注意到如果你升級(jí)你的SDK卻沒通知用戶,用戶不會(huì)知道。記得寫更新日志來記錄無論是主要、次要甚至bug修復(fù)等修改。這將是一個(gè)好的開發(fā)經(jīng)驗(yàn),我們能快速的跟蹤到SDK某個(gè)API的修改。所以保持更新日志 – Keep a Changelog, Github Repo

          每個(gè)版本的日志應(yīng)該有:

          [新增] 新功能.

          [更新] 修改現(xiàn)有的更能

          [廢棄] 在即將發(fā)布的版本中刪除某個(gè)功能.

          [刪除] 在這個(gè)版本中刪除棄用的功能.

          [修正] bug修復(fù)

          [安全] 邀請(qǐng)用戶對(duì)安全進(jìn)行升級(jí)

          命名空間

          在你的SDK里只定義一個(gè)全局命名空間,并且不要用太過通用的名字,避免和其它類庫(kù)名發(fā)生沖突。SDK的主體用(function () { … })()包裹。這種做法越來越普遍的應(yīng)用于各種流行的javascript類庫(kù)譬如jQuery,Node.js等等。這種創(chuàng)建私有的命名空間的技術(shù)很重要,有助于避免各種類庫(kù)之間命名的沖突。

          為了避免命名空間沖突

          學(xué)習(xí)Google Analytics的做法,你可以通過改變 ga的值來定義你自己的命名空間。

          (function(i,s,o,g,r,a,m) {i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
          (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
          m=s.getElementsByTagName(o) [0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
          })(window,document,'script','//www.google- analytics.com/analytics.js','ga');
          

          下面的是 openX的做法,支持通過給地址傳遞參數(shù)定義命名空間。

          <script src="http://your_domain/sdk?namespace=yourcompany"></script>
          

          存儲(chǔ)機(jī)制

          cookie

          使用cookie就會(huì)面臨復(fù)雜的作用域范圍問題,而且涉及到子域和路徑問題。

          比如在路徑 path=/下, cookie first=value1 在域名 http://github.com下, 另外一個(gè) cookie second=value2 在域名 http://sub.github.com下

          http://github.comhttp://sub.github.comfirst=value1??second=value2??

          有個(gè) cookie first=value1 在 http://github.com下, cookie second=value2 在 http://github.com/path1 另外一個(gè) cookie third=value3 在 http://sub.github.com下,

          http://github.comhttp://github.com/path1http://sub.github.comfirst=value1???second=value2???third=value3???

          檢查 Cookie 可讀寫

          給定一個(gè)域 (默認(rèn)當(dāng)前主機(jī)域名), 檢查cookie是否可讀寫。

          var checkCookieWritable=function(domain) {
          try {
           // Create cookie
           document.cookie='cookietest=1' + (domain ? '; domain=' + domain : '');
           var ret=document.cookie.indexOf('cookietest=') !=-1;
           // Delete cookie
           document.cookie='cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT' + (domain ? '; domain=' + domain : '');
           return ret;
          } catch (e) {
           return false;
          }
          };
          

          檢查第三方 Cookie 可讀寫

          檢查第三方cookie僅僅通過客戶端js是辦不到的,需要服務(wù)器端配合。

          寫 讀 刪除 Cookie 代碼

          代碼片段寫/讀/刪除cookie的腳本。

          var cookie={
          write: function(name, value, days, domain, path) {
           var date=new Date();
           days=days || 730; // two years
           path=path || '/';
           date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
           var expires='; expires=' + date.toGMTString();
           var cookieValue=name + '=' + value + expires + '; path=' + path;
           if (domain) {
           cookieValue +='; domain=' + domain;
           }
           document.cookie=cookieValue;
          },
          read: function(name) {
           var allCookie='' + document.cookie;
           var index=allCookie.indexOf(name);
           if (name===undefined || name==='' || index===-1) return '';
           var ind1=allCookie.indexOf(';', index);
           if (ind1==-1) ind1=allCookie.length;
           return unescape(allCookie.substring(index + name.length + 1, ind1));
          },
          remove: function(name) {
           if (this.read(name)) {
           this.write(name, '', -1, '/');
           }
          }
          };
          

          Session

          js寫不了session,需要服務(wù)器端寫。

          一個(gè)頁(yè)面的session會(huì)一直保存著只要瀏覽器是開著的即使頁(yè)面重新加載。打開一個(gè)新頁(yè)面會(huì)生成一個(gè)新的session。子窗口會(huì)和父窗口共享一個(gè)session。

          LocalStorage

          存儲(chǔ)的數(shù)據(jù)沒有時(shí)間限制。存儲(chǔ)數(shù)據(jù)量大(至少5MB)并且信息不會(huì)傳送到服務(wù)器。而且同一個(gè)域名從http和https訪問localStorage是不共享的。你可以在你的網(wǎng)頁(yè)上創(chuàng)建個(gè)iframe,然后用postMessage方法去傳值到父頁(yè)面。HOW TO?

          檢查 LocalStorage 可寫

          window.localStorage 并不是任何瀏覽器都支持,SDK在用之前要檢查是否可用。

          var testCanLocalStorage=function() {
          var mod='modernizr';
           try {
           localStorage.setItem(mod, mod);
           localStorage.removeItem(mod);
           return true;
           } catch (e) {
           return false;
           }
          };
          

          SessionStorage

          針對(duì)一個(gè) session 的數(shù)據(jù)存儲(chǔ)(當(dāng)用戶關(guān)閉瀏覽器窗口后,數(shù)據(jù)會(huì)被刪除).

          檢查 SessionStorage 可寫

          var checkCanSessionStorage=function() {
          var mod='modernizr';
          try {
          sessionStorage.setItem(mod, mod);
          sessionStorage.removeItem(mod);
          return true;
          } catch (e) {
          return false;
          }
          }
          

          事件

          在客戶端瀏覽器有很多事件加載、卸載、綁定等會(huì)存在兼容問題。polyfills是個(gè)解決不同平臺(tái)事件綁定的不錯(cuò)的解決方案。

          Document Ready

          確保整個(gè)頁(yè)面完成加載了再執(zhí)行SDK方法。

          // handle IE8+
          function ready (fn) {
          if (document.readyState !='loading') {
           fn();
          } else if (window.addEventListener) {
           // window.addEventListener('load', fn);
           window.addEventListener('DOMContentLoaded', fn);
          } else {
           window.attachEvent('onreadystatechange', function() {
           if (document.readyState !='loading')
           fn();
           });
          }
          }
          

          DOMContentLoaded - 所有DOM解析完會(huì)觸發(fā)整個(gè)事件 不需要等到樣式表、圖片等加載完。

          load 頁(yè)面完整加載。

          Message Event

          這里是實(shí)現(xiàn)iframe和父頁(yè)面之間的數(shù)據(jù)通信, 這里有文檔 API documentation.

          // in the iframe
          parent.postMessage("Hello"); // string
          //==========================================// in the iframe's parent
          // Create IE + others compatible event handler
          var eventMethod=window.addEventListener ? "addEventListener" : "attachEvent";
          var eventer=window[eventMethod];
          var messageEvent=eventMethod=="attachEvent" ? "onmessage" : "message";
          // Listen to message from child window
          eventer(messageEvent,function(e) {
          // e.origin , check the message origin
          console.log('parent received message!: ',e.data);
          },false);
          

          發(fā)送的數(shù)據(jù)是字符串, 對(duì)于使用更高級(jí)的json字符串. 不是所有的瀏覽器對(duì)支持 Structured Clone Algorithm on the parameter, (參數(shù)的結(jié)構(gòu)化克?。?。

          Orientation Change 橫屏事件

          檢測(cè)設(shè)備橫屏

          window.addEventListener('orientationchange', fn);
          

          獲取旋轉(zhuǎn)方向和角度

          window.orientation; //=> 90, -90, 0
          

          Screen portrait-primary(豎屏正方向), portrait-secondary(豎屏反方向), landscape-primary(橫屏正方向), landscape-secondary (橫屏反方向)(Experimental)

          // https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation
          var orientation=screen.orientation || screen.mozOrientation || screen.msOrientation;
          

          Request

          我們的SDK和服務(wù)器之間通信通過Ajax請(qǐng)求,因?yàn)槲覀冎牢覀兛梢允褂胘Query的Ajax 方法。但是有更好的方案來實(shí)現(xiàn)它。

          圖片預(yù)加載

          通過創(chuàng)建一個(gè)Image對(duì)象預(yù)加載一張圖片。為了防止瀏覽器緩存記得加上時(shí)間戳。

          (new Image()).src='http://xxxxx.com/collect?id=1111';
          

          要注意通過GET方式傳輸參數(shù)最大長(zhǎng)度是2048個(gè)字節(jié)(取決于不同的瀏覽器和服務(wù)器)。這里要做一些處理如果超過長(zhǎng)度。

          if (length > 2048) {
          // do Multiple Post (form)
          } else {
          // do Image Beacon
          }
          

          你可能遇到問題在使用encodeURI 還是 encodeURIComponent的時(shí)候,最好理解它們的區(qū)別。 See below.

          對(duì)于圖像加載成功/錯(cuò)誤回調(diào)

          var img=new Image();
          img.src='http://xxxxx.com/collect?id=1111';
          img.onload=successCallback;
          img.onerror=errorCallback;
          

          單個(gè) Post 請(qǐng)求

          普通表單發(fā)送一個(gè)對(duì)應(yīng)元素和值

          var form=document.createElement('form');
          var input=document.createElement('input');
          form.style.display='none';
          form.setAttribute('method', 'POST');
          form.setAttribute('action', 'http://xxxx.com/track');
          input.name='username';
          input.value='attacker';
          form.appendChild(input);
          document.getElementsByTagName('body')[0].appendChild(form);
          form.submit();
          

          多個(gè) Post 請(qǐng)求

          服務(wù)通常比較復(fù)雜,需要通過POST方法發(fā)送更多數(shù)據(jù)。

          function requestWithoutAjax( url, params, method ){
          params=params || {};
          method=method || "post";
          // function to remove the iframe
          var removeIframe=function( iframe ){
           iframe.parentElement.removeChild(iframe);
          };
          // make a iframe...
          var iframe=document.createElement('iframe');
          iframe.style.display='none';
          iframe.onload=function(){
           var iframeDoc=this.contentWindow.document;
           // Make a invisible form
           var form=iframeDoc.createElement('form');
           form.method=method;
           form.action=url;
           iframeDoc.body.appendChild(form);
           // pass the parameters
           for( var name in params ){
           var input=iframeDoc.createElement('input');
           input.type='hidden';
           input.name=name;
           input.value=params[name];
           form.appendChild(input);
           }
           form.submit();
           // remove the iframe
           setTimeout( function(){
           removeIframe(iframe);
           }, 500);
          };
          document.body.appendChild(iframe);
          }
          requestWithoutAjax('url/to', { id: 2, price: 2.5, lastname: 'Gamez'});
          

          Iframe

          當(dāng)你在需要在頁(yè)面中生成內(nèi)容時(shí)候,你可以通過iframe嵌入。

          var iframe=document.createElement('iframe');
          var body=document.getElementsByTagName('body')[0];
          iframe.style.display='none';
          iframe.src='http://xxxx.com/page';
          iframe.onreadystatechange=function () {
          if (iframe.readyState !=='complete') {
           return;
          }
          };
          iframe.onload=loadCallback;
          body.appendChild(iframe);
          

          清除iframe的邊框,內(nèi)部margin值。

          <iframe src="..."
           marginwidth="0"
           marginheight="0"
           hspace="0"
           vspace="0"
           frameborder="0"
           scrolling="no">
          </iframe>
          

          iframe中插入html

          <iframe id="iframe"></iframe>
          <script>
           var html_string="content <script>alert(location.href); </script>";
           document.getElementById('iframe').src="data:text/html;charset=utf-8," + escape(html_string);
           // alert data:text/html;charset=utf-8.....
           // access cookie get ERROR
           var doc=document.getElementById('iframe').contentWindow.document;
           doc.open();
           doc.write('<body>Test<script>alert(location.href);</script></body>');
           doc.close();
           // alert "top window url"
           var iframe=document.createElement('iframe');
           iframe.src='javascript:;\\\\'' + encodeURI('<html><body> <script>alert(location.href);</body></html>') + '\\\\'';
           // iframe.src='javascript:;"' + encodeURI((html_tag).replace(/\\\\"/g, '\\\\\\\\\\\\"')) + '"';
           document.body.appendChild(iframe);
           // alert "about:blank"
          </script>
          

          jsonp

          這種情況下,你的服務(wù)器需要響應(yīng)JavaScript 代碼,并讓瀏覽器執(zhí)行它,僅僅通過js腳本鏈接。

          (function () {
           var s=document.createElement('script');
           s.type='text/javascript';
           s.async=true;
           s.src='/yourscript? some=parameter&callback=jsonpCallback';
           var x=document.getElementsByTagName('script')[0];
          x.parentNode.insertBefore(s, x);
           })();
          

          關(guān)于jsonp你需要了解:

          • JSONP 只能通過GET請(qǐng)求。
          • JSONP 缺少錯(cuò)誤處理機(jī)制, 意味著你不能檢測(cè)代碼是否404還是500等狀態(tài)。
          • JSONP 請(qǐng)求是異步的。
          • 當(dāng)心 CSRF 攻擊。
          • 跨域通信。腳本響應(yīng)端(服務(wù)器端)不需要關(guān)心CORS。

          XMLHttpRequest

          自己寫XMLHttpRequest不是個(gè)好主意,因?yàn)槟阋速M(fèi)很多時(shí)間去做IE或者其它瀏覽器的兼容。這里提供一些現(xiàn)成的解決方案供大家參考:

          1 - window.fetch - A window.fetch JavaScript polyfill.

          2 - got - Simplified HTTP/HTTPS requests

          3 - microjs - list of ajax lib

          4 – more

          Maximum Number of Connection

          檢查不同瀏覽器的最大連接數(shù) browserscope

          調(diào)試

          模擬多個(gè)域

          你不需要注冊(cè)多個(gè)域名來模擬域,在本地搭建個(gè)虛擬服務(wù)器,綁定host的方式就可以:

          $ sudo vim /etc/hosts
          

          添加以下條目

          #refer to localhost 
          127.0.0.1 publisher.net
          127.0.0.1 sdk.net
          

          然后你就可以訪問該頁(yè)面http://publisher.net和http://sdk.net

          Developer Tools

          用瀏覽器自帶的調(diào)試工具,Chrome Developer Tool 、Safari Developer Tools、Firebug都是不錯(cuò)的選擇。

          開發(fā)工具也簡(jiǎn)稱為工具。

          工具提供Web開發(fā)者深進(jìn)入瀏覽器和Web應(yīng)用程序的內(nèi)部。使用工具來有效地追蹤布局問題,將JavaScript打斷點(diǎn),并獲得代碼優(yōu)化的建議。

          控制臺(tái)日志

          用于測(cè)試和輸出文本和其他一般的調(diào)試, 控制臺(tái)日志可通過瀏覽器的API log()輸出顯示。有各種各樣的方法和格式輸出你的信息,了解更多API: Console API.

          調(diào)試代理

          代理在你調(diào)試SDK的很多時(shí)候都很有用。 修改cookies, headers, cache, 編輯 http request/response, SSL Proxying, ajax 調(diào)試等等。

          這里推薦一些代理工具:

          • FiddlerCore
          • Charles
          • Cellist

          BrowserSync

          Browsersync能讓瀏覽器實(shí)時(shí)、快速響應(yīng)您的文件更改(html、js、css、sass、less等)并自動(dòng)刷新頁(yè)面。更重要的是 Browsersync可以同時(shí)在PC、平板、手機(jī)等設(shè)備下進(jìn)項(xiàng)調(diào)試。它真的很有幫助如果你需要跨平臺(tái)測(cè)試你的SDK)。

          提示和小技巧

          Console Logs Polyfill(Polyfilling 是由 RemySharp 提出的一個(gè)術(shù)語(yǔ),它是用來描述復(fù)制缺少的 API 和API 功能的行為)

          這不是一個(gè)真正的polyfill,只是保證在調(diào)用console.log API的時(shí)候不拋出錯(cuò)誤。

          if (typeof console==="undefined") { var f=function() {}; console={ log: f, debug: f, error: f, info: f };}
          

          EncodeURI or EncodeURIComponent

          理解三者的不同 escape()、encodeURI()、encodeURIComponent()

          here.

          記住使用 encodeURI()和encodeURIComponent()有11個(gè)字符不同。 它們是: # $ & + , / : ;=? @ more discussion。

          你可能真的不需要JQuery

          正如標(biāo)題所說, 你可能真的不需要JQuery。如果你正在找一些公共的代碼那下面這些會(huì)很有用:- AJAX EFFECTS, ELEMENTS, EVENTS, UTILS

          你不需要 jQuery

          Free yourself from the chains of jQuery by embracing and understanding the modern Web API and discovering various directed libraries to help you fill in the gaps.

          http://blog.garstasio.com/you-dont-need-jquery/

          有用的 Tips

          Selecting Elements

          DOM Manipulation

          回調(diào)函數(shù)加載腳本

          類似于 異步加載腳本 增加回調(diào)函數(shù)。

          function loadScript(url, callback) { 
           var script=document.createElement('script'); 
           script.async=true; script.src=url; 
           var entry=document.getElementsByTagName('script')[0]; entry.parentNode.insertBefore(script, entry); 
           script.onload=script.onreadystatechange=function () { var rdyState=script.readyState; 
           if (!rdyState || /complete|loaded/.test(script.readyState)) { 
           callback(); // detach the event handler to avoid memory leaks in IE (http://mng.bz/W8fx) 
           script.onload=null;
           script.onreadystatechange=null; } };
          }
          

          執(zhí)行一次函數(shù)

          這里展示了如何實(shí)現(xiàn)函數(shù)只執(zhí)行一次。

          每當(dāng)你想有一個(gè)只運(yùn)行一次的函數(shù)。通常這些函數(shù)是以事件監(jiān)聽的方式,很難管理。當(dāng)然如果很容易管理,你只需要?jiǎng)h除監(jiān)聽事件,但是這是個(gè)理想的狀態(tài),很多時(shí)候你只需要允許一個(gè)函數(shù)執(zhí)行一次。下面的代碼可以實(shí)現(xiàn):

          // Copy from DWB
          // http://davidwalsh.name/javascript-once
           function once(fn, context) { 
           var result; return function() { 
           if(fn) { 
           result=fn.apply(context || this, arguments); 
           fn=null;
           } 
           return result; };
          }
          // Usagevar 
           canOnlyFireOnce=once(function() { console.log('Fired!');});
           canOnlyFireOnce(); // "Fired!"canOnlyFireOnce(); // nada
          

          獲取樣式

          獲取行間樣式

          <span id="black" style="color: black"> 
           This is black color span 
          </span>
          <script> document.getElementById('black').style.color; //=> black</script>
          

          獲取真正的樣式

          <style>
           #black { color: red !important;}
          </style>
          <span id="black" style="color: black">
           This is black color span 
          </span>
          <script> 
           document.getElementById('black').style.color; //=> black 
          // real var black=document.getElementById('black'); 
           window.getComputedStyle(black, null).getPropertyValue('color'); //=> rgb(255, 0, 0)
          </script>
          

          ref:https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle

          檢測(cè)當(dāng)前窗口

          了解更多: here。

          chrome中使用F12獲取的nerwork中的網(wǎng)絡(luò)請(qǐng)求,可以看到request headers,直接復(fù)制下來是一個(gè)字符串,可以手動(dòng)修改為python requests可用的字典,但是費(fèi)時(shí)費(fèi)力,下面這段代碼,可以直接把復(fù)制下來的字符串類型,轉(zhuǎn)換為requests可用的python字典。

          天要給大家介紹的是驗(yàn)證碼的爬取和識(shí)別,不過只涉及到最簡(jiǎn)單的圖形驗(yàn)證碼,也是現(xiàn)在比較常見的一種類型。

          運(yùn)行平臺(tái):Windows

          Python版本:Python3.6

          IDE: Sublime Text

          其他:Chrome瀏覽器

          想要學(xué)習(xí)更多驗(yàn)證、破解網(wǎng)站驗(yàn)證碼的知識(shí)點(diǎn),關(guān)注小編后私信學(xué)習(xí)資料,領(lǐng)取相關(guān)學(xué)習(xí)資料

          簡(jiǎn)述流程:

          步驟1:簡(jiǎn)單介紹驗(yàn)證碼

          步驟2:爬取少量驗(yàn)證碼圖片

          步驟3:介紹百度文字識(shí)別OCR

          步驟4:識(shí)別爬取的驗(yàn)證碼

          步驟5:簡(jiǎn)單圖像處理

          目前,很多網(wǎng)站會(huì)采取各種各樣的措施來反爬蟲,驗(yàn)證碼就是其中一種,比如當(dāng)檢測(cè)到訪問頻率過高時(shí)會(huì)彈出驗(yàn)證碼讓你輸入,確認(rèn)訪問網(wǎng)站的不是機(jī)器人。但隨著爬蟲技術(shù)的發(fā)展,驗(yàn)證碼的花樣也越來越多,從最開始簡(jiǎn)單的幾個(gè)數(shù)字或字母構(gòu)成的圖形驗(yàn)證碼(也就是我們今天要涉及的)發(fā)展到需要點(diǎn)擊倒立文字字母的、與文字相符合的圖片的點(diǎn)觸型驗(yàn)證碼,需要滑動(dòng)到合適位置的極驗(yàn)滑動(dòng)驗(yàn)證碼,以及計(jì)算題驗(yàn)證碼等等,總之花樣百出,讓人頭禿。驗(yàn)證碼其他的相關(guān)知識(shí)大家可以看下這個(gè)網(wǎng)站:captcha.org

          再來簡(jiǎn)單說下圖形驗(yàn)證碼吧,就像這張:

          由字母和數(shù)字組成,再加上一些噪點(diǎn),但為了防止被識(shí)別,簡(jiǎn)單的圖形驗(yàn)證碼現(xiàn)在也變得復(fù)雜,有的加了干擾線,有的加噪點(diǎn),有的加上背景,字體扭曲、粘連、鏤空、混用等等,甚至有時(shí)候人眼都難以識(shí)別,只能默默點(diǎn)擊“看不清,再來一張”。

          驗(yàn)證碼難度的提高隨之帶來的就是識(shí)別的成本也需要提高,在接下來的識(shí)別過程中,我會(huì)先直接使用百度文字識(shí)別OCR,來測(cè)試識(shí)別準(zhǔn)確度,再確認(rèn)是否選擇轉(zhuǎn)灰度、二值化以及去干擾等圖像操作優(yōu)化識(shí)別率。

          接下來我們就來爬取少量驗(yàn)證碼圖片存入文件。

          首先打開Chrome瀏覽器,訪問剛剛介紹的網(wǎng)站,里面有一個(gè)captcha圖像樣本鏈接:https://captcha.com/captcha-examples.html?cst=corg,網(wǎng)頁(yè)里有60張不同類型的圖形驗(yàn)證碼,足夠我們用來識(shí)別試驗(yàn)了。


          直接來看代碼吧:

          import requests
          import os
          import time
          from lxml import etree
          def get_Page(url,headers):
           response=requests.get(url,headers=headers)
           if response.status_code==200:
           # print(response.text)
           return response.text
           return None
          def parse_Page(html,headers):
           html_lxml=etree.HTML(html)
           datas=html_lxml.xpath('.//div[@class="captcha_images_left"]|.//div[@class="captcha_images_right"]')
           item={}
           # 創(chuàng)建保存驗(yàn)證碼文件夾
           file='D:/******'
           if os.path.exists(file):
           os.chdir(file)
           else: 
           os.mkdir(file)
           os.chdir(file) 
           for data in datas:
           # 驗(yàn)證碼名稱
           name=data.xpath('.//h3')
           # print(len(name))
           # 驗(yàn)證碼鏈接
           src=data.xpath('.//div/img/@src') 
           # print(len(src))
           count=0
           for i in range(len(name)):
           # 驗(yàn)證碼圖片文件名
           filename=name[i].text + '.jpg'
           img_url='https://captcha.com/' + src[i]
           response=requests.get(img_url,headers=headers)
           if response.status_code==200:
           image=response.content
           with open(filename,'wb') as f:
           f.write(image)
           count +=1
           print('保存第{}張驗(yàn)證碼成功'.format(count))
           time.sleep(1)
          def main():
           url='https://captcha.com/captcha-examples.html?cst=corg'
           headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36'}
           html=get_Page(url,headers)
           parse_Page(html,headers)
          if __name__=='__main__':
           main()
          

          仍然使用Xpath爬取,在右鍵檢查圖片時(shí)可以發(fā)現(xiàn),網(wǎng)頁(yè)分為兩欄,如下圖紅框所示,根據(jù)class分為左右兩欄,驗(yàn)證碼分別位于兩欄中。



          datas=html_lxml.xpath('.//div[@class="captcha_images_left"]|.//div[@class="captcha_images_right"]')
          


          這里我使用了Xpath中的路徑選擇,在路徑表達(dá)式中使用“|”表示選取若干路徑,例如這里表示的就是選取class為"captcha_images_left"或者"captcha_images_right"的區(qū)塊。再來看下運(yùn)行結(jié)果:



          由于每爬取一張驗(yàn)證碼圖片都強(qiáng)制等待了1秒,最后這個(gè)運(yùn)行時(shí)間確實(shí)讓人絕望,看樣子還是需要多線程來加快速度的,關(guān)于多進(jìn)程多線程我們下次再說,這里我們先來看下爬取到的驗(yàn)證碼圖片。



          圖片到手了,接下來就是調(diào)用百度文字識(shí)別的OCR來識(shí)別這些圖片了,在識(shí)別之前,先簡(jiǎn)單介紹一下百度OCR的使用方法,因?yàn)楹芏嘧R(shí)別驗(yàn)證碼的教程用的都是tesserocr庫(kù),所以一開始我也嘗試過,安裝過程中就遇到了很多坑,后來還是沒有繼續(xù)使用,而是選擇了百度OCR來識(shí)別。百度OCR接口提供了自然場(chǎng)景下圖片文字檢測(cè)、定位、識(shí)別等功能。文字識(shí)別的結(jié)果可以用于翻譯、搜索、驗(yàn)證碼等代替用戶輸入的場(chǎng)景。另外還有其他視覺、語(yǔ)音技術(shù)方面的識(shí)別功能,大家可以直接閱讀文檔了解:百度OCR-API文檔https://ai.baidu.com/docs#/OCR-API/top



          使用百度OCR的話,首先注冊(cè)用戶,然后下載安裝接口模塊,直接終端輸入pip install baidu-aip即可。然后創(chuàng)建文字識(shí)別應(yīng)用,獲取相關(guān)Appid,API Key以及Secret Key,需要了解一下的是百度AI每日提供50000次免費(fèi)調(diào)用通用文字識(shí)別接口的使用次數(shù),足夠我們揮霍了。



          然后就可以直接調(diào)用代碼了。

          from aip import AipOcr
          # 你的 APPID AK SK 
          APP_ID='你的 APP_ID '
          API_KEY='你的API_KEY'
          SECRET_KEY='你的SECRET_KEY'
          client=AipOcr(APP_ID, API_KEY, SECRET_KEY)
          # 讀取圖片 
          def get_file_content(filePath):
           with open(filePath, 'rb') as fp:
           return fp.read()
          image=get_file_content('test.jpg')
          # 調(diào)用通用文字識(shí)別, 圖片參數(shù)為本地圖片 
          result=client.basicGeneral(image)
          # 定義參數(shù)變量 
          options={
           # 定義圖像方向
           'detect_direction' : 'true',
           # 識(shí)別語(yǔ)言類型,默認(rèn)為'CHN_ENG'中英文混合
           'language_type' : 'CHN_ENG',
          }
          # 調(diào)用通用文字識(shí)別接口 
          result=client.basicGeneral(image,options)
          print(result)
          for word in result['words_result']:
           print(word['words'])
          

          這里我們識(shí)別的是這張圖


          可以看一下識(shí)別結(jié)果

          上面是識(shí)別后直接輸出的結(jié)果,下面是單獨(dú)提取出來的文字部分??梢钥吹?,除了破折號(hào)沒有輸出外,文字部分都全部正確輸出了。這里我們使用的圖片是jpg格式,文字識(shí)別傳入的圖像支持jpg/png/bmp格式,但在技術(shù)文檔中有提到,使用jpg格式的圖片上傳會(huì)提高一定準(zhǔn)確率,這也是我們爬取驗(yàn)證碼時(shí)使用jpg格式保存的原因。

          輸出結(jié)果中,各字段分別代表:

          • log_id : 唯一的log id,用于定位問題
          • direction : 圖像方向,傳入?yún)?shù)時(shí)定義為true表示檢測(cè),0表示正向,1表示逆時(shí)針90度,2表示逆時(shí)針180度,3表示逆時(shí)針270度,-1表示未定義。
          • words_result_num : 識(shí)別的結(jié)果數(shù),即word_result的元素個(gè)數(shù)
          • word_result : 定義和識(shí)別元素?cái)?shù)組
          • words : 識(shí)別出的字符串
          • 還有一些非必選字段大家可以去文檔里熟悉一下。

          接下來,我們要做的,就是將我們之前爬取到的驗(yàn)證碼用剛介紹的OCR來識(shí)別,看看究竟能不能得到正確結(jié)果。

          from aip import AipOcr
          import os
          i=0
          j=0
          APP_ID='你的 APP_ID '
          API_KEY='你的API_KEY'
          SECRET_KEY='你的SECRET_KEY'
          client=AipOcr(APP_ID, API_KEY, SECRET_KEY)
          # 讀取圖片 
          file_path='D:\******\驗(yàn)證碼圖片'
          filenames=os.listdir(file_path)
          # print(filenames)
          for filename in filenames:
           # 將路徑與文件名結(jié)合起來就是每個(gè)文件的完整路徑
           info=os.path.join(file_path,filename)
           with open(info, 'rb') as fp:
           # 獲取文件夾的路徑 
           image=fp.read()
           # 調(diào)用通用文字識(shí)別, 圖片參數(shù)為本地圖片
           result=client.basicGeneral(image)
           # 定義參數(shù)變量 
           options={
           'detect_direction' : 'true',
           'language_type' : 'CHN_ENG',
           }
           # 調(diào)用通用文字識(shí)別接口 
           result=client.basicGeneral(image,options)
           # print(result)
           if result['words_result_num']==0:
           print(filename + ':' + '----')
           i +=1
           else:
           for word in result['words_result']: 
           print(filename + ' : ' +word['words'])
           j +=1
          print('共識(shí)別驗(yàn)證碼{}張'.format(i+j))
          print('未識(shí)別出文本{}張'.format(i))
          print('已識(shí)別出文本{}張'.format(j))
          

          和識(shí)別圖片一樣,這里我們將文件夾驗(yàn)證碼圖片里的圖片全部讀取出來,依次讓OCR識(shí)別,并依據(jù)“word_result_num”字段判斷是否成功識(shí)別出文本,識(shí)別出文本則打印結(jié)果,未識(shí)別出來的用“----”代替,并結(jié)合文件名對(duì)應(yīng)識(shí)別結(jié)果 。最后統(tǒng)計(jì)識(shí)別結(jié)果數(shù)量,再來看下識(shí)別結(jié)果。



          看到結(jié)果,只能說Amazing!60張圖片居然識(shí)別出了65張,并且還有27張為未識(shí)別出文本的,這不是我想要的結(jié)果~先來簡(jiǎn)單看下問題出在哪里,看到“Vertigo Captcha Image.jpg"這張圖名出現(xiàn)了兩次,懷疑是在識(shí)別過程中由于被干擾,所以識(shí)別成兩行文字輸出了,這樣就很好解釋為什么多出來5張驗(yàn)證碼圖片了??墒?!為什么會(huì)有這么多未識(shí)別出文本呢,而且英文數(shù)字組成的驗(yàn)證碼識(shí)別成中文了,看樣子,不對(duì)驗(yàn)證碼圖片進(jìn)行去干擾處理,僅靠OCR來識(shí)別的想法果然還是行不通啊。那么接下來我們便使用圖像處理的方法來重新識(shí)別驗(yàn)證碼吧。

          還是介紹驗(yàn)證碼時(shí)用的這張圖




          這張圖也沒能被識(shí)別出來,讓人頭禿。接下來就對(duì)這張圖片進(jìn)行一定處理,看能不能讓OCR正確識(shí)別

          from PIL import Image
          filepath='D:\******\驗(yàn)證碼圖片\AncientMosaic Captcha Image.jpg'
          image=Image.open(filepath)
          # 傳入'L'將圖片轉(zhuǎn)化為灰度圖像
          image=image.convert('L')
          # 傳入'1'將圖片進(jìn)行二值化處理
          image=image.convert('1')
          image.show()
          

          這樣子轉(zhuǎn)化后再來看下圖片變成什么樣了?



          確實(shí)有些不同了,趕緊拿去試試能不能識(shí)別,還是失敗了~~繼續(xù)修改

          from PIL import Image
          filepath='D:\******\驗(yàn)證碼圖片\AncientMosaic Captcha Image.bmp'
          image=Image.open(filepath)
          # 傳入'L'將圖片轉(zhuǎn)化為灰度圖像
          image=image.convert('L')
          # 傳入'l'將圖片進(jìn)行二值化處理,默認(rèn)二值化閾值為127
          # 指定閾值進(jìn)行轉(zhuǎn)化
          count=170
          table=[]
          for i in range(256):
           if i < count:
           table.append(0)
           else:
           table.append(1 )
          image=image.point(table,'1')
          image.show()
          

          這里我將圖片保存成了bmp模式,然后指定二值化的閾值,不指定的話默認(rèn)為127,我們需要先轉(zhuǎn)化原圖為灰度圖像,不能直接在原圖上轉(zhuǎn)化。然后將構(gòu)成驗(yàn)證碼的所需像素添加到一個(gè)table中,然后再使用point方法構(gòu)建新的驗(yàn)證碼圖片。





          現(xiàn)在已經(jīng)識(shí)別到文字了,雖然我不知道為啥識(shí)別成了“珍”,分析之后發(fā)現(xiàn)是因?yàn)閦我在設(shè)置參數(shù)設(shè)置了“l(fā)anguage_type”為“CHN_ENG”,中英文混合模式,于是我修改成“ENG”英文類型,發(fā)現(xiàn)可以識(shí)別成字符了,但依然沒有識(shí)別成功,嘗試其他我所知道的方法后,我表示很無語(yǔ),我決定繼續(xù)嘗試PIL庫(kù)的其他方法試試。

          # 找到邊緣
          image=image.filter(ImageFilter.FIND_EDGES)
          # image.show()
          # 邊緣增強(qiáng)
          image=image.filter(ImageFilter.EDGE_ENHANCE)
          image.show()
          



          還是不能正確識(shí)別,我決定換個(gè)驗(yàn)證碼試試。。。。。。



          我找了這張帶有陰影的

          from PIL import Image,ImageFilter
          filepath='D:\******\驗(yàn)證碼圖片\CrossShadow2 Captcha Image.jpg'
          image=Image.open(filepath)
          # 傳入'L'將圖片轉(zhuǎn)化為灰度圖像
          image=image.convert('L')
          # 傳入'l'將圖片進(jìn)行二值化處理,默認(rèn)二值化閾值為127
          # 指定閾值進(jìn)行轉(zhuǎn)化
          count=230
          table=[]
          for i in range(256):
           if i < count:
           table.append(1)
           else:
           table.append(0)
          image=image.point(table,'1')
          image.show()
          

          簡(jiǎn)單處理后,得到這樣的圖片:



          識(shí)別結(jié)果為:



          識(shí)別成功了,老淚縱橫?。?!看樣子百度OCR還是可以識(shí)別出驗(yàn)證碼的,不過識(shí)別率還是有點(diǎn)低,需要對(duì)圖像進(jìn)行一定處理,才能增加識(shí)別的準(zhǔn)確率。不過百度OCR對(duì)規(guī)范文本的識(shí)別還是很準(zhǔn)確的。

          那么與其他驗(yàn)證碼相比,究竟是什么讓這個(gè)驗(yàn)證碼更容易被OCR讀懂呢?

          • 字母沒有相互疊加在一起,在水平方向上也沒有彼此交叉。也就是說,可以在每一個(gè)字 母外面畫一個(gè)方框,而不會(huì)重疊在一起。
          • 圖片沒有背景色、線條或其他對(duì) OCR 程序產(chǎn)生干擾的噪點(diǎn)。
          • 白色背景色與深色字母之間的對(duì)比度很高。

          這樣的驗(yàn)證碼相對(duì)識(shí)別起來較容易,另外,像識(shí)別圖片時(shí)的白底黑字就屬于很標(biāo)準(zhǔn)的規(guī)范文本了,所以識(shí)別的準(zhǔn)確度較高。至于更復(fù)雜的圖形驗(yàn)證碼,就需要更深的圖像處理技術(shù)或者訓(xùn)練好的OCR來完成了,如果只是簡(jiǎn)單識(shí)別一個(gè)驗(yàn)證碼的話,不如人工查看圖片輸入,更多一點(diǎn)的話,也可以交給打碼平臺(tái)來識(shí)別。


          主站蜘蛛池模板: 久久精品无码一区二区三区免费| 国产乱码精品一区二区三区麻豆 | 精品人无码一区二区三区| 海角国精产品一区一区三区糖心 | 好爽毛片一区二区三区四无码三飞| 午夜视频久久久久一区| 一区二区三区日本电影| 一区二区国产精品| 精品亚洲综合在线第一区| 亚洲一区二区三区深夜天堂| 免费看无码自慰一区二区| 无码欧精品亚洲日韩一区夜夜嗨 | 精品少妇人妻AV一区二区| 日韩成人一区ftp在线播放| 合区精品久久久中文字幕一区| 久久精品无码一区二区三区| 中文字幕一区二区三区久久网站 | 日韩一区二区视频在线观看| 国产aⅴ一区二区| 国产成人无码一区二区三区| 在线观看日本亚洲一区| 亚洲国模精品一区| 精品人妻系列无码一区二区三区| 免费看无码自慰一区二区| 亚洲综合av永久无码精品一区二区 | av一区二区三区人妻少妇| 国产精品区AV一区二区| 亚洲国产高清在线精品一区| 白丝爆浆18禁一区二区三区| 无码人妻AⅤ一区二区三区| 色天使亚洲综合一区二区| 一区二区三区亚洲| 日本美女一区二区三区| 亚洲蜜芽在线精品一区| 亚洲性日韩精品国产一区二区| 无码精品尤物一区二区三区| 97久久精品午夜一区二区| 在线免费观看一区二区三区| 国产在线一区二区| 国产AⅤ精品一区二区三区久久| 日本一区二区三区爆乳|