整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          手把手教你深入CSS實現一個粒子動效的按鈕



          鈕(button)可能是網頁中最常見的組件之一了,大部分都平淡無奇,如果你碰到的是一個這樣的按鈕,會不會忍不住多點幾次呢?

          轉載鏈接: https://github.com/XboxYan/notes/issues/16

          通常這類效果第一反應可能就是借助canvas了,比如下面這個案例點擊預覽(建議去codepen原鏈接點擊預覽訪問,segmentfault內置的預覽js會加載失敗)

          效果就更加震撼了,當然canvas實現也有一定的門檻,而且實際使用起來也略微麻煩(所有js實現的通病),這里嘗試一下CSS的實現方式。

          生成粒子

          拋開js方案,還有HTML和CSS實現方式。HTML就不用說了,直接寫上大量的標簽

          <button>
              button
              <i></i>
              <i></i>
              <i></i>
              <i></i>
              <i></i>
              ...
          </button>

          一般情況下我不是很喜歡這種方式,標簽太多,結構不美觀,而且有可能對現有的頁面造成其他影響(很多情況下并不方便修改原始HTML結構)

          那么來看看CSS實現方式,主要也是兩種方式,其實就是想一下有哪些屬性可以無限疊加,一個是box-shadow,還有一個是background-image(CSS3支持無限疊加)。

          1.box-shadow

          我們先看看box-shadow方式,為了避免使用額外標簽,這里采用偽元素生成。

          .button::before{
            position: absolute;
            content: '';
            width: 5px;
            height: 5px;
            border-radius: 50%;
            background-color: #ff0081;
            box-shadow: 10px 10px #ff0081,15px 0px 0 2px #ff0081,20px 15px 0 3px #ff0081,...;/*無限疊加*/
          }

          效果還是有的,主要就是多花點時間來調試,這里主要根據偏移量和擴展來決定粒子的位置和大小。

          不過這里的偏移量只能是px單位,無法很好的自適應按鈕的大小,所以這里采用第二種方式來實現

          2.background-image

          CSS3中background-image是可以無限疊加的,類似于

          .myclass {
            background: background1, background2, /*...*/ backgroundN;
          }

          這里我們可以采用徑向漸變radial-gradient來實現多個小圓點。

          .button::before{
            position: absolute;
            content: '';
            left: -2em;
            right: -2em;
            top: -2em;
            bottom: -2em;
            pointer-events: none;
            background-repeat: no-repeat;
            background-image: radial-gradient(circle, #ff0081 20%, transparent 0), 
            radial-gradient(circle, #ff0081 20%, transparent 0),
            radial-gradient(circle, #ff0081 20%, transparent 0), 
            radial-gradient(circle, #ff0081 20%, transparent 0), 
            ...;
            background-size: 10% 10%, 20% 20%, 15% 15%,...;
            background-position: 18% 40%, 20% 31%, 30% 30%,...;
          }

          這里主要通過background-size和background-position來控制原點的尺寸與位置,看著好像挺復雜,其實只要background-size和background-position與background-image位置一一對應就行了。實際開發中可能有點難調試,可以直接在控制臺中通過鍵盤上下左右鍵微調實時預覽效果(可以考慮做一個可視化工具)。

          這樣就做出了一個簡單的粒子效果。

          動起來

          雖然background-image不支持CSS動畫,但是另外兩個background-size和background-position支持呀,所以,CSS transition和CSS animation都可以用起來。

          動畫效果很簡單,就是粒子從中心往外擴散,并且逐漸消失的過程。

          transition

          我們先看看:hover交互

          .button::before{
            transition:.75s background-position ease-in-out,75s background-size ease-in-out;
          }
          .button:hover::before{
            background-position: 5% 44%, -5% 20%, 7% 5%...;
            background-size: 0% 0%;
          }

          當然直接這樣設置肯定是不理想,鼠標離開時會收縮回去,效果如下

          我們需要是鼠標離開時不收縮回去,如何實現呢?

          很簡單,把transition設置在:hover下就可以了,表示只有當鼠標經過時才有過渡,離開時沒有

          .button:hover::before{
            background-position: 5% 44%, -5% 20%, 7% 5%...;
            background-size: 0% 0%;
            transition:.75s background-position ease-in-out,75s background-size ease-in-out;
          }

          這樣是不是感覺稍微好些了呢?點擊這里點擊預覽查看。

          如果我們想做成點擊的時候出現粒子動畫該怎么做呢?這里就需要借助:active偽類了。

          如果我們按照:hover邏輯,那么

          .button:active::before{
            background-position: 5% 44%, -5% 20%, 7% 5%...;
            background-size: 0% 0%;
            transition:.75s background-position ease-in-out,75s background-size ease-in-out;
          }

          很遺憾,只有當只有按住不動的時候才能觸發,一旦鼠標抬起就沒有了,這個時候我們就需要換個角度了。可以這么想象一下,默認就是發散的,然后點擊的時候聚攏,抬起的時候就會有還原成之前的發散狀態,同時,在點擊的時候需要取消掉過渡效果,如下

          .button::before {
              /*...*/
              background-position: 5% 44%...;/*擴散的狀態*/
              background-size: 0% 0%;
              transition: background-position .5s ease-in-out, background-size .75s ease-in-out;
          }
          
          .button:active::before {
            transition:0s;/**注意取消掉過渡**/
            background-size: 10% 10%, 20% 20%...;
            background-position: 18% 40%, 20% 31%,...;
          }

          你可以查看這個demo點擊預覽

          為什么在:active需要transition:0s呢,你可以試下不添加的效果就明白了,如下

          animation

          animation和transition實現原理比較類似,優點是可以做出更加精細的動畫,這里就拿:active方式來說吧。

          .button::before{
            /*...*/
            animation: bubbles ease-in-out .75s forwards;
          }
          .button:active::before {
            animation: none; /*這里注意取消動畫*/
            background-size: 0;
          }
          @keyframes bubbles {
            0% {
              background-position: 18% 40%, ...;
            }
            50% {
              background-position: 10% 44%, ...;
            }
            100% {
              background-position: 5% 44%, ...;
              background-size: 0% 0%;
            }
          }

          可以在這里點擊預覽查看源碼。

          唯一的不足可能是初始化動畫會自執行一次。

          小結

          上面介紹了純CSS實現一個粒子動效的按鈕,優點很明顯,復制一下CSS直接扔到項目里就能用,管他什么原生項目還是react項目,也無需綁定什么事件,也無需額外的邏輯處理,增強現有體驗。試想一下,如果這是一個‘購買’按鈕,會不會觸發你多購買幾次呢,反正我肯定是會被吸引住了,哈~缺點也還是有的,比如上面的定位,密密麻麻都是工作量啊,建議這些功能在項目整體完成之后再細細打磨,也可以試著做一些可視化工具來減輕工作量,完。

          一種:selenium導入瀏覽器驅動,用get方法打開瀏覽器,例如:

          import time
          from selenium import webdriver
          
          def mac():
           # browser = webdriver.Chrome()
           # browser = webdriver.Firefox()
           browser = webdriver.Ie()
           browser.implicitly_wait(5)
           browser.get('http://www.baidu.com/')
          

          第二種:通過導入python的標準庫webbrowser打開瀏覽器,例如:

          import webbrowser
          
          def mac():
           # web.open(‘http://www.baidu.com’,new=0,autoraise=True) # new:0/1/2 0:同一瀏覽器窗口打開 1:打開瀏覽器新的窗口,2:打開瀏覽器窗口新的tab; autoraise=True:窗口自動增長
           # web.open_new_tab(‘http://www.baidu.com’)
           web.open_new(‘http://www.baidu.com’)
          

          第三種:使用Splinter模塊模塊

          一、Splinter 的安裝
          Splinter 的使用必修依靠 Cython、lxml、selenium 這三個軟件。所以,安裝前請提前安裝 Cython、lxml、selenium。

          二、Splinter 的使用
          這里,我給出自動登錄 126 郵箱的案例。難點是要找到頁面的賬戶、密碼、登錄的頁面元素,這里需要查看 126 郵箱登錄頁面的源碼,才能找到相關控件的 id.

          例如: 輸入密碼,密碼的文本控件 id 是 pwdInput. 可以使用browser.find_by_id() 方法定位到密碼的文本框,接著使用fill() 方法,填寫密碼。至于模擬點擊按鈕,也是要先找到按鈕控件的 id, 然后使用 click() 方法。

          # coding=utf-8
          import time 
          from splinter import Browser
          
          def splinter(url):
           browser = Browser()
           # login 126 email websize
           browser.visit(url)
           # wait web element loading
           time.sleep(5)
           # fill in account and password
           browser.find_by_id('idInput').fill('xxxxxx')
           browser.find_by_id('pwdInput').fill('xxxxx')
           # click the button of login
           browser.find_by_id('loginBtn').click()
           time.sleep(8)
           # close the window of brower
           browser.quit()
          	
          if __name__ == '__main__': 
           websize3 ='http://www.126.com' 
           splinter(websize3) 
          

          WebDriver簡介

          selenium 從 2.0 開始集成了 webdriver 的 API,提供了更簡單,更簡潔的編程接口。selenium webdriver 的目標是提供一個設計良好的面向對象的 API,提供了更好的支持進行 web-app 測試。

          打開瀏覽器

          在 selenium+python 自動化測試(一)–環境搭建中,運行了一個測試腳本,腳本內容如下:

          from selenium import webdriver
          import time
          
          browser = webdriver.Chrome()
          browser.get("http://www.baidu.com")
          print(browser.title)
          browser.find_element_by_id("kw").send_keys("selenium")
          browser.find_element_by_id("su").click()
          time.sleep(3)
          browser.close() 
          

          webdriver 是一個 Web 應用程序測試自動化工具,用來驗證程序是否如預期的那樣執行。

          webdriver.Chrome():創建一個 Chrome 瀏覽器的 webdriver 實例

          browser.get(“http://www.baidu.com“):打開”http://www.baidu.com”頁面

          browser.find_element_by_id(“kw”).send_keys(“selenium”):
          找到 id 為“kw”的元素,在這個頁面上為百度首頁的搜索框,在其中輸入“selenium”

          browser.find_element_by_id(“su”).click():找到 id 為“su”的元素并點擊,在這個頁面上為百度首頁的“百度一下”按鈕

          browser.close():退出瀏覽器

          運行腳本的第一步是打開瀏覽器,使用 webdriver.Chrome() 打開谷歌瀏覽器,如果要指定其他瀏覽器,比如要使用 Firefox 或者 IE 瀏覽器,更換瀏覽器名稱就可以了

          browser = webdriver.Chrome() // 打開 Chrome 瀏覽器

          browser = webdriver.Firefox() // 打開 Firefox 瀏覽器

          browser = webdriver.Ie() // 打開 IE 瀏覽器

          第二步操作是打開頁面,使用browser.get(url)方法來打開網頁鏈接,例如腳本中打開百度首頁

          browser.get("http://www.baidu.com")

          接下來是 **print(browser.title)**,使用browser.title獲取當前頁面的title,title就是在瀏覽器 tab 上顯示的內容,例如百度首頁的標題是“百度一下,你就知道”

          瀏覽器前進后退

          在當前頁面打開一個新的鏈接后,如果想回退到前一個頁面,使用如下browser.back(),相當于點擊了瀏覽器的后退按鈕

          和 back 操作對應的是瀏覽器前進操作browser.forward(),相當于點擊了瀏覽器的前進按鈕

          browser.back() // 回到上一個頁面

          browser.forward() // 切換到下一個頁面

          瀏覽器運行后,如果頁面沒有最大化,可以調用browser.maximize_window()將瀏覽器最大化,相當于點擊了頁面右上角的最大化按鈕

          browser.maximize_window() // 瀏覽器窗口最大化

          browser.set_window_size(800, 720) // 設置窗口大小為 800*720

          瀏覽器截屏操作,參數是截屏的圖片保存路徑:

          browser.get_screenshot_as_file("D:/data/test.png") 屏幕截圖保存為***

          browser.refresh() // 重新加載頁面, 頁面刷新

          在測試腳本運行完后,一般會在最后關閉瀏覽器,有兩種方法關閉瀏覽器,close()方法用于關閉當前頁面,quit()方法關閉所有和當前測試有關的瀏覽器窗口

          browser.close() // 關閉當前頁面

          browser.quit() // 關閉所有由當前測試腳本打開的頁面

          <h1 class="csdn_top" line-height:38px;color:#2c3033;padding:0px="" 29px;white-space:normal;"="" style="word-wrap: break-word; color: rgb(0, 0, 0); font-family: "sans serif", tahoma, verdana, helvetica; margin-top: 0px; margin-bottom: 0px; font-size: 24px;"> 頁面元素定位

          要定位頁面元素,需要找到頁面的源碼。

          IE 瀏覽器中,打開頁面后,在頁面上點擊鼠標右鍵,會有“查看源代碼”的選項,點擊后就會進入頁面源碼頁面,在這里就可以找到頁面的所有元素

          使用 Chrome 瀏覽器打開頁面后,在瀏覽器的地址欄右側有一個圖標,點擊這個圖標后,會出現許多菜單項,選擇更多工具里的開發者工具,就會出現頁面的源碼,不同版本的瀏覽器菜單選項可能不同,但是都會在開發者工具里找到頁面的源碼

          Firefox 瀏覽器打開頁面后,在右鍵菜單里也可以找到“查看頁面源代碼”的選項。在 Firefox 中,可以使用瀏覽器自帶的插件查看定位元素,在 Firefox 的附加組件里搜索 firebug 進行下載,安裝 firebug 組件后會在瀏覽器的工具欄中多出一個小蟲子的圖標,點擊這個圖標就可以打開組件查看頁面源碼,打開后如下圖所示

          以百度首頁搜索頁面為例,看一下 webdriver 定位元素的八種方式

          使用id定位

          在頁面源碼中找到搜索輸入框的元素定義


          可以看到輸入框有一個有一個 id 的屬性,調用find_element_by_id()根據 id 屬性來找到元素,參數為屬性的值

          input_search = browser.find_element_by_id("kw")

          使用name定位

          使用find_element_by_name()根據 name 屬性找到元素,參數為 name 屬性的值

          搜索框有一個 name=”wd”的屬性,使用 name 查找搜索輸入框元素

          input_search = browser.find_element_by_name("wd")

          使用className定位

          使用find_element_by_class_name()根據 className 屬性找到元素,參數為 className 屬性的值

          搜索框有一個 class=”s_ipt”的屬性,使用 className 查找元素

          input_search = browser.find_element_by_class_name("s_ipt")

          使用tagName定位

          使用find_element_by_tag_name()根據 tagName 屬性找到元素,參數為元素標簽的名稱

          每個頁面的元素都有一個 tag,搜索框的標簽為 input,有時候一個頁面里有許多相同的標簽,所以用這種方法找到的元素一般都不準確,除非這個元素使用的標簽在這個頁面里是唯一的。一般不會使用這種方式來定位元素

          input_search = browser.find_element_by_class_name("input")

          使用link_text定位

          頁面上都會有一些文本鏈接,點擊鏈接后會打開一個新的頁面,這些可以點擊的鏈接可以使用find_element_by_link_text來定位,百度首頁上方有如下幾個元素

          例如要定位“新聞”,找到元素的代碼,有一個 href 的屬性,這是點擊后打開的頁面

          新聞

          使用 link_text 查找元素,參數為元素的文本信息

          news = browser.find_element_by_link_text("新聞")

          使用partial_link_text定位

          這種方式類似于 link_text 的定位方式,如果一個元素的文本過長,不需要使用文本的所有信息,可以使用其中的部分文本就可以定位

          使用 partial_link_text 查找百度首頁的“新聞”元素,參數為文本信息,可以使用全部的文本,也可以使用部分文本

          news = browser.find_element_by_link_text("新聞") // 使用全部文本

          news = browser.find_element_by_link_text("新") // 使用部分文本

          使用css selector定位

          使用 css 屬性定位元素有多種方法,可以使用元素的 id、name、className,也可以使用元素的其他屬性,如果一個元素沒有上述的幾種屬性或者定位不到時,可以使用 css 來定位

          還是使用百度搜索框的實例來說明 css 定位的用法

          css使用元素的id定位

          css 屬性使用 id 定位時,使用 #號表示元素的 id

          input_search = browser.find_element_by_css_selector("#kw") // 使用元素的 id 定位

          css使用元素的class定位

          css 屬性使用 class 定位時,使用. 號表示元素的 class

          input_search = browser.find_element_by_css_selector(".s_ipt") // 使用元素的 class 定位

          css使用元素的tag定位

          css 屬性使用 tagName 定位時,直接使用元素的標簽

          input_search = browser.find_element_by_css_selector("input") // 使用元素的 tagName 定位

          css 使用元素的其他屬性

          除了上述 3 種屬性,css 屬性可以使用元素的其他屬性定位,格式如下

          input_search = browser.find_element_by_css_selector("[maxlength='255']")

          使用元素的maxlength屬性定位

          input_search = browser.find_element_by_css_selector("[autocomplete='off']")

          使用元素的autocomplete屬性定位

          可以在參數中加入元素的標簽名稱

          input_search = browser.find_element_by_css_selector("input#kw") // 使用元素的 id 定位

          input_search = browser.find_element_by_css_selector("input.s_ipt") // 使用元素的 class 定位

          input_search = browser.find_element_by_css_selector("input[maxlength='255']") // 使用元素的 maxlength 屬性定位

          input_search = browser.find_element_by_css_selector("input[autocomplete='off']") // 使用元素的 autocomplete 屬性定位

          css 的層級定位

          當一個元素使用自身的屬性不容易定位時,可以通過它的父元素來找到它,如果父元素也不好定位,可以再通過上元素來定位,以此類推,一直找到容易定位的父元素為止,通過層級定位到需要查找的元素

          通過 Firefox 的 firebug 組件查看百度首頁的源碼

          通過層級來定位搜索框

          input_search = browser.find_element_by_css_selector("form#form>span:nth-child(1)>input")

          input_search = browser.find_element_by_css_selector("form.fm>span:nth-child(1)>input")

          搜索框的父元素為 span 標簽,span 的父元素為 form,form 有 id 和 class 屬性,可以通過這兩個屬性來定位,找到 form 元素后,form 下有多個 span 標簽,所以要使用 span:nth-child(1),表示 form 下的第一個 span 標簽,這種用法很容易理解,表示第幾個孩子,最后是 span 下的 input 標簽,span 下只有一個 input,所以就可以定位到搜索框

          css邏輯運算

          用一個屬性來定位元素時,如果有其他元素的屬性和此元素重復,可以組合多個屬性來功共同定位

          組合多個屬性定位元素定位百度搜索框

          input_search = browser.find_element_by_css_selector("input[id='kw'][name='wd']")

          在元素內定義的屬性,都可以使用 css 來定位,使用其他幾種方式無法定位到元素時,可以使用 css,夠強大!

          使用xpath定位

          XPath 是一種在 XML 文檔中定位元素的語言。因為 HTML 可以看做 XML 的一種實現,所以 selenium 用戶可是使用這種強大語言在 web 應用中定位元素。xpath 也可以通過元素的各種屬性定位到元素

          使用元素屬性定位

          input_search = browser.find_element_by_xpath("//*[@id='kw']") // 通過元素 id 查找元素

          input_search = browser.find_element_by_xpath("//*[@name='wd']") // 通過元素 name 查找元素

          input_search = browser.find_element_by_xpath("//*[@class='s_ipt']") // 通過元素 class 查找元素

          input_search = browser.find_element_by_xpath("//*[@maxlength='255']") // 通過其他屬性查找元素

          input_search = browser.find_element_by_xpath("//*[@autocomplete='off']") // 通過其他屬性查找元素

          前面的 * 號表示查找所有的標簽元素,可以替換為標簽名稱,更準確的定位元素

          input_search = browser.find_element_by_xpath("//input[@id='kw']") // 通過元素 id 查找元素

          input_search = browser.find_element_by_xpath("//input[@name='wd']") // 通過元素 name 查找元素

          input_search = browser.find_element_by_xpath("//input[@class='s_ipt']") // 通過元素 class 查找元素

          input_search = browser.find_element_by_xpath("//input[@maxlength='255']") // 通過其他屬性查找元素

          input_search = browser.find_element_by_xpath("//input[@autocomplete='off']") // 通過其他屬性查找元素

          xpath也可以通過層級來定位,定位方式

          input_search = browser.find_element_by_xpath("//input[@id='form']//span[1]//input")

          browser.find_element_by_xpath("//input[@class='fm']//span[1]//input")查找效果和通過 css 的層級定位是相同的,意思是 form 元素下面的第一個 span 元素的 input 標簽子元素xpath的邏輯元素通過and運算符來組合元素屬性

          input_search = browser.find_element_by_xpath("//input[@id='kw' and name='wd']")

          屬性匹配
          xpath 中還有一種更強大的定位方式,通過模糊匹配元素的屬性

          news = browser.find_element_by_xpath("//a[contains(text(), '新聞')]")

          查找text中包含"新聞"的元素

          input_search = browser.find_element_by_xpath("//input[contains(@id, 'kw']")

          查找id中包含"kw"的元素

          input_search = browser.find_element_by_xpath("//input[starts-with(@id, 'k']")

          查找id以"k"開頭的元素

          input_search = browser.find_element_by_xpath("//input[ends-with(@id, 'w']")

          查找id以"w"結尾的元素

          input_search = browser.find_element_by_xpath("//input[matchs(@id, 'k*']")

          利用正則表達式查找元素

          上面介紹了查找頁面元素的八種方法,通過這些方式找到的都是單個元素,如果需要批量查找元素,還有和上面方式對應的八種復數形式

          find_elements_by_id

          find_elements_by_name

          find_elements_by_class_name

          find_elements_by_tag_name

          find_elements_by_link_text

          find_elements_by_partial_link_text

          find_elements_by_css_selector

          find_elements_by_xpath

          這8種方法查找到的是一組元素,返回的是list,可以通過索引來操作元素

          例如頁面上的復選框和單選框,或者頁面上有多個屬相相同的輸入框,這些屬性相同的一組元素,可以批量獲取,然后過濾出需要操作的元素,選擇其中的一個或者多個進行操作

          通過百度首頁搜索“selenium”關鍵字,會出現許多搜索結果,這些搜索結果具有相同的屬性,不同的是屬性的值不同,定位這些元素時,可以使用批量定位的方法

          看下面的代碼

          這是搜索 selenium 關鍵字后的頁面結果,每一個搜索結果都是可點擊的鏈接,定位這些元素的方法:

          search_results = browser.find_elements_by_css_selector("h.t>a")

          search_results[3].click() // 通過索引點擊第 4 條搜索結果

          第二個例子

          checkbox
          checkbox1
          checkbox2
          checkbox3

          這個頁面上有 3 個復選框,打開后如下圖所示:

          操作復選框

          // 查找所有的復選框并點擊
          checkboxs = browser.find_element_by_xpath('input[@type="checkbox"]')

          返回一個 list

          for checkbox in checkboxs:
          checkbox.click()
          // 點擊最后一個復選框

          checkboxs[2].click()

          from selenium import webdriver

          browser=webdriver.Firefox()

          browser.get(r’http://www.baidu.com/’)

          print(‘browser attributes:’)

          print(dir(browser))

          elem=browser.find_element_by_id(‘kw’)

          print(‘WebElement attributes:’)

          print(dir(elem))

          瀏覽器屬性:

          driver attributes:

          [‘NATIVE_EVENTS_ALLOWED’, ‘class’, ‘delattr’, ‘dict’, ‘doc’, ‘format’, ‘getattribute’, ‘hash’, ‘init’, ‘module’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘_file_detector’, ‘_is_remote’, ‘_mobile’, ‘_switch_to’, ‘_unwrap_value’, ‘_wrap_value’, ‘add_cookie’, ‘application_cache’, ‘back’, ‘binary’, ‘capabilities’, ‘close’, ‘command_executor’, ‘create_web_element’, ‘current_url’, ‘current_window_handle’, ‘delete_all_cookies’, ‘delete_cookie’, ‘desired_capabilities’, ‘error_handler’, ‘execute’, ‘execute_async_script’, ‘execute_script’, ‘file_detector’, ‘find_element’, ‘find_element_by_class_name’, ‘find_element_by_css_selector’, ‘find_element_by_id’, ‘find_element_by_link_text’, ‘find_element_by_name’, ‘find_element_by_partial_link_text’, ‘find_element_by_tag_name’, ‘find_element_by_xpath’, ‘find_elements’, ‘find_elements_by_class_name’, ‘find_elements_by_css_selector’, ‘find_elements_by_id’, ‘find_elements_by_link_text’, ‘find_elements_by_name’, ‘find_elements_by_partial_link_text’, ‘find_elements_by_tag_name’, ‘find_elements_by_xpath’, ‘firefox_profile’, ‘forward’, ‘get’, ‘get_cookie’, ‘get_cookies’, ‘get_log’, ‘get_screenshot_as_base64’, ‘get_screenshot_as_file’, ‘get_screenshot_as_png’, ‘get_window_position’, ‘get_window_size’, ‘implicitly_wait’, ‘log_types’, ‘maximize_window’, ‘mobile’, ‘name’, ‘orientation’, ‘page_source’, ‘profile’, ‘quit’, ‘refresh’, ‘save_screenshot’, ‘session_id’, ‘set_page_load_timeout’, ‘set_script_timeout’, ‘set_window_position’, ‘set_window_size’, ‘start_client’, ‘start_session’, ‘stop_client’, ‘switch_to’, ‘switch_to_active_element’, ‘switch_to_alert’, ‘switch_to_default_content’, ‘switch_to_frame’, ‘switch_to_window’, ‘title’, ‘w3c’, ‘window_handles’]

          調用說明:

          driver. 屬性值

          變量說明:
          1.driver.current_url:用于獲得當前頁面的 URL

          2.driver.title:用于獲取當前頁面的標題

          3.driver.page_source: 用于獲取頁面 html 源代碼

          4.driver.current_window_handle: 用于獲取當前窗口句柄

          5.driver.window_handles: 用于獲取所有窗口句柄

          函數說明:

          1.driver.find_element*(): 定位元素,
          2.driver.get(url): 瀏覽器加載 url。
          實例:driver.get(“http//:www.baidu.com”)
          3.driver.forward():瀏覽器向前(點擊向前按鈕)。
          4.driver.back():瀏覽器向后(點擊向后按鈕)。
          5.driver.refresh():瀏覽器刷新(點擊刷新按鈕)。
          6.driver.close():關閉當前窗口,或最后打開的窗口。
          7.driver.quit(): 關閉所有關聯窗口,并且安全關閉 session。

          8.driver.maximize_window(): 最大化瀏覽器窗口。
          9.driver.set_window_size(寬,高):設置瀏覽器窗口大小。
          10.driver.get_window_size():獲取當前窗口的長和寬。
          11.driver.get_window_position():獲取當前窗口坐標。
          12.driver.get_screenshot_as_file(filename): 截取當前窗口。

          實例:driver.get_screenshot_as_file(‘D:/selenium/image/baidu.jpg’)

          13.driver.implicitly_wait(秒):隱式等待,通過一定的時長等待頁面上某一元素加載完成。
          若提前定位到元素,則繼續執行。若超過時間未加載出,則拋出 NoSuchElementException 異常。

          實例:driver.implicitly_wait(10) #等待 10 秒

          14.driver.switch_to_frame(id 或 name 屬性值):切換到新表單 (同一窗口)。若無 id 或屬性值,可先通過 xpath 定位到 iframe,再將值傳給 switch_to_frame()

          15.driver.switch_to.parent_content(): 跳出當前一級表單。該方法默認對應于離它最近的 switch_to.frame() 方法。

          16.driver.switch_to.default_content(): 跳回最外層的頁面。
          17.driver.switch_to_window(窗口句柄):切換到新窗口。
          18.driver.switch_to.window(窗口句柄): 切換到新窗口。
          19.driver.switch_to_alert(): 警告框處理。處理 JavaScript 所生成的 alert,confirm,prompt.

          20.driver.switch_to.alert(): 警告框處理。
          21.driver.execute_script(js): 調用 js。

          22.driver.get_cookies(): 獲取當前會話所有 cookie 信息。

          23.driver.get_cookie(cookie_name):返回字典的 key 為“cookie_name”的 cookie 信息。

          實例:driver.get_cookie(“NET_SessionId”)

          24.driver.add_cookie(cookie_dict): 添加 cookie。“cookie_dict”指字典對象,必須有 name 和 value 值。
          25.driver.delete_cookie(name,optionsString): 刪除 cookie 信息。

          26.driver.delete_all_cookies(): 刪除所有 cookie 信息。

          頁面元素屬性:

          WebElement attributes:

          [‘class’, ‘delattr’, ‘dict’, ‘doc’, ‘eq’, ‘format’, ‘getattribute’, ‘hash’, ‘init’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘_execute’, ‘_id’, ‘_parent’, ‘_upload’, ‘_w3c’, ‘clear’, ‘click’, ‘find_element’, ‘find_element_by_class_name’, ‘find_element_by_css_selector’, ‘find_element_by_id’, ‘find_element_by_link_text’, ‘find_element_by_name’, ‘find_element_by_partial_link_text’, ‘find_element_by_tag_name’, ‘find_element_by_xpath’, ‘find_elements’, ‘find_elements_by_class_name’, ‘find_elements_by_css_selector’, ‘find_elements_by_id’, ‘find_elements_by_link_text’, ‘find_elements_by_name’, ‘find_elements_by_partial_link_text’, ‘find_elements_by_tag_name’, ‘find_elements_by_xpath’, ‘get_attribute’, ‘id’, ‘is_displayed’, ‘is_enabled’, ‘is_selected’, ‘location’, ‘location_once_scrolled_into_view’, ‘parent’, ‘rect’, ‘screenshot’, ‘screenshot_as_base64’, ‘screenshot_as_png’, ‘send_keys’, ‘size’, ‘submit’, ‘tag_name’, ‘text’, ‘value_of_css_property’]

          調用說明:
          driver.find_element*. 屬性值

          element=driver.find_element*
          element. 屬性值

          變量說明:
          1.element.size: 獲取元素的尺寸。
          2.element.text:獲取元素的文本。
          3.element.tag_name: 獲取標簽名稱。

          函數說明:
          1.element.clear(): 清除文本。
          2.element.send_keys(value): 輸入文字或鍵盤按鍵(需導入 Keys 模塊)。
          3.element.click():單擊元素。
          4.element.get_attribute(name): 獲得屬性值
          5.element.is_displayed(): 返回元素結果是否可見(True 或 False)
          6.element.is_selected(): 返回元素結果是否被選中(True 或 False)
          7.element.find_element*(): 定位元素,用于二次定位。

          網頁自動化最基本的要求就是要定位到各個元素,然后才能對該元素進行各種操作(輸入,點擊,清除,提交等)。

          以百度搜索輸入框為例,具體說明各個定位方式的用法:

          (通過 chrome 瀏覽器查看元素或者搜狐瀏覽器的 firebug 查看,即可看到 html 源碼)

          注意點:第三行的元素是灰色的,該元素是不可定位到的,下方會說明。

          1
          2
          3
          4
          5
          6
          7
          8

          1. 通過 id 定位元素

          如果 id 不是動態的,一個頁面的 id 是唯一的。最簡單的定位方式。

          使用:find_element_by_id(“id_vaule”)

          實例:find_element_by_id(“kw”)

          注意點:有些 id 值是動態變化的,則不能使用該方法定位。如下:id 就是動態的,每次進入頁面,該 id 都會改變

          郵箱帳號或手機號

          2. 通過 class_name 定位元素

          classname 有可能重復哦。

          使用:find_element_by_class_name(“class_name_vaule”)

          實例:find_element_by_class_name(“s_ipt”)

          3. 通過 tag_name 定位元素

          標簽名字最容易重復,不過,當定位一組數據時,可使用。

          使用:find_element_by_tag_name(“tag_name_vaule”)

          實例:find_element_by_tag_name(“input”)

          注意點:當定位一組元素時:可勾選一組復選框。如下:

          find_element_by_tag_name(“input”)

          checkbox
          checkbox1
          checkbox2
          checkbox3

          4. 通過 name 定位元素

          name 有可能會重復哦。

          使用:find_element_by_name(“name_vaule”)

          實例:find_element_by_name(“wd”)

          5. 通過 link 文字精確定位元素

          登錄

          使用:find_element_by_link_text(“text_vaule”)

          實例:find_element_by_link_text(“登錄”)

          6. 通過 link 文字模糊定位元素

          使用:find_element_by_partial_link_text(“部分 text_vaule”)

          實例:find_element_by_partial_link_text(“登”)

          7. 通過 CSS 定位元素

          CSS(Cascading Style Sheets)是一種語言,它用來描述 HTML 和 XML 文檔的表現。CSS 可以較為靈活的選擇控件的任意屬性,一般情況下會比 XPath 快。且語法也比較簡潔。


          使用:find_element_by_css_selector(“CSS”)

          實例:

          7.1 通過 id 屬性定位元素

          #號表示通過 id 屬性來定位元素

          find_element_by_css_selector(“#kw”)

          7.2 通過 class 屬性定位元素

          . 號表示通過 class 屬性來定位元素

          find_element_by_css_selector(“.s_ipt”)

          7.3 通過標簽名定位元素

          find_element_by_css_selector(“input”)

          7.4 通過屬性定位元素(挺常用的)

          find_element_by_css_selector(“[name=‘wd’]”)

          find_element_by_css_selector(“[maxlength=‘255’]”)

          屬性值包含某個值

          屬性值包含 wd:適用于由空格分隔的屬性值。

          find_element_by_css_selector(“[name~=‘wd’]”)

          7.5 父子定位元素

          查找有父親元素的標簽名為 span,它的所有標簽名叫 input 的子元素

          **find_element_by_css_selector(“span>input”) **

          7.6 組合定位元素

          標簽名 #id 屬性值:指的是該 input 標簽下 id 屬性為 kw 的元素

          find_element_by_css_selector(“input#kw”)

          標簽名.class 屬性值:指的是該 input 標簽下 class 屬性為 s_ipt 的元素

          find_element_by_css_selector(“input.s_ipt”)

          標簽名 [屬性 =’屬性值‘]:指的是該 input 標簽下 name 屬性為 wd 的元素

          find_element_by_css_selector(“input[name=‘wd’]”)

          父元素標簽名 > 標簽名.class 屬性值:指的是 span 下的 input 標簽下 class 屬性為 s_ipt 的元素

          find_element_by_css_selector(“span>input.s_ipt”)

          多個屬性組合定位元素(挺常用的)

          指的是 input 標簽下 id 屬性為 kw 且 name 屬性為 wd 的元素

          find_element_by_css_selector(“input.s_ipt[name=‘wd’]”)

          指的是 input 標簽下 name 屬性為 wd 且 maxlength 為 255 的元素

          find_element_by_css_selector(“input[name=‘wd’][maxlength='255']”)

          8. 通過 XPath 定位元素

          XPath 是一種 XML 文檔中定位元素的語言。該定位方式也是比較常用的定位方式。

          使用:find_element_by_xpath(“XPath”)

          8.1 通過屬性定位元素

          find_element_by_xpath(“// 標簽名 [@屬性 =‘屬性值’]”)

          id 屬性:find_element_by_xpath(“//input[@id=‘kw’]”)
          ?
          class 屬性:find_element_by_xpath(“//input[@class=‘s_ipt’]”)

          name 屬性:find_element_by_xpath(“//input[@name=‘wd’]”)

          maxlength 屬性:find_element_by_xpath(“//input[@maxlength=‘255’]”)

          8.2 通過標簽名定位元素

          指所有 input 標簽元素 find_element_by_xpath(“//input”)

          8.3 父子定位元素

          查找有父親元素的標簽名為 span,它的所有標簽名叫 input 的子元素

          find_element_by_xpath(“//span/input”)

          8.4 根據元素內容定位元素(非常實用)

          find_element_by_xpath(“//p[contains(text(),‘京公網’)]”)

          京公網安備 11000002000001 號

          注:contains 的另一種用法

          //**input[contains(@class,‘s’)] **說明 class 屬性包含 s 的元素

          8.5 組合定位元素

          // 父元素標簽名 / 標簽名的屬性值:指的是 span 下的 input 標簽下 class 屬性為 s_ipt 的元素

          find_element_by_xpath(“//span/input[@class=‘s_ipt’]”)

          多個屬性組合定位(挺常用的)

          指的是 input 標簽下 id 屬性為 kw 且 name 屬性為 wd 的元素

          find_element_by_xpath(“//input[@class=‘s_ipt’ and @name=‘wd’]”)

          指的是 p 標簽下內容包含“京公網”且 id 屬性為 jgwab 的元素

          find_element_by_xpath(“//p[contains(text(),‘京公網’)and @id=‘jgwab’]”)

          比較懶惰的方法:

          使用搜狐瀏覽器的 firebug 工具,復制 XPath 路徑,不過這種方式對層級要求高,到時候自己再修改下。

          9. 通過 By 定位元素

          使用:find_element(定位的類型,具體定位方式)

          定位的類型包括 By.ID,By.NAME,By.CLASS_NAME,By.TAG_NAME,By.LINK_TEXT,By.PARTIAL_LINK_TEXT,By.XPATH,By.CSS_SELECTOR

          具體定位方式參考上方 1-8 的說明。

          實例:find_element(By.ID,‘kw’)

          注意:使用 By 定位方式,需先導入 By 類

          from selenium.webdriver.common.by import By

          10. 具體實例說明

          下方例子是登陸 126 郵件,然后發送郵件。

          1 # coding=utf-8
          2 '''
          3 Created on 2016-7-27
          4 @author: Jennifer
          5 Project: 發送郵件
          6 '''
          7 from selenium import webdriver
          8 import time
          9
          10 from test_5_2_public import Login # 由于公共模塊文件命名為 test_5_2_public
          11 driver=webdriver.Firefox()
          12 driver.implicitly_wait(30)
          13 driver.get(r’http://www.126.com/‘) # 字符串加 r,防止轉義。
          14 time.sleep(3)
          15 driver.switch_to.frame(‘x-URS-iframe’)
          16 #調用登錄模塊
          17 Login().user_login(driver)
          18 time.sleep(10)
          19 #發送郵件
          20 #點擊發件箱
          21 #_mail_component_61_61 是動態 id,所以不能用于定位
          22 #driver.find_element_by_css_selector(’#_mail_component_61_61>span.oz0’).click()
          23 #不能加 u"//span[contains(text(),u’寫 信’)]“,否則定位不到。
          24 #以下定位是查找 span 標簽有個文本(text)包含(contains)‘寫 信’ 的元素,該定位方法重要
          25 **driver.find_element_by_xpath(”//span[contains(text(),‘寫 信’)]“).click()**
          26 #填寫收件人
          27 #driver.find_element_by_class_name(‘nui-editableAddr-ipt’).send_keys(r’xxx@doov.com.cn’)
          28 driver.find_element_by_class_name(‘nui-editableAddr-ipt’).send_keys(r’xxx@163.com’)
          29 #填寫主題
          30 #通過 and 連接更多的屬性來唯一地標志一個元素
          31 **driver.find_element_by_xpath(”//input[@class=‘nui-ipt-input’ and @maxlength=‘256’]“).send_keys(u’自動化測試’)**
          32 #填寫正文
          33 #通過 switch_to_frame() 將當前定位切換到 frame/iframe 表單的內嵌頁面中
          34 driver.switch_to_frame(driver.find_element_by_class_name(‘APP-editor-iframe’))
          35 #在內嵌頁面中定位郵件內容位置
          36 emailcontext=driver.find_element_by_class_name(‘nui-scroll’)
          37 #填寫郵件內容
          38** emailcontext.send_keys(u’這是第一封自動化測試郵件’)**
          39 #通過 switch_to().default_content() 跳回最外層的頁面
          40 #注:不要寫成 switch_to().default_content(), 否則報 AttributeError: SwitchTo instance has no call method
          41 driver.switch_to.default_content()
          42 #driver.switch_to.parent_frame()
          43 #點擊發送
          44 time.sleep(3)
          45 #有可能存在元素不可見(查看元素是灰色的),會報 ElementNotVisibleException 錯誤
          46 #包含發送二字的元素很多,所以還得再加上其他限制
          47 #sendemails=driver.find_element_by_xpath(”//span[contains(text(),‘發送’)]“)
          48 **sendemails=driver.find_element_by_xpath(”//span[contains(text(),‘發送’)and @class=‘nui-btn-text’]")**
          49 time.sleep(3)
          50
          51 #校驗郵件是否發送成功
          52 try:
          53 assert ‘發送成功’ in driver.page_source
          54 except AssertionError:
          55 print ‘郵件發送成功’
          56 else:
          57 print ‘郵件發送失敗’
          58
          59 #調用退出模塊
          60 Login().user_logout(driver)

          元素定位說明:
          1. 代碼 22 行,定位不到是因為 id 是動態的,所以需采取其他方式定位元素。

          2. 代碼 25 行,是根據元素內容來定位的,具體用法詳看 8.4.

          3. 代碼 28 行,是根據 class 名來定位元素的,由于該值在該頁面上是唯一的,所以可以用它來定位。

          4. 代碼 31 行,是使用邏輯運算符 and 連接更多的屬性從而唯一的標志一個元素,具體用法詳看 8.5.

          5. 代碼 34 行,由于使用內嵌的 iframe 框架,所以需要先使用 switch_to_frame() 移到該表單上,才能定位該表單上的元素,非常重要,否則無論怎么定位都會報“NoSuchElementException”,找不到該元素。

          6. 代碼 41 行,跳出 iframe 框架,當框架內的動作操作完畢后,需要使用 switch_to.default_content 跳出 iframe 框架, 非常重要。

          7. 代碼 47 行,由于內容包括“發送”的元素中包含不可見元素(html 查看元素可以看到此行是灰色的),這樣有可能定位到不可見元素,會報“ElementNotVisibleException”。

          8. 代碼 48 行,是使用邏輯運算符 and 連接更多的屬性從而唯一的標志一個元素,具體用法詳看 8.5. 這樣可以排除掉那個不可見元素。

          1.Frame/Iframe 原因定位不到元素:
            這個是最常見的原因,首先要理解下 frame 的實質,frame 中實際上是嵌入了另一個頁面,而 webdriver 每次只能在一個頁面識別,因此需要先定位到相應的 frame,對那個頁面里的元素進行定位。
          ?
          解決方案:
          ?
          如果 iframe 有 name 或 id 的話,直接使用 switch_to_frame(“name 值”) 或 switch_to_frame(“id 值”)
          ?
          ?如下:
          driver=webdriver.Firefox()
          driver.get(r’http://www.126.com/’)
          driver.switch_to_frame(‘x-URS-iframe’) # 需先跳轉到 iframe 框架
          username=driver.find_element_by_name(‘email’)
          username.clear()

          如果 iframe 沒有 name 或 id 的話,則可以通過下面的方式定位:
          #先定位到 iframe
          elementi= driver.find_element_by_class_name(‘APP-editor-iframe’)
          #再將定位對象傳給 switch_to_frame() 方法
          driver.switch_to_frame(elementi)

          如果完成操作后,
          ?可以通過 switch_to.parent_content()方法跳出當前 iframe,或者還可以通過 switch_to.default_content() 方法跳回最外層的頁面。
          ?


          2.Xpath 描述錯誤原因:
          ?
          由于 Xpath 層級太復雜,容易犯錯。但是該定位方式能夠有效定位絕大部分的元素,建議掌握。
          解決方案:
          ?
          2.1 可以使用 Firefox 的 firePath,復制 xpath 路徑。該方式容易因為層級改變而需要重新編寫過 xpath 路徑,不建議使用,初學者可以先復制路徑,然后嘗試去修改它。
          ?
          2.2 提高下寫 xpath 的水平。


          如何檢驗編寫的 Xpath 是否正確?編寫好 Xpath 路徑,可以直接復制到搜狐瀏覽器的 firebug 查看 html 源碼,通過 Xpath 搜索:如下紅色框,若無報錯,則說明編寫的 Xpath 路徑沒錯。
          ?
          **find_element_by_xpath(“//input[@id=‘kw’]”) **

          ?

          ?

          ?3. 頁面還沒有加載出來,就對頁面上的元素進行的操作:
          這種情況一般說來,可以設置等待,等待頁面顯示之后再操作,這與人手工操作的原理一樣:
          3.1 設置等待時間;缺點是需要設置較長的等待時間,案例多了測試就很慢;
          3.2 設置等待頁面的某個元素出現,比如一個文本、一個輸入框都可以,一旦指定的元素出現,就可以做操作。
          3.3 在調試的過程中可以把頁面的 html 代碼打印出來,以便分析。
          ?
          解決方案:
          導入時間模塊。
          import time
          time.sleep(3)

          4. 動態 id 定位不到元素:
          解決方案:
          如果發現是動態 id,直接用 xpath 定位或其他方式定位。

          ?

          ??

          5. 二次定位,如彈出框登錄
          ?
          如百度登錄彈出框登錄百度賬號,需先定位到百度彈出框,然后再定位到用戶名密碼登錄。

          #coding=utf-8
          '''
          Created on 2016-7-20
          @author: Jennifer
          Project: 登錄百度賬號
          '''
          from selenium import webdriver
          import time

          driver = webdriver.Firefox()
          driver.get(“http://www.baidu.com/”)

          time.sleep(3)

          #點擊登錄:有些 name 為 tj_login 的元素為不可見的,點擊可見的那個登錄按鈕即可。
          #否則會報:ElementNotVisibleException
          element0=driver.find_elements_by_name(“tj_login”)
          for ele0 in element0:
          if ele0.is_displayed():
          ele0.click()

          #在登錄彈出框,需先定位到登錄彈出框
          #否則會報:NoSuchElementException
          element1=driver.find_element_by_class_name(“tang-content”)
          element11=element1.find_element_by_id(“TANGRAM__PSP_8__userName”)
          element11.clear()
          element11.send_keys(“登錄名”)

          element2=element1.find_element_by_id(“TANGRAM__PSP_8__password”)
          element2.clear()
          element2.send_keys(“密碼”)

          element3=element1.find_element_by_id(“TANGRAM__PSP_8__submit”)
          element3.click()
          element3.submit()

          try:
          assert “登錄名” in driver.page_source
          except AssertionError:
          print “登錄失敗”
          else:
          print “登錄成功”
          time.sleep(3)
          finally:
          print “測試記錄:已測試”
          driver.close()

          6. 不可見元素定位

          如上百度登錄代碼,通過名稱為 tj_login 查找的登錄元素,有些是不可見的,所以加一個循環判斷,找到可見元素(is_displayed())點擊登錄即可。

          然我們生活在一個寬帶無處不在、4/5G 幾乎全覆蓋的時代,但網站加載緩慢還是常態,就算我們打開一個以文本為中心的新聞網站,都可能需要至少 30 秒才能開始閱讀。畢竟在內容膨脹時代,一張照片就能輕易超過 1MB 大小,許多網站為了顯示幾段文本,還會單獨加載至少 10MB 的 JS 和自定義字體。

          對此,對優化和極簡主義充滿熱情的資深 Web 開發 Nathaniel 告訴我們,你應該讓你的網頁盡力控制在 14KB 以內,而且即使對于以富媒體為中心的網站,這條 14KB 的規則可能仍然值得遵循。如果 14KB 不足以用于最終布局,則需要優先考慮“首屏”字節,可以用發送給訪問者的前 14KB 數據來渲染一些有用的東西,減少用戶還沒有開始閱讀就流失掉的機會。


          網頁越小,加載速度就越快——這一點都不奇怪。


          但令人感到驚訝的是,14KB 網頁的加載速度比 15KB 要快得多——可能快 612 毫秒——而 15KB 和 16KB 網頁之間的加載速度差異微乎其微。


          這是 TCP 慢啟動算法導致的。本文將介紹這個算法、它的原理以及為什么你應該關注它。但首先我們需要快速過一遍一些基礎知識。


          TCP 是什么


          傳輸控制協議(Transmission Control Protocol,TCP)是一種使用 IP 協議可靠地發送數據包的方法——有時被稱為 TCP/IP。


          當瀏覽器向你的網站(或圖像或樣式表)發出請求時,它會使用 HTTP 請求。HTTP 建立在 TCP 之上,一個 HTTP 請求通常由許多 TCP 數據包組成。IP 只是一個將數據包從互聯網上的一個位置發送到另一個位置的系統。IP 沒有檢查數據包是否成功到達目的地的方法。


          對于網站來說,確保所有的數據到達請求端是非常關鍵的,否則我們可能會因為丟失數據包無法獲得完整的網頁。但在網絡的其他應用場景中,這一點并不那么重要——比如流媒體直播視頻。


          TCP 是 IP 的擴展,瀏覽器和網站服務器通過它告訴對方哪些數據包已經成功到達。


          服務器發送一些數據包,然后等待瀏覽器已經收到數據包的響應(這叫確認或 ACK),然后它繼續發送更多的數據包——或者如果它沒有收到 ACK,將再次發送相同的數據包。


          什么是 TCP 慢啟動


          TCP 慢啟動是一種算法,服務器用它來確定一次可以發送多少數據包。


          當瀏覽器第一次連接到服務器時,服務器無法知道它們之間的帶寬是多少。帶寬是指在單位時間內網絡可以傳輸的數據量。通常以比特/秒(b/s)為單位。我們可以用管道來作類比——把帶寬想象成每秒從管道流出多少水。


          服務器不知道網絡連接可以處理多少數據——所以它先發送少量且安全的數據——通常是 10 個 TCP 數據包。如果這些數據包成功地到達網站訪問者,他們的計算機返回確認(ACK),表示數據包已經被收到了。然后,服務器發送更多的數據包,但這一次它將數據包的數量增加了一倍。


          這個過程會不斷重復,直到數據包丟失,服務器沒有收到 ACK。(此時,服務器會繼續發送數據包,但速度較慢)。


          這就是 TCP 慢啟動的要點——在現實當中,雖然算法各不相同,但這是它的基本原理。


          那么 14KB 這個數字是怎么來的


          大多數 Web 服務器的 TCP 慢啟動算法都是從發送 10 個 TCP 數據包開始的。


          TCP 數據包最大長度為 1500 字節。這個最大值不是由 TCP 規范設置的,它來自于以太網標準。


          每個 TCP 數據包的標頭占了 40 個字節,其中 16 個字節用于 IP,另外 24 個字節用于 TCP。


          這樣每個 TCP 數據包還剩下 1460 個字節。10 x 1460 = 14600 字節,或大約 14KB!


          因此,如果你能把網站的網頁——或網頁的關鍵部分——壓縮到 14KB,就可以為訪問者節省大量的時間——他們和網站服務器之間的往返時間。


          一個數據往返能有多糟糕?但人們非常沒有耐心——一個數據往返可能會出奇地長,具體多長取決于延遲……延遲是指數據包從源傳輸到目的地所花費的時間。如果帶寬是每秒鐘可以通過管道的水的數量,那么延遲就是一滴水進入管道后從另一端流出所花費的時間。


          下面是一個關于延遲有多糟糕的例子。


          衛星網絡


          衛星網絡是由環繞地球軌道的衛星提供的,在人煙稀少的地區、石油鉆井平臺、游輪以及飛機上,人們可以使用這種網絡。


          為了說明這種糟糕的延遲,我們想象一群在石油鉆井平臺工作的兄弟把骰子忘在了家里,他們需要通過 missingdice.com(少于 14KB)來玩《龍與地下城》游戲。


          首先,他們中的一個用手機發出一個網頁請求……


          手機將請求發送到鉆井平臺的 WiFi 路由器,路由器將數據發送給平臺上的衛星天線,我們假設這可能需要 1 毫秒時間。


          然后,衛星天線將數據發送到地球軌道上方的衛星。


          通常,這是通過在地球表面上方 35786 公里處運行的軌道衛星實現的。光速為 299792458 米/秒,所以信息從地球發送到衛星需要 120 毫秒。然后,衛星將信息傳回地面接收站,這又需要 120 毫秒。


          然后,地面站必須將請求發送到位于地球任意位置的服務器(當光通過光纖電纜傳輸時,速度會降至每秒 200000000 米)。如果地面站和服務器之間的距離等于紐約到倫敦之間的距離,那么大約需要 28 毫秒,如果地面站和服務器之間的距離等于紐約到悉尼之間的距離,則需要 80 毫秒——所以我們姑且定一個 60 毫秒的數字(這個數字便于計算)。


          然后,服務器需要處理請求,這可能需要 10 毫秒,然后服務器再次將它發送出去。


          回到地面站,進入太空,回到衛星天線,然后回到無線路由器,再到手機上。


          手機 -> WiFi 路由器 ->衛星天線 ->衛星 -> 地面站 -> 服務器 -> 地面站 -> 衛星 -> 衛星天線 -> WiFi 路由器 -> 手機


          如果我們算一下,就是 10 + ( 1 + 120 + 120 + 60 ) x 2 = 612 毫秒。


          這是每次往返額外的 612 毫秒——也許這看起來不是很長時間,但你的網站可能只是為了獲取第一個資源就需要許多個往返。


          另外,HTTPS 在完成第一個往返之前需要額外的兩次往返——這使延遲達到了 1836 毫秒!


          對于生活在陸地上的人,延遲又是怎樣的


          衛星網絡似乎是一個極端的例子——我選擇它作為例子是因為它能夠充分說明了網絡延遲這個問題——但對于生活在陸地上的人來說,延遲可能比這更糟糕,原因有很多。


          • 2G 網絡的延遲通常在 300 毫秒到 1000 毫秒之間;
          • 3G 網絡的延遲可以在 100 毫秒到 500 毫秒之間;
          • 嘈雜的移動網絡——比如在一個異常擁擠的地方,比如音樂節;
          • 處理大流量的服務器;
          • 其他一些不好的東西。


          不穩定的網絡連接也會導致數據包丟失——導致需要另一個往返來獲取丟失的數據包。


          了解了 14KB 法則,接下來可以做些什么


          當然,你應該讓你的網頁盡可能的小——你愛你的訪客,你希望他們開心。將每個頁面的大小控制在 14KB 以內是一個不錯的主意。


          這 14KB 可以是壓縮數據——所以實際上可以對應大約 50KB 的未壓縮數據——這已經非常慷慨了。要知道,阿波羅 11 的制導計算機只有 72KB 內存。


          去掉自動播放的視頻、彈出窗口、Cookie、Cookie 橫幅、社交網絡按鈕、跟蹤腳本、JavaScript 和 CSS 框架,以及所有其他人們不喜歡的垃圾——你可能就能實現 14KB 法則。


          假設你已經盡力將所有內容控制在 14KB 以內,但仍然做不到——但 14KB 法則仍然很有用。


          你可以用發送給訪問者的前 14KB 數據來渲染一些有用的東西——例如一些關鍵的 CSS、JS 和解釋如何使用你的應用程序的前幾段文本。


          需要注意的是,14KB 法則包含了 HTTP 標頭——這些是未壓縮的(即使是 HTTP/2 的第一個響應),也包含圖片,所以你應該只加載在頁面上方的內容,并保持它們最小,或者使用占位符,讓訪問者知道他們在等待一些更好的內容。


          關于這個法則的一些注意事項


          14KB 法則更像是一種經驗之談,而不是計算的基本法則。


          • 一些服務器已經將 TCP 慢啟動初始窗口從 10 個數據包增加到 30 個;
          • 有時服務器知道它可以從更大數量的數據包開始傳輸,因為它使用 TLS 握手來建立一個更大的窗口;
          • 服務器可以緩存路由可管理的數據包數量,并在下一次連接時發送更多的數據包;
          • 還有其他需要注意的地方——這里有一篇文章更深入地探討關于為什么 14KB 法則并不總是這么回事(https://www.tunetheweb.com/blog/critical-resources-and-the-first-14kb/)。


          HTTP/2 和 14KB 法則


          有一種觀點認為,在使用 HTTP/2 時,14KB 法則不再適用。我已經讀了所有我能讀到的關于這個問題的東西,但我還沒有看到任何證據表明使用 HTTP/2 的服務器已經停止使用 TCP 慢啟動(從 10 個數據包開始)。


          HTTP/3 和 QUIC


          與 HTTP/2 類似,有一種觀點認為 HTTP/3 和 QUIC 將廢除 14KB 法則——事實并非如此。實際上,QUIC 仍然建議使用 14KB 法則。


          原文鏈接:

          https://endtimes.dev/why-your-website-should-be-under-14kb-in-size/


          主站蜘蛛池模板: 国产一区二区三区美女| 中文字幕精品一区二区2021年| 99精品国产高清一区二区| 亚洲一区精彩视频| 中文字幕无码免费久久9一区9| 玩弄放荡人妻一区二区三区| 久久青草国产精品一区| 波霸影院一区二区| 日本福利一区二区| 国产精品一区二区毛卡片| 国产成人AV一区二区三区无码| 一区二区三区在线看| 国产一区二区三区在线免费 | 亚洲色偷精品一区二区三区| 亚洲一区二区三区久久| 无码精品国产一区二区三区免费 | 精品无人区一区二区三区在线 | 日韩视频在线一区| 亚洲欧美日韩一区二区三区| 久久精品午夜一区二区福利| 亚洲一区二区电影| 亚洲福利视频一区| 精品无码人妻一区二区免费蜜桃| 国产精品区一区二区三在线播放| 中文字幕一区日韩在线视频 | 国产短视频精品一区二区三区| 另类一区二区三区| 熟女精品视频一区二区三区| 久久免费国产精品一区二区| 亚洲AV日韩AV天堂一区二区三区| 亚洲综合色自拍一区| 久久精品一区二区三区资源网 | 亚洲一区二区三区免费在线观看| 久久久无码一区二区三区| 国产精品香蕉在线一区| 亚洲AV日韩综合一区| 国产精品综合一区二区| 亚洲国产精品成人一区| 国产av天堂一区二区三区| 亚洲第一区香蕉_国产a| 亚洲熟妇AV一区二区三区宅男|